inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

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

게시글 불러오기

무한정 back으로부터 post 데이터를 불러와요!

203

seonjun Moon

작성한 질문수 35

0

안녕하세요 zerocho 선생님...

다른게 아니라 이 부분에서 아주 크나큰 문제에 맞닥뜨려 

작업을 못하고 있습니다. 

 

pages/index.js에서  useEffect를 사용해서 

첫 페이지에 오면 로그인 유무를 판단하고 

또 기존에 있던 게시글을 불러와야 하는것을 잘 아는데

 

문제는 로그인할 경우에는 딱 한번만 LOAD_MY_INFO_REQUEST를 잘 수행하는데  LOAD_POST_REQUEST를하는순간 무한 응답을 합니다. 

 

 

 

post man에서 받아온 데이터를 다음과 같습니다.

 

사실 저는 springboot를 back으로 사용하고 있는데 

 

이유는 JPA를 좀 사용해보고 싶어서 였습니다.

 

next.js를 아예 안쓰는것이 나앗던 것 같지만 처음에 강의를 접하고 할때는 아무것도 몰라서...  무진 후회중.. 그냥따라할걸...

 

하여간... 기존의 데이터도 이런 식으로 받아오는데 문제는 

무한 응답을 요청하니까 도대체 어디서 잘못된건지 모르겠다는 것입니다. ㅠㅠ 제발좀 도와주십시오.;.. 

 

pages/index.js

import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import AppLayout from "../components/AppLayout";
import PostCard from "../components/middleComponent/Post/PostCard";
import PostForm from "../components/middleComponent/Post/PostForm";
import { LOAD_POST_REQUEST } from "../reducers/post";
import { LOAD_MY_INFO_REQUEST } from "../reducers/user";

const Home = () => {
    const dispatch = useDispatch();
    const { me } = useSelector((state) => state.user);
    const { mainPosts, hasMorePost, loadPostLoading } = useSelector((state) => state.post);

    useEffect(() => {
        console.log("useEffect in index.js")
        dispatch({
            type: LOAD_MY_INFO_REQUEST
        });
        // dispatch({
        //     type: LOAD_POST_REQUEST,
        // });
    }, [hasMorePost, loadPostLoading]);

    useEffect(() => {
        function onScroll() {
            if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight -  300){
                if (hasMorePost && !loadPostLoading) {
                    dispatch({
                        type: LOAD_POST_REQUEST,
                    });
                }
            }
        }
        window.addEventListener('scroll', onScroll);
        return () => {
            window.removeEventListener('scroll', onScroll);
        }
    }, []);
    console.log(mainPosts);
    return (
        <AppLayout>
            {me && <PostForm />}
            {mainPosts.map((post) => <PostCard key={post.id} post={post}/>)}
        </AppLayout>
    );
}

export default Home;

 

reducers/post.js

 
import shortId from 'shortid';
import produces, { produce } from 'immer';
import faker from 'faker';
import { ConsoleSqlOutlined } from '@ant-design/icons';
export const initialState = {
    mainPosts: [],
    imagePaths: [],
    hasMorePost: true,
    loadPostLoading: false,
    loadPostDone: false,
    loadPostError: null,
    addPostLoading: false,
    addPostDone: false,
    addPostError: null,
    addCommentLoading: false,
    addCommentDone: false,
    addCommentError: null,
    removePostLoading: false,
    removePostDone: false,
    removePostError: null,
}

export const generateDummyPost = (number) => Array(number).fill().map(() => ({
    id: shortId.generate(),
    User: {
        id: shortId.generate(),
        nickname: faker.name.findName(),
    },
    content:faker.lorem.paragraph(),
    Images: [{
        src: faker.image.imageUrl(),
    }],
    Comments: [{
        User: {
            id: shortId.generate(),
            nickname: faker.name.findName()
        },
        content: faker.lorem.sentence(),
    }],
}));

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 ADD_POST_REQUEST = 'ADD_POST_REQUEST';
export const ADD_POST_SUCCESS = 'ADD_POST_SUCCESS';
export const ADD_POST_FAILURE = 'ADD_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_POST_REQUEST = 'REMOVE_POST_REQUEST';
export const REMOVE_POST_SUCCESS = 'REMOVE_POST_SUCCESS';
export const REMOVE_POST_FAILURE = 'REMOVE_POST_FAILURE';

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

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

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

// const dummyPost = (data) => ({
//     id: data.id,
//     content: data.content,
//     User: {
//         id: 1,
//         nickname: 'thelovedaejeon',
//     },
//     Images: [],
//     Comments: [],
// });

// const dummyComment = (data) => ({
//     id: shortId.generate(),
//     content: data,
//     User: {
//         id: 1,
//         nickname: 'thelovedaejeon',
//     },
// })

//이전 상태를 action을 통해 다음 상태로 만들어 내는 함수 (불변성을 지키면서)
const reducer = (state = initialState, action) => {
    return produce (state, (draft) => {
        switch (action.type){
            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.mainPosts = action.data.concat(draft.mainPosts);
                draft.hasMorePost = false;
                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(dummyPost(action.data));
                break;
            case ADD_POST_FAILURE:
                draft.addPostLoading = false;
                draft.addPostError = action.error;
                break;
            case REMOVE_POST_REQUEST:
                draft.removePostLoading = true;
                draft.removePostDone = false;
                draft.removePostError = null;
                break;
            case REMOVE_POST_SUCCESS:
                draft.removePostLoading = false;
                draft.removePostDone = true;
                draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data);
                break;
            case REMOVE_POST_FAILURE:
                draft.removePostLoading = false;
                draft.removePostError = action.error;
                break;
            case ADD_COMMENT_REQUEST:
                draft.addCommentLoading = true;
                draft.addCommentDone = false;
                draft.addCommentError = null;
                break;
            case ADD_COMMENT_SUCCESS:{
                const post = draft.mainPosts.find((v) => v.id === action.data.postId);
                post.Comments.unshift(dummyComment(action.data.content));
                draft.addCommentLoading = false;
                draft.addCommentDone = true;
                break;
                // const postIndex = state.mainPosts.findIndex((v) => v.id === action.data.postId);
                // const post = { ...state.mainPosts[postIndex] };
                // post.Comments = [dummyComment(action.data.content), ...post.Comments];
                // const mainPosts = [...state.mainPosts];
                // mainPosts[postIndex] = post;
                // return {
                //   ...state,
                //   mainPosts,
                //   addCommentLoading: false,
                //   addCommentDone: true,
                // };
            }
            case ADD_COMMENT_FAILURE:
                draft.addCommentLoading = false;
                draft.addCommentError = action.error;
                break;
            default:
                break;
        }
    });
};

export default reducer;

 

sagas/post.js

import { delay, fork, all, takeLatest, put, call} from "redux-saga/effects";
import shortId from "shortid";
import axios from 'axios';
// import Axios from 'axios';
// import qs from 'query-string';
import Cookies from 'universal-cookie';
import {
    ADD_COMMENT_FAILURE,
    ADD_COMMENT_REQUEST,
    ADD_COMMENT_SUCCESS,
    ADD_POST_FAILURE,
    ADD_POST_REQUEST,
    ADD_POST_SUCCESS,
    ADD_POST_TO_ME,
    generateDummyPost,
    LOAD_POST_FAILURE,
    LOAD_POST_REQUEST,
    LOAD_POST_SUCCESS,
    REMOVE_POST_FAILURE,
    REMOVE_POST_REQUEST,
    REMOVE_POST_SUCCESS
} from "../reducers/post";
import { REMOVE_POST_OF_ME } from "../reducers/user";
const cookies = new Cookies();
function addPostAPI(data) {
    const accessToken = cookies.get("accessToken");
    const userEmail = cookies.get("userEmail");
    const newObj = {
        description : data,
        email : userEmail
    }
    return axios.post('/auth/post', newObj,{
        headers:{
            'Authorization': `Bearer ${accessToken}`,
            "Content-Type": "application/json"
        }
    });
}

function* addPost(action) {
    try {
        const result = yield call(addPostAPI, action.data);
        yield put({
            type: ADD_POST_SUCCESS,
            data: {
                id : result.data.postId,
                content: action.data
            }
        })
        yield put({
            type: ADD_POST_TO_ME,
            data: result.data.postId,
        })
    } catch (error) {
        console.log(error);
        yield put({
            type: ADD_POST_FAILURE,
            data: error.data
        })
    }
}

function loadPostAPI(data) {
    return axios.get('/api/posts');
}

function* loadPost(action) {
    try {
        const result = yield call(loadPostAPI);
        console.log(result);
        console.log(result.data);
        yield put({
            type: LOAD_POST_SUCCESS,
            data: result.data.result,
        });
    } catch (error) {
        console.log(error);
        yield put({
            type: LOAD_POST_FAILURE,
            data: error.data
        })
    }
}

function removePostAPI(data) {
    return axios.post('/api/post', data);
}

function* removePost(action) {
    try {

        delay(1000);
        // const result = yield call(addPostAPI, action.data);
        const id = shortId.generate();
        yield put({
            type: REMOVE_POST_SUCCESS,
            data: action.data
           
        });
        yield put({
            type: REMOVE_POST_OF_ME,
            data: action.data
        })
    } catch (error) {
        yield put({
            type: REMOVE_POST_FAILURE,
            data: error.data
        })
    }
}

function addCommentAPI(data) {
    return axios.post('/api/post/${data.postId}/comment', data);
}

function* addComment(action) {
    try {
        delay(1000);
        // const result = yield call(addPostAPI, action.data);
        yield put({
            type: ADD_COMMENT_SUCCESS,
            data: action.data
        })
    } catch (error) {
        yield put({
            type: ADD_COMMENT_FAILURE,
            data: error.data
        })
    }
}

function* watchAddPost(){
    yield takeLatest(ADD_POST_REQUEST, addPost); // 첫번째것만 하고 싶으면 takeLeading
}

function* watchLoadPost(){
    yield takeLatest(LOAD_POST_REQUEST, loadPost); // 첫번째것만 하고 싶으면 takeLeading
}

function* watchRemovePost(){
    yield takeLatest(REMOVE_POST_REQUEST, removePost); // 첫번째것만 하고 싶으면 takeLeading
}

function* watchAddComment(){
    yield takeLatest(ADD_COMMENT_REQUEST, addComment); // 첫번째것만 하고 싶으면 takeLeading
}

export default function* postSaga(){
    yield all([
        fork(watchLoadPost),
        fork(watchAddPost),
        fork(watchRemovePost),
        fork(watchAddComment),
    ])
};

 

 

nodejs redux express react Next.js

답변 1

1

제로초(조현영)

useEffect의 []에서 hasMorePost랑 isPostLoading을 빼세요.

0

seonjun Moon

아 진짜... 너무감사합니다... 정말 좋은 강의 잘 듣고있습니다. ㅠㅠ

넥스트 버젼 질문

0

90

2

로그인시 401 Unauthorized 오류가 뜹니다

0

104

1

무한 스크롤 중 스크롤 튐 현상

0

192

1

특정 페이지 접근을 막고 싶을 때

0

116

2

createGlobalStyle의 위치와 영향범위

0

102

2

인라인 스타일 리렌더링 관련

0

98

2

vsc 에서 npm init 설치시 오류

0

157

2

nextjs 15버전 사용 가능할까요?

0

166

1

화면 새로고침 문의

0

129

1

RTK에서 draft, state 차이가 있나요?

0

160

2

Next 14 사용해도 될까요?

0

455

1

next, node 버전 / 폴더 구조 질문 드립니다.

0

359

1

url 오류 질문있습니다

0

217

1

ssh xxxxx로 우분투에 들어가려니까 port 22: Connection timed out

0

391

1

sudo certbot --nginx 에러

0

1293

2

Minified React error 콘솔에러 (hydrate)

0

478

1

카카오 공유했을 때 이전에 작성했던 글이 나오는 버그

0

255

1

프론트서버 배포 후 EADDRINUSE에러 발생

0

338

1

npm run build 에러

0

525

1

front 서버 npm run build 중에 발생한 에러들

0

399

1

서버 실행하고 브라우저로 들어갔을때 404에러

0

350

2

css 서버사이드 랜더링이 적용되지 않아서 문의 드립니다.

0

290

1

팔로워 3명씩 불러오고 데이터 합쳐주는걸로 바꾸고 서버요청을 무한으로하고있습니다.

0

249

2

해시태그 검색에서 throttle에 관해 질문있습니다.

0

206

1