강의

멘토링

커뮤니티

인프런 커뮤니티 질문&답변

castinglife님의 프로필 이미지
castinglife

작성한 질문수

[리뉴얼] React로 NodeBird SNS 만들기

user/[id].js => Unhandled Runtime Error TypeError: Cannot read property 'id' of undefined 에러가 발생합니다.

작성

·

314

0

페이지 로딩은 정상적으로 되는데 스크롤시 에러가 발생합니다.

<Head> 태그 url 부분을 약간 다르게 수정해 주었고 다른 부분은 이상이 없어 보입니다.

로그를 찍어 봤는데..   값이 나오는데 값을 찾을수 없다...???? 

user/[id].js

const User = () => {

    const dispatch = useDispatch();

    const router = useRouter();

    const { id } = router.query;    <====  이부분에서 id 값은 잘 받아 오는것 같습니다.     

    console.log(router.query);    <====  로그를 찍어보니     {id: "1"}    로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과 값도 동일하게 string 인데.. 정상적으로 스크롤 됩니다. 

    const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);

    const { userInfo } = useSelector((state) => state.user);

FRONT
user/[id]/.js

// user/[id].js

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Avatar, Card } from 'antd';
import { END } from 'redux-saga';
import Head from 'next/head';
import { useRouter } from 'next/router';

import axios from 'axios';
import { LOAD_USER_POSTS_REQUEST } from '../../reducers/post';
import { LOAD_MY_INFO_REQUEST, LOAD_USER_REQUEST } from '../../reducers/user';
import PostCard from '../../components/PostCard';
import wrapper from '../../store/configureStore';
import AppLayout from '../../components/AppLayout';

import { backUrl } from '../../config/config';

// 프론트서버와 브라우저 모두에서 실행
const User = () => {
    const dispatch = useDispatch();
    const router = useRouter();
    const { id } = router.query;    <====  이부분에서 id 값은 잘 받아 오는것 같습니다.     
    console.log(router.query);    <====  로그를 찍어보니     {id: "1"}    로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과도 동일한데.. 정상적으로 스크롤 됩니다.
    const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);
    const { userInfo } = useSelector((state) => state.user);

    useEffect(() => {
        function onScroll() {
          if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
            if (hasMorePosts && !loadPostsLoading) {
              dispatch({
                type: LOAD_USER_POSTS_REQUEST,
                lastId: mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.lenth - 1].id,      <====== 에러가 발생하는 부분
                data: id,
              });
            }
          }
        }
        window.addEventListener('scroll', onScroll);
        return () => {
          window.removeEventListener('scroll', onScroll);
        };
      }, [ mainPosts.length, hasMorePosts, id]);

    return (
        <AppLayout>
            <Head>
                <title>
                    {userInfo.nickname}
                    님의 글
                </title>
                <meta name="description" content={`${userInfo.nickname}님의 게시글`} />
                <meta property="og:title" content={`${userInfo.nickname}님의 게시글`} />
                <meta property="og:description" content={`${userInfo.nickname}님의 게시글`} />
                <meta property="og:image" content={`${backUrl}/favicon.ico`} />
                <meta property="og:url" content={`${backUrl}/user/${id}`} />
            </Head>
            {userInfo
                ? (
                <Card
                    actions={[
                    <div key="twit">
                        짹짹
                        <br />
                        {userInfo.Posts}
                    </div>,
                    <div key="following">
                        팔로잉
                        <br />
                        {userInfo.Followings}
                    </div>,
                    <div key="follower">
                        팔로워
                        <br />
                        {userInfo.Followers}
                    </div>,
                    ]}
                >
                    <Card.Meta
                    avatar={<Avatar>{userInfo.nickname[0]}</Avatar>}
                    title={userInfo.nickname}
                    />
                </Card>
                )
                : null}
            {mainPosts.map((c) => (
                <PostCard key={c.id} post={c} />
            ))}
        </AppLayout>
    );
};

// front server 에서 실행
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
    const cookie = context.req ? context.req.headers.cookie : '';
    axios.defaults.headers.Cookie = '';
    if (context.req && cookie) {
      axios.defaults.headers.Cookie = cookie;
    }
    context.store.dispatch({
      type: LOAD_USER_POSTS_REQUEST,
      data: context.params.id,
    });
    context.store.dispatch({
      type: LOAD_MY_INFO_REQUEST,
    });
    context.store.dispatch({
      type: LOAD_USER_REQUEST,
      data: context.params.id,
    });
    context.store.dispatch(END);
    await context.store.sagaTask.toPromise();
    // return { props: {} };
  });

export default User;



config/config.js

const domainUrl = 'https://api.nodebird.com';
const localhostUrl = 'http://localhost:3065';

const backUrl = process.env.NODE_ENV === 'production' ? 'https://api.nodebird.com' : 'http://localhost:3065';


export { backUrl, domainUrl, localhostUrl };




BACK
sagas/user.js




// all   fork   call    put  delay   debounce   throttle   takeLatest   tabkeEvery   takeLeding    taekMaybe
import { all, fork, put, takeLatest, throttle, call } from 'redux-saga/effects';
import axios from 'axios';
// import shortId from 'shortid';

import { 
    LOAD_POSTS_REQUEST,
    LOAD_POSTS_SUCCESS,
    LOAD_POSTS_FAILURE,

    LOAD_USER_POSTS_REQUEST,
    LOAD_USER_POSTS_SUCCESS,
    LOAD_USER_POSTS_FAILURE,

    LOAD_HASHTAG_POSTS_REQUEST,
    LOAD_HASHTAG_POSTS_SUCCESS,
    LOAD_HASHTAG_POSTS_FAILURE,

    LOAD_POST_REQUEST,
    LOAD_POST_SUCCESS,
    LOAD_POST_FAILURE,

    LIKE_POST_REQUEST,
    LIKE_POST_SUCCESS,
    LIKE_POST_FAILURE,

    UNLIKE_POST_REQUEST,
    UNLIKE_POST_SUCCESS,
    UNLIKE_POST_FAILURE,

    ADD_POST_REQUEST, 
    ADD_POST_SUCCESS, 
    ADD_POST_FAILURE,

    ADD_COMMENT_REQUEST, 
    ADD_COMMENT_SUCCESS, 
    ADD_COMMENT_FAILURE,
    
    REMOVE_POST_REQUEST,
    REMOVE_POST_SUCCESS,
    REMOVE_POST_FAILURE,

    UPLOAD_IMAGES_REQUEST,
    UPLOAD_IMAGES_SUCCESS,
    UPLOAD_IMAGES_FAILURE,

    RETWEET_REQUEST,
    RETWEET_SUCCESS,
    RETWEET_FAILURE,
    
} from '../reducers/post';

import { 
    ADD_POST_TO_ME, 
    REMOVE_POST_OF_ME,
} from '../reducers/user';


function likePostAPI(data) {
    return axios.patch(`/post/${data}/like`);
};

function* likePost(action) {
    try {

        const result = yield call(likePostAPI, action.data);

        yield put({
            type: LIKE_POST_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LIKE_POST_FAILURE,
            error: err.response.data,
        });
    }
}


function unlikePostAPI(data) {
    return axios.delete(`/post/${data}/like`);
};

function* unlikePost(action) {
    try {

        const result = yield call(unlikePostAPI, action.data);

        yield put({
            type: UNLIKE_POST_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: UNLIKE_POST_FAILURE,
            error: err.response.data,
        });
    }
}

function loadPostsAPI(lastId) {
    return axios.get(`/posts?lastId=${lastId || 0}`);
};

function* loadPosts(action) {
    try {

            const result = yield call(loadPostsAPI, action.lastId);

        yield put({
            type: LOAD_POSTS_SUCCESS,
            data: result.data,
            // data: generateDummyPost(10),
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LOAD_POSTS_FAILURE,
            error: err.response.data,
        });
    }
}




function loadPostAPI(data) {
    return axios.get(`/post/${data}`);
};

function* loadPost(action) {
    try {
            const result = yield call(loadPostAPI, action.data);

        yield put({
            type: LOAD_POST_SUCCESS,
            data: result.data,
            // data: generateDummyPost(10),
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LOAD_POST_FAILURE,
            error: err.response.data,
        });
    }
}


function addCommentAPI(data) {
    return axios.post(`/post/${data.postId}/comment`, data);    // POST /post/1/comment
};

function* addComment(action) {
    try {

        const result = yield call(addCommentAPI, action.data);
        // yield delay(1000);
        yield put({
            type: ADD_COMMENT_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: ADD_COMMENT_FAILURE,
            error: err.response.data,
        });
    };
};



function addPostAPI(data) {
    return axios.post('/post', data);

};

function* addPost(action) {
    try {

        const result = yield call(addPostAPI, action.data);

        yield put({
            type: ADD_POST_SUCCESS,
            data: result.data
        });
        yield put({
            type: ADD_POST_TO_ME,
            data: result.data.id,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: ADD_POST_FAILURE,
            error: err.response.data,
        });
    };
};


function removePostAPI(data) {
    return axios.delete(`/post/${data}`);
};

function* removePost(action) {
    try {
        const result = yield call(removePostAPI, action.data);

        yield put({
            type: REMOVE_POST_SUCCESS,
            data: result.data,
        });
        console.log('removePost');
        yield put({
            type: REMOVE_POST_OF_ME,
            data: action.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: REMOVE_POST_FAILURE,
            error: err.response.data,
        });
    };
};

function uploadImagesAPI(data) {
    return axios.post('/post/images', data);  // POST /post/images
};

function* uploadImages(action) {
    try {
        const result = yield call(uploadImagesAPI, action.data);

        yield put({
            type: UPLOAD_IMAGES_SUCCESS,
            data: result.data,
        });

    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: UPLOAD_IMAGES_FAILURE,
            error: err.response.data,
        });
    };
};

function retweetAPI(data) {
    return axios.post(`/post/${data}/retweet`);  // POST /post/images
};

function* retweet(action) {
    try {

        const result = yield call(retweetAPI, action.data);

        yield put({
            type: RETWEET_SUCCESS,
            data: result.data,
        });

    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: RETWEET_FAILURE,
            error: err.response.data,
        });
    };
};



function loadHashtagPostsAPI(data, lastId) {

    return axios.get(`/hashtag/${data}?lastId=${lastId || 0}`);
};

function* loadHashtagPosts(action) {
    try {

            const result = yield call(loadHashtagPostsAPI, action.data, action.lastId);

        yield put({
            type: LOAD_HASHTAG_POSTS_SUCCESS,
            data: result.data,

        });
    } catch (err) {
        console.error(err);
        yield put({   
            type: LOAD_HASHTAG_POSTS_FAILURE,
            error: err.response.data,
        });
    }
}

function loadUserPostsAPI(data, lastId) {

    return axios.get(`/user/${data}/posts?lastId=${lastId || 0}`);
};

function* loadUserPosts(action) {
    try {

            const result = yield call(loadUserPostsAPI, action.data, action.lastId);

        yield put({
            type: LOAD_USER_POSTS_SUCCESS,
            data: result.data,
            // data: generateDummyPost(10),
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LOAD_USER_POSTS_FAILURE,
            error: err.response.data,
        });
    }
}


function* watchLoadUserPosts() {

    yield throttle(5000, LOAD_USER_POSTS_REQUEST, loadUserPosts);
};

function* watchLoadHashtagPosts() {
    // 5초에 한번 게사글이 로드 된다.
    yield throttle(5000, LOAD_HASHTAG_POSTS_REQUEST, loadHashtagPosts); 
};




function* watchLoadPosts() {
    // 5초에 한번 게사글이 로드 된다.
    yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts);
};

function* watchLoadPost() {
    // 5초에 한번 게사글이 로드 된다.
    yield takeLatest(LOAD_POST_REQUEST, loadPost); 
};

function* watchRetweet() {
    // 5초에 한번 게사글이 로드 된다.
    yield takeLatest(RETWEET_REQUEST, retweet);
};

function* watchAddPost() {
    yield takeLatest(ADD_POST_REQUEST, addPost);
};

function* watchUploadImages() {
    yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages);
};

function* watchAddComment() {
    yield takeLatest(ADD_COMMENT_REQUEST, addComment);   
};

function* watchRemovePost() {
    yield takeLatest(REMOVE_POST_REQUEST, removePost);   
};

function* watchLikePost() {
    yield takeLatest(LIKE_POST_REQUEST, likePost);   
};

function* watchUnLikePost() {
    yield takeLatest(UNLIKE_POST_REQUEST, unlikePost);   
};

export default function* postSaga() {
    yield all([             
        fork(watchRetweet),
        fork(watchUploadImages),
        fork(watchLikePost),
        fork(watchUnLikePost),
        fork(watchAddPost),
        fork(watchLoadPosts),
        fork(watchLoadHashtagPosts),
        fork(watchLoadUserPosts),
        fork(watchLoadPost),
        fork(watchRemovePost),
        fork(watchAddComment),
    ]);
};




reducers/post.js







reducers/user.js




FRONT
user/[id]/.js

import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Avatar, Card } from 'antd';
import { END } from 'redux-saga';
import Head from 'next/head';
import { useRouter } from 'next/router';

import axios from 'axios';
import { LOAD_USER_POSTS_REQUEST } from '../../reducers/post';
import { LOAD_MY_INFO_REQUEST, LOAD_USER_REQUEST } from '../../reducers/user';
import PostCard from '../../components/PostCard';
import wrapper from '../../store/configureStore';
import AppLayout from '../../components/AppLayout';

import { backUrl } from '../../config/config';

// 프론트서버와 브라우저 모두에서 실행
const User = () => {
    const dispatch = useDispatch();
    const router = useRouter();
    const { id } = router.query;    <====  이부분에서 id 값은 잘 받아 오는것 같습니다.     
    console.log(router.query);    <====  로그를 찍어보니     {id: "1"}    로 나옵니다. github에 올리신 파일로 돌려서 로그를 찍어 본 결과도 동일한데.. 정상적으로 스크롤 됩니다.
    const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector((state) => state.post);
    const { userInfo } = useSelector((state) => state.user);

    useEffect(() => {
        function onScroll() {
          if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
            if (hasMorePosts && !loadPostsLoading) {
              dispatch({
                type: LOAD_USER_POSTS_REQUEST,
                lastId: mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.lenth - 1].id,      <====== 에러가 발생하는 부분
                data: id,
              });
            }
          }
        }
        window.addEventListener('scroll', onScroll);
        return () => {
          window.removeEventListener('scroll', onScroll);
        };
      }, [ mainPosts.length, hasMorePosts, id]);

    return (
        <AppLayout>
            <Head>
                <title>
                    {userInfo.nickname}
                    님의 글
                </title>
                <meta name="description" content={`${userInfo.nickname}님의 게시글`} />
                <meta property="og:title" content={`${userInfo.nickname}님의 게시글`} />
                <meta property="og:description" content={`${userInfo.nickname}님의 게시글`} />
                <meta property="og:image" content={`${backUrl}/favicon.ico`} />
                <meta property="og:url" content={`${backUrl}/user/${id}`} />
            </Head>
            {userInfo
                ? (
                <Card
                    actions={[
                    <div key="twit">
                        짹짹
                        <br />
                        {userInfo.Posts}
                    </div>,
                    <div key="following">
                        팔로잉
                        <br />
                        {userInfo.Followings}
                    </div>,
                    <div key="follower">
                        팔로워
                        <br />
                        {userInfo.Followers}
                    </div>,
                    ]}
                >
                    <Card.Meta
                    avatar={<Avatar>{userInfo.nickname[0]}</Avatar>}
                    title={userInfo.nickname}
                    />
                </Card>
                )
                : null}
            {mainPosts.map((c) => (
                <PostCard key={c.id} post={c} />
            ))}
        </AppLayout>
    );
};

// front server 에서 실행
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
    const cookie = context.req ? context.req.headers.cookie : '';
    axios.defaults.headers.Cookie = '';
    if (context.req && cookie) {
      axios.defaults.headers.Cookie = cookie;
    }
    context.store.dispatch({
      type: LOAD_USER_POSTS_REQUEST,
      data: context.params.id,
    });
    context.store.dispatch({
      type: LOAD_MY_INFO_REQUEST,
    });
    context.store.dispatch({
      type: LOAD_USER_REQUEST,
      data: context.params.id,
    });
    context.store.dispatch(END);
    await context.store.sagaTask.toPromise();
    // return { props: {} };
  });

export default User;



config/config.js

const domainUrl = 'https://api.nodebird.com';
const localhostUrl = 'http://localhost:3065';

const backUrl = process.env.NODE_ENV === 'production' ? 'https://api.nodebird.com' : 'http://localhost:3065';


export { backUrl, domainUrl, localhostUrl };




BACK
sagas/user.js

// all   fork   call    put  delay   debounce   throttle   takeLatest   tabkeEvery   takeLeding    taekMaybe
import { all, fork, put, takeLatest, throttle, call } from 'redux-saga/effects';
import axios from 'axios';
// import shortId from 'shortid';

import { 
    LOAD_POSTS_REQUEST,
    LOAD_POSTS_SUCCESS,
    LOAD_POSTS_FAILURE,

    LOAD_USER_POSTS_REQUEST,
    LOAD_USER_POSTS_SUCCESS,
    LOAD_USER_POSTS_FAILURE,

    LOAD_HASHTAG_POSTS_REQUEST,
    LOAD_HASHTAG_POSTS_SUCCESS,
    LOAD_HASHTAG_POSTS_FAILURE,

    LOAD_POST_REQUEST,
    LOAD_POST_SUCCESS,
    LOAD_POST_FAILURE,

    LIKE_POST_REQUEST,
    LIKE_POST_SUCCESS,
    LIKE_POST_FAILURE,

    UNLIKE_POST_REQUEST,
    UNLIKE_POST_SUCCESS,
    UNLIKE_POST_FAILURE,

    ADD_POST_REQUEST, 
    ADD_POST_SUCCESS, 
    ADD_POST_FAILURE,

    ADD_COMMENT_REQUEST, 
    ADD_COMMENT_SUCCESS, 
    ADD_COMMENT_FAILURE,
    
    REMOVE_POST_REQUEST,
    REMOVE_POST_SUCCESS,
    REMOVE_POST_FAILURE,

    UPLOAD_IMAGES_REQUEST,
    UPLOAD_IMAGES_SUCCESS,
    UPLOAD_IMAGES_FAILURE,

    RETWEET_REQUEST,
    RETWEET_SUCCESS,
    RETWEET_FAILURE,
    
} from '../reducers/post';

import { 
    ADD_POST_TO_ME, 
    REMOVE_POST_OF_ME,
} from '../reducers/user';


function likePostAPI(data) {
    return axios.patch(`/post/${data}/like`);
};

function* likePost(action) {
    try {

        const result = yield call(likePostAPI, action.data);

        yield put({
            type: LIKE_POST_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LIKE_POST_FAILURE,
            error: err.response.data,
        });
    }
}


function unlikePostAPI(data) {
    return axios.delete(`/post/${data}/like`);
};

function* unlikePost(action) {
    try {

        const result = yield call(unlikePostAPI, action.data);

        yield put({
            type: UNLIKE_POST_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: UNLIKE_POST_FAILURE,
            error: err.response.data,
        });
    }
}

function loadPostsAPI(lastId) {
    return axios.get(`/posts?lastId=${lastId || 0}`);
};

function* loadPosts(action) {
    try {

            const result = yield call(loadPostsAPI, action.lastId);

        yield put({
            type: LOAD_POSTS_SUCCESS,
            data: result.data,
            // data: generateDummyPost(10),
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LOAD_POSTS_FAILURE,
            error: err.response.data,
        });
    }
}




function loadPostAPI(data) {
    return axios.get(`/post/${data}`);
};

function* loadPost(action) {
    try {
            const result = yield call(loadPostAPI, action.data);

        yield put({
            type: LOAD_POST_SUCCESS,
            data: result.data,
            // data: generateDummyPost(10),
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LOAD_POST_FAILURE,
            error: err.response.data,
        });
    }
}


function addCommentAPI(data) {
    return axios.post(`/post/${data.postId}/comment`, data);    // POST /post/1/comment
};

function* addComment(action) {
    try {

        const result = yield call(addCommentAPI, action.data);
        // yield delay(1000);
        yield put({
            type: ADD_COMMENT_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: ADD_COMMENT_FAILURE,
            error: err.response.data,
        });
    };
};



function addPostAPI(data) {
    return axios.post('/post', data);

};

function* addPost(action) {
    try {

        const result = yield call(addPostAPI, action.data);

        yield put({
            type: ADD_POST_SUCCESS,
            data: result.data
        });
        yield put({
            type: ADD_POST_TO_ME,
            data: result.data.id,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: ADD_POST_FAILURE,
            error: err.response.data,
        });
    };
};


function removePostAPI(data) {
    return axios.delete(`/post/${data}`);
};

function* removePost(action) {
    try {
        const result = yield call(removePostAPI, action.data);

        yield put({
            type: REMOVE_POST_SUCCESS,
            data: result.data,
        });
        console.log('removePost');
        yield put({
            type: REMOVE_POST_OF_ME,
            data: action.data,
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: REMOVE_POST_FAILURE,
            error: err.response.data,
        });
    };
};

function uploadImagesAPI(data) {
    return axios.post('/post/images', data);  // POST /post/images
};

function* uploadImages(action) {
    try {
        const result = yield call(uploadImagesAPI, action.data);

        yield put({
            type: UPLOAD_IMAGES_SUCCESS,
            data: result.data,
        });

    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: UPLOAD_IMAGES_FAILURE,
            error: err.response.data,
        });
    };
};

function retweetAPI(data) {
    return axios.post(`/post/${data}/retweet`);  // POST /post/images
};

function* retweet(action) {
    try {

        const result = yield call(retweetAPI, action.data);

        yield put({
            type: RETWEET_SUCCESS,
            data: result.data,
        });

    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: RETWEET_FAILURE,
            error: err.response.data,
        });
    };
};



function loadHashtagPostsAPI(data, lastId) {

    return axios.get(`/hashtag/${data}?lastId=${lastId || 0}`);
};

function* loadHashtagPosts(action) {
    try {

            const result = yield call(loadHashtagPostsAPI, action.data, action.lastId);

        yield put({
            type: LOAD_HASHTAG_POSTS_SUCCESS,
            data: result.data,

        });
    } catch (err) {
        console.error(err);
        yield put({   
            type: LOAD_HASHTAG_POSTS_FAILURE,
            error: err.response.data,
        });
    }
}

function loadUserPostsAPI(data, lastId) {

    return axios.get(`/user/${data}/posts?lastId=${lastId || 0}`);
};

function* loadUserPosts(action) {
    try {

            const result = yield call(loadUserPostsAPI, action.data, action.lastId);

        yield put({
            type: LOAD_USER_POSTS_SUCCESS,
            data: result.data,
            // data: generateDummyPost(10),
        });
    } catch (err) {
        console.error(err);
        yield put({         // put => dispatch 다.
            type: LOAD_USER_POSTS_FAILURE,
            error: err.response.data,
        });
    }
}


function* watchLoadUserPosts() {

    yield throttle(5000, LOAD_USER_POSTS_REQUEST, loadUserPosts);
};

function* watchLoadHashtagPosts() {
    // 5초에 한번 게사글이 로드 된다.
    yield throttle(5000, LOAD_HASHTAG_POSTS_REQUEST, loadHashtagPosts); 
};




function* watchLoadPosts() {
    // 5초에 한번 게사글이 로드 된다.
    yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts);
};

function* watchLoadPost() {
    // 5초에 한번 게사글이 로드 된다.
    yield takeLatest(LOAD_POST_REQUEST, loadPost); 
};

function* watchRetweet() {
    // 5초에 한번 게사글이 로드 된다.
    yield takeLatest(RETWEET_REQUEST, retweet);
};

function* watchAddPost() {
    yield takeLatest(ADD_POST_REQUEST, addPost);
};

function* watchUploadImages() {
    yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages);
};

function* watchAddComment() {
    yield takeLatest(ADD_COMMENT_REQUEST, addComment);   
};

function* watchRemovePost() {
    yield takeLatest(REMOVE_POST_REQUEST, removePost);   
};

function* watchLikePost() {
    yield takeLatest(LIKE_POST_REQUEST, likePost);   
};

function* watchUnLikePost() {
    yield takeLatest(UNLIKE_POST_REQUEST, unlikePost);   
};

export default function* postSaga() {
    yield all([             
        fork(watchRetweet),
        fork(watchUploadImages),
        fork(watchLikePost),
        fork(watchUnLikePost),
        fork(watchAddPost),
        fork(watchLoadPosts),
        fork(watchLoadHashtagPosts),
        fork(watchLoadUserPosts),
        fork(watchLoadPost),
        fork(watchRemovePost),
        fork(watchAddComment),
    ]);
};




reducers/post.js
import produce from 'immer';

import { 
    REMOVE_POST_OF_ME 
} from './user';


export const initialState = {
    mainPosts: [],
    imagePaths: [],   // 이미지를 업로드 할때 이미지 경로
    singlePost: null,

    postAdded: false,

    hasMorePosts: true,

    likePostLoading: false,
    likePostDone: false, // 추가
    likePostError: null, // 추가

    retweetLoading: false,
    retweetDone: false, // 추가
    retweetError: null, // 추가

    uploadImagesLoading: false,
    uploadImagesDone: false, // 추가
    uploadImagesError: null, // 추가

    unlikePostLoading: false,
    unlikePostDone: false, // 추가
    unlikePostError: null, // 추가
    
    loadPostsLoading: false,
    loadPostsDone: false, // 추가
    loadPostsError: null, // 추가

    loadPostLoading: false,
    loadPostDone: false, // 추가
    loadPostError: null, // 추가
    
    // 게시글 추가가 완료되었을때 true 로 변한다.    postAdded: false,  =>  addPostLoading: false,
    addPostLoading: false,
    addPostDone: false, // 추가
    addPostError: null, // 추가
    
    addCommentLoading: false, // 댓글 추가가 완료되었을때 true 로 변한다.
    addCommentDone: false, // 추가
    addCommentError: null, // 추가
    
    removePostLoading: false,
    removePostDone: false, // 추가
    removePostError: null, // 추가
};

export const UPLOAD_IMAGES_REQUEST = 'UPLOAD_IMAGES_REQUEST';
export const UPLOAD_IMAGES_SUCCESS = 'UPLOAD_IMAGES_SUCCESS';
export const UPLOAD_IMAGES_FAILURE = 'UPLOAD_IMAGES_FAILURE';

export const RETWEET_REQUEST = 'RETWEET_REQUEST';
export const RETWEET_SUCCESS = 'RETWEET_SUCCESS';
export const RETWEET_FAILURE = 'RETWEET_FAILURE';

export const LOAD_POSTS_REQUEST = 'LOAD_POSTS_REQUEST';
export const LOAD_POSTS_SUCCESS = 'LOAD_POSTS_SUCCESS';
export const LOAD_POSTS_FAILURE = 'LOAD_POSTS_FAILURE';

export const LOAD_POST_REQUEST = 'LOAD_POST_REQUEST';
export const LOAD_POST_SUCCESS = 'LOAD_POST_SUCCESS';
export const LOAD_POST_FAILURE = 'LOAD_POST_FAILURE';


export const LOAD_USER_POSTS_REQUEST = 'LOAD_USER_POSTS_REQUEST';
export const LOAD_USER_POSTS_SUCCESS = 'LOAD_USER_POSTS_SUCCESS';
export const LOAD_USER_POSTS_FAILURE = 'LOAD_USER_POSTS_FAILURE';

export const LOAD_HASHTAG_POSTS_REQUEST = 'LOAD_HASHTAG_POSTS_REQUEST';
export const LOAD_HASHTAG_POSTS_SUCCESS = 'LOAD_HASHTAG_POSTS_SUCCESS';
export const LOAD_HASHTAG_POSTS_FAILURE = 'LOAD_HASHTAG_POSTS_FAILURE';


export const LIKE_POST_REQUEST = 'LIKE_POST_REQUEST';
export const LIKE_POST_SUCCESS = 'LIKE_POST_SUCCESS';
export const LIKE_POST_FAILURE = 'LIKE_POST_FAILURE';

export const UNLIKE_POST_REQUEST = 'UNLIKE_POST_REQUEST';
export const UNLIKE_POST_SUCCESS = 'UNLIKE_POST_SUCCESS';
export const UNLIKE_POST_FAILURE = 'UNLIKE_POST_FAILURE';

export const ADD_POST_REQUEST = 'ADD_POST_REQUEST';
export const ADD_POST_SUCCESS = 'ADD_POST_SUCCESS';
export const ADD_POST_FAILURE = 'ADD_POST_FAILURE';

export const REMOVE_POST_REQUEST = 'REMOVE_POST_REQUEST';
export const REMOVE_POST_SUCCESS = 'REMOVE_POST_SUCCESS';
export const REMOVE_POST_FAILURE = 'REMOVE_POST_FAILURE';

export const ADD_COMMENT_REQUEST = 'ADD_COMMENT_REQUEST';
export const ADD_COMMENT_SUCCESS = 'ADD_COMMENT_SUCCESS';
export const ADD_COMMENT_FAILURE = 'ADD_COMMENT_FAILURE';

export const REMOVE_IMAGE = 'REMOVE_IMAGE';


export const addComment = (data) => ({
    type: ADD_COMMENT_REQUEST,
    data,
});

export const addPost = (data) => ({
    type: ADD_POST_REQUEST,
    data,
});

export const removePost = (data) => ({
    type: REMOVE_POST_OF_ME,
    data,
});

const reducer = (state = initialState, action) =>  produce(state, (draft) => {
        switch (action.type) {

            case REMOVE_IMAGE:
                draft.imagePaths = draft.imagePaths.filter((v, i) => i !== action.data);
                break;

            case RETWEET_REQUEST:
                draft.retweetLoading = true;
                draft.retweetDone = false;
                draft.retweetError = null;
                break;
            case RETWEET_SUCCESS: 
                draft.retweetLoading = false;
                draft.retweetDone = true;
                draft.mainPosts.unshift(action.data);
                break;
            case RETWEET_FAILURE:
                draft.retweetLoading = false;
                draft.retweetError = action.error;
                break;

            case UPLOAD_IMAGES_REQUEST:
                draft.uploadImagesLoading = true;
                draft.uploadImagesDone = false;
                draft.uploadImagesError = null;
                break;
            case UPLOAD_IMAGES_SUCCESS: {
                draft.imagePaths = action.data;
                draft.uploadImagesLoading = false;
                draft.uploadImagesDone = true;
                break;
            }
            case UPLOAD_IMAGES_FAILURE:
                draft.uploadImagesLoading = false;
                draft.uploadImagesError = action.error;
                break;

            case LIKE_POST_REQUEST:
                draft.likePostLoading = true;
                draft.likePostDone = false;
                draft.likePostError = null;
                break;
            case LIKE_POST_SUCCESS: {
                const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
                post.Likers.push({ id: action.data.UserId });
                draft.likePostLoading = false;
                draft.likePostDone = true;
                break;
            }
            case LIKE_POST_FAILURE:
                draft.likePostLoading = false;
                draft.likePostError = action.error;
                break;

            case UNLIKE_POST_REQUEST:
                draft.unlikePostLoading = true;
                draft.unlikePostDone = false;
                draft.unlikePostError = null;
                break;
            case UNLIKE_POST_SUCCESS: {
                console.log(action.data);
                const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
                post.Likers = post.Likers.filter((v) => v.id !== action.data.UserId); // 제거
                draft.unlikePostLoading = false;
                draft.unlikePostDone = true;
                break;
            }
            case UNLIKE_POST_FAILURE:
                draft.unlikePostLoading = false;
                draft.unlikePostError = action.error;
                break;

            case LOAD_USER_POSTS_REQUEST:
            case LOAD_HASHTAG_POSTS_REQUEST:
            case LOAD_POSTS_REQUEST:
                draft.loadPostsLoading = true;
                draft.loadPostsDone = false;
                draft.loadPostsError = null;
                break;
            case LOAD_USER_POSTS_SUCCESS:
            case LOAD_HASHTAG_POSTS_SUCCESS:
            case LOAD_POSTS_SUCCESS:
                draft.loadPostsLoading = false;
                draft.loadPostsDone = true;
                draft.mainPosts = draft.mainPosts.concat(action.data);
                draft.hasMorePosts = action.data.length === 10;
                break;
            case LOAD_USER_POSTS_FAILURE:
            case LOAD_HASHTAG_POSTS_FAILURE:
            case LOAD_POSTS_FAILURE:
                draft.loadPostsLoading = false;
                draft.loadPostsError = action.error;
                break;

            case LOAD_POST_REQUEST:
                draft.loadPostLoading = true;
                draft.loadPostDone = false;
                draft.loadPostError = null;
                break;
            case LOAD_POST_SUCCESS:
                draft.loadPostLoading = false;
                draft.loadPostDone = true;
                draft.singlePost = action.data;
                break;
            case LOAD_POST_FAILURE:
                draft.loadPostLoading = false;
                draft.loadPostError = action.error;
                break;

            case ADD_POST_REQUEST:
                draft.addPostLoading = true;
                draft.addPostDone = false;
                draft.addPostError = null;
                break;
            case ADD_POST_SUCCESS:
                draft.addPostLoading = false;
                draft.addPostDone = true;
                draft.mainPosts.unshift(action.data);
                draft.imagePaths = [];
                break;
            case ADD_POST_FAILURE:
                draft.addPostLoading = true;
                draft.addPostError = action.error;
                break;

            case ADD_COMMENT_REQUEST:
                draft.addCommentLoading = true;
                draft.addCommentDone = false;
                draft.addCommentError = null; 
                break;
            case ADD_COMMENT_SUCCESS:
                // action.data.content, action.data.postId, action.data.userId
                // post.Comments.unshift(dummyComment(action.data.content));
                const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
                post.Comments.unshift(action.data);
                draft.addCommentLoading = false;
                draft.addCommentDone = true;
                break;
            case ADD_COMMENT_FAILURE:
                draft.addCommentLoading = false;
                draft.addCommentError = action.error;
                break;
            case REMOVE_POST_REQUEST:
                draft.removePostLoading = true;
                draft.removePostDone = false;
                draft.removePostError = null;
                break;
                //  [ !== 일치하는 게시글만 지운다. ] 그리고 [ === 일치하는 게시글 이외의 모든 게시글이 지워진다. ]
                // filter를 사용해서 불변성을 유지한다. 
            case REMOVE_POST_SUCCESS:
                draft.removePostLoading = false;
                draft.removePostDone = true;
                draft.mainPosts = draft.mainPosts.filter((v) => v.id !==  action.data.PostId);
                break;
            case REMOVE_POST_FAILURE:
                draft.removePostLoading = false;
                draft.removePostError = action.error;
                break;
            default:
                break;
        
        }
});

export default reducer;





reducers/user.js

import produce from 'immer';

export const initialState = {


    removeFollowerLoading: false,   // 내가 수락한 친구 차단하기
    removeFollowerDone: false, // 추가
    removeFollowerError: null, // 추가

    loadUserLoading: false,     // 사용자 정보 가져오기 시도중..  로딩창을 띄운다.
    loadUserDone: false,
    loadUserError: null,

    loadMyInfoLoading: false,     // 내 정보 가져오기 시도중..  로딩창을 띄운다.
    loadMyInfoDone: false,
    loadMyInfoError: null,

    logInLoading: false,     // 로그인 시도중..  로딩창을 띄운다.
    logInDone: false,
    logInError: null,

    logOutLoading: false,     // 로그아웃 시도중..   로딩창을 띄운다.
    logOutDone: false,
    logOutError: null,

    followLoading: false,     // 팔로우 시도중..  로딩창을 띄운다.
    followDone: false,
    followError: null,

    unfollowLoading: false,     // 언팔로우 시도중..  로딩창을 띄운다.
    unfollowDone: false,
    unfollowError: null,

    loadFollowersLoading: false,     // 팔로워 목록 가져오기 시도중..  로딩창을 띄운다.
    loadFollowersDone: false,
    loadFollowersError: null,

    loadFollowingsLoading: false,     // 팔로윙 목록 가져오기 시도중..  로딩창을 띄운다.
    loadFollowingsDone: false,
    loadFollowingsError: null,

    signUpLoading: false,       // 회원가입 시도중.. 로딩창을 띄운다.
    signUpDone: false,
    signUpError: null,

    changeNicknameLoading: false,       // 닉네임 변경 시도중.. 로딩창을 띄운다.
    changeNicknameDone: false,
    changeNicknameError: null,

    me: null,
    userInfo: null,
};

export const REMOVE_FOLLOWER_REQUEST = 'REMOVE_FOLLOWER_REQUEST';
export const REMOVE_FOLLOWER_SUCCESS = 'REMOVE_FOLLOWER_SUCCESS';
export const REMOVE_FOLLOWER_FAILURE = 'REMOVE_FOLLOWER_FAILURE';

export const LOAD_MY_INFO_REQUEST = 'LOAD_MY_INFO_REQUEST';
export const LOAD_MY_INFO_SUCCESS = 'LOAD_MY_INFO_SUCCESS';
export const LOAD_MY_INFO_FAILURE = 'LOAD_MY_INFO_FAILURE';

export const LOAD_USER_REQUEST = 'LOAD_USER_REQUEST';
export const LOAD_USER_SUCCESS = 'LOAD_USER_SUCCESS';
export const LOAD_USER_FAILURE = 'LOAD_USER_FAILURE';

export const LOG_IN_REQUEST = 'LOG_IN_REQUEST';
export const LOG_IN_SUCCESS = 'LOG_IN_SUCCESS';
export const LOG_IN_FAILURE = 'LOG_IN_FAILURE';

export const LOG_OUT_REQUEST = 'LOG_OUT_REQUEST';
export const LOG_OUT_SUCCESS = 'LOG_OUT_SUCCESS';
export const LOG_OUT_FAILURE = 'LOG_OUT_FAILURE';

export const SIGN_UP_REQUEST = 'SIGN_UP_REQUEST';
export const SIGN_UP_SUCCESS = 'SIGN_UP_SUCCESS';
export const SIGN_UP_FAILURE = 'SIGN_UP_FAILURE';

export const CHANGE_NICKNAME_REQUEST = 'CHANGE_NICKNAME_REQUEST';
export const CHANGE_NICKNAME_SUCCESS = 'CHANGE_NICKNAME_SUCCESS';
export const CHANGE_NICKNAME_FAILURE = 'CHANGE_NICKNAME_FAILURE';

export const FOLLOW_REQUEST = 'FOLLOW_REQUEST';
export const FOLLOW_SUCCESS = 'FOLLOW_SUCCESS';
export const FOLLOW_FAILURE = 'FOLLOW_FAILURE';

export const UNFOLLOW_REQUEST = 'UNFOLLOW_REQUEST';
export const UNFOLLOW_SUCCESS = 'UNFOLLOW_SUCCESS';
export const UNFOLLOW_FAILURE = 'UNFOLLOW_FAILURE';

export const LOAD_FOLLOWERS_REQUEST = 'LOAD_FOLLOWERS_REQUEST';
export const LOAD_FOLLOWERS_SUCCESS = 'LOAD_FOLLOWERS_SUCCESS';
export const LOAD_FOLLOWERS_FAILURE = 'LOAD_FOLLOWERS_FAILURE';

export const LOAD_FOLLOWINGS_REQUEST = 'LOAD_FOLLOWINGS_REQUEST';
export const LOAD_FOLLOWINGS_SUCCESS = 'LOAD_FOLLOWINGS_SUCCESS';
export const LOAD_FOLLOWINGS_FAILURE = 'LOAD_FOLLOWINGS_FAILURE';

export const ADD_POST_TO_ME = 'ADD_POST_TO_ME';
export const REMOVE_POST_OF_ME = 'REMOVE_POST_OF_ME';

export const ADD_COMMENT_TO_ME = 'ADD_COMMENT_TO_ME';
export const REMOVE_COMMENT_OF_ME = 'REMOVE_COMMENT_OF_ME';

const reducer = (state = initialState, action) => produce (state, (draft) => {
        switch (action.type) {

            case REMOVE_FOLLOWER_REQUEST:
                draft.removeFollowerLoading = true;
                draft.removeFollowerDone = false;
                draft.removeFollowerError = null;
                break;
            case REMOVE_FOLLOWER_SUCCESS: {
                draft.removeFollowerLoading = false;
                draft.me.Followers = draft.me.Followers.filter((v) => v.id !== action.data.UserId);
                draft.removeFollowerDone = true;
                break;
            }
            case REMOVE_FOLLOWER_FAILURE:
                draft.removeFollowerLoading = false;
                draft.removeFollowerError = action.error;
                break;

            case LOAD_MY_INFO_REQUEST:
                draft.loadMyInfoLoading = true;
                draft.loadMyInfoError = null;
                draft.loadMyInfoDone = false;
                break;
            case LOAD_MY_INFO_SUCCESS:
                draft.loadMyInfoLoading = false;
                draft.me = action.data;
                draft.loadMyInfoDone = true;
                break;
            case LOAD_MY_INFO_FAILURE:
                draft.loadMyInfoLoading = false;        // isLoggingOut: true, => logOutLoading: true,
                draft.loadMyInfoError = action.error;   // logInError: action.error, => logOutDone: false,
                break;

            case LOAD_USER_REQUEST:
                draft.loadUserLoading = true;
                draft.loadUserError = null;
                draft.loadUserDone = false;
                break;
            case LOAD_USER_SUCCESS:
                draft.loadUserLoading = false;
                draft.userInfo = action.data;
                draft.loadUserDone = true;
                break;
            case LOAD_USER_FAILURE:
                draft.loadUserLoading = false;        // isLoggingOut: true, => logOutLoading: true,
                draft.loadUserError = action.error;   // logInError: action.error, => logOutDone: false,
                break;
            
            case LOAD_FOLLOWERS_REQUEST:
                draft.loadFollowersLoading = true;
                draft.loadFollowersError = null;
                draft.loadFollowersDone = false;
                break;
            case LOAD_FOLLOWERS_SUCCESS:
                draft.loadFollowersLoading = false;
                draft.me.Followers = action.data;
                draft.loadFollowersDone = true;
                break;
            case LOAD_FOLLOWERS_FAILURE:
                draft.loadFollowersLoading = false;
                draft.loadFollowersError = action.error;
                break;

            case LOAD_FOLLOWINGS_REQUEST:
                draft.loadFollowingsLoading = true;
                draft.loadFollowingsError = null;
                draft.loadFollowingsDone = false;
                break;
            case LOAD_FOLLOWINGS_SUCCESS:
                draft.loadFollowingsLoading = false;
                draft.me.Followings = action.data;
                draft.loadFollowingsDone = true;
                break;
            case LOAD_FOLLOWINGS_FAILURE:
                draft.loadFollowingsLoading = false;
                draft.loadFollowingsError = action.error;
                break;

            case FOLLOW_REQUEST:
                draft.followLoading = true;
                draft.followError = null;
                draft.followDone = false;
                break;
            case FOLLOW_SUCCESS:
                draft.followLoading = false;
                draft.me.Followings.push({ id: action.data.UserId });
                draft.followDone = true;
                break;
            case FOLLOW_FAILURE:
                draft.followLoading = false;        // isLoggingOut: true, => logOutLoading: true,
                draft.followError = action.error;   // logInError: action.error, => logOutDone: false,
                break;

            case UNFOLLOW_REQUEST:
                draft.unfollowLoading = true;
                draft.unfollowError = null;
                draft.unfollowDone = false;
                break;
            case UNFOLLOW_SUCCESS:
                draft.unfollowLoading = false;
                draft.unfollowDone = true;
                draft.me.Followings = draft.me.Followings.filter((v) => v.id !== action.data.UserId );
                break;
            case UNFOLLOW_FAILURE:
                draft.unfollowLoading = false;        // isLoggingOut: true, => logOutLoading: true,
                draft.unfollowError = action.error;   // logInError: action.error, => logOutDone: false,
                break;

            case CHANGE_NICKNAME_REQUEST:
                draft.changeNicknameLoading = true;     // 닉네임 변경 시도중이니까  => 버튼 로딩 O
                draft.changeNicknameDone = false;       // 닉네임 변경중
                draft.changeNicknameError = null;
                break;
            case CHANGE_NICKNAME_SUCCESS:
                draft.me.nickname = action.data.nickname;
                draft.changeNicknameLoading = false;    // 닉네임 변경 요청이 성공했으니까 => 버튼 로딩 X
                draft.changeNicknameDone = true;        // 닉네임 변경 완료
                break;
            case CHANGE_NICKNAME_FAILURE:
                draft.changeNicknameLoading = false;        // 닉네임 변경 요청이 끝났으니까 => 버튼 로딩 X
                draft.changeNicknameError = action.error;  
                break;

            case SIGN_UP_REQUEST:
                draft.signUpLoading = true;     // 회원가입 시도중이니까  => true   isLoggingOut: true, => logInLoading: true,
                draft.signUpDone = false;
                draft.signUpError = null;
                break;
            case SIGN_UP_SUCCESS:
                draft.signUpLoading = false;    // 회원가입 요청이 성공했으니까 => false   isLoggingOut: false, => logOutLoading: false,
                draft.signUpDone = true;        // isLoggedIn: false, => logOutDone: true, 
                break;
            case SIGN_UP_FAILURE:
                draft.signUpLoading = false;        // 회원가입 요청이 끝났으니까 => false    isLoggingOut: false, => logOutLoading: false,
                draft.signUpError = action.error;  // 추가
                break;

            case LOG_IN_REQUEST:
                draft.logInLoading = true;
                draft.logInError = null;
                draft.logInDone = false;
                break;
            case LOG_IN_SUCCESS:
                draft.logInLoading = false;
                draft.logInDone = true;
                draft.me = action.data;
                break;
            case LOG_IN_FAILURE:
                draft.logInLoading = false;        // isLoggingOut: true, => logOutLoading: true,
                draft.logInError = action.error;   // logInError: action.error, => logOutDone: false,
                break;

            case LOG_OUT_REQUEST:
                draft.logOutLoading = true;     // 로그아웃 시도중이니까  => true
                draft.logOutDone = false;
                draft.logOutError = null;
                break;
            case LOG_OUT_SUCCESS:
                draft.logOutLoading = false;    // 로그아웃 요청이 성공했으니까 => false   isLoggingOut: false, => logOutLoading: false,
                draft.logOutDone = true;        // isLoggedIn: false, => logOutDone: true, 
                draft.me = null;
                break;
            case LOG_OUT_FAILURE:
                draft.isLoggingOut = false;        // 요청이 끝났으니까 => false    isLoggingOut: false, => logOutLoading: false,
                draft.logOutError = action.error;  // 추가
                break;
                
            case ADD_POST_TO_ME:
                draft.me.Posts.unshift({ id: action.data });
                break;

            case REMOVE_POST_OF_ME:
                draft.me.Posts = draft.me.Posts.filter((v) => v.id !== action.data);
                break;
            default:
                break;
            }
    });

export default reducer;


답변 2

0

castinglife님의 프로필 이미지
castinglife
질문자

ㅡ.ㅡ  다음부터는 좀 더 자세히 보도록 하겠습니다..

0

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

mainPosts[mainPosts.length - 1].id
오타입니다.
castinglife님의 프로필 이미지
castinglife

작성한 질문수

질문하기