inflearn logo
강의

Course

Instructor

[Renewal] Creating a NodeBird SNS with React

서버 사이드랜더링 후 오류 관련 질문 드립니다.

433

uphoon

26 asked

0

Server Error

Error: Error serializing .initialState.user.loadMyInfoErr returned from getServerSideProps in "/". Reason: undefined cannot be serialized as JSON. Please use null or omit this value all together.


선생님 해당 애러 한시간 넘게 지금 찾고있는데 일단 번역해보거나 지금 다른분이 올리셨던 질문이 똑같은게 있었는데 도움이 되질않았습니다. 보니 직렬화 문제? 그리고 initialState.user.loadMyInfoErr이쪽이 문제였던것같아 리듀서에 loadMyInfoerr를 그냥 없애보니 화면랜더링은 되는데 로그인이 안되는 게 발생했습니다. 일단 선생님께 질문드리고 난뒤에도 계속 찾아보겠습니다.

//index.js

import React, { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { END } from 'redux-saga'
import AppLayout from '../components/AppLayout'
import PostForm from '../components/PostForm'
import PostCard from '../components/PostCard'
import { LOAD_POSTS_REQUEST } from '../reducers/post'
import { LOAD_MYINFO_REQUEST } from '../reducers/user'
import wrapper from '../store/configureStore'
import axios from 'axios'

const Home = () => {
  const dispatch = useDispatch();
  const { me, logInErr } = useSelector((state) => state.user)
  const { mainPosts, hasMorePosts, loadPostsLoading, retweetErr } = useSelector((state) => state.post)

  useEffect(() => {
    if (retweetErr) {
      alert(retweetErr)
    }
  }, [retweetErr])

  useEffect(() => {
    function onScroll() {
      if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) {
        if (hasMorePosts && !loadPostsLoading) {
          const lastId = mainPosts[mainPosts.length - 1]?.id;
          dispatch({
            type: LOAD_POSTS_REQUEST,
            lastId,
          });
        }
      }
    }
    window.addEventListener('scroll', onScroll);
    return () => {
      window.removeEventListener('scroll', onScroll);
    };
  }, [hasMorePosts, loadPostsLoading, mainPosts])

  return (
    <AppLayout>
      {me && <PostForm />}
      {mainPosts.map((item) => <PostCard key={item.id} post={item} />)}
    </AppLayout>
  )
}

export const getServerSideProps = wrapper.getServerSideProps( async (context) => {
  context.store.dispatch({
    type: LOAD_MYINFO_REQUEST,
  });
  context.store.dispatch({
    type: LOAD_POSTS_REQUEST,
  });
  context.store.dispatch(END);
  await context.store.sagaTask.toPromise();
})
export default Homeimport { delay, all, fork, put, takeLatest, call } from "redux-saga/effects";
import axios from 'axios'
import {
    LOG_IN_REQUEST,
    LOG_IN_SUCCESS,
    LOG_IN_FAILURE,
    LOG_OUT_REQUEST,
    LOG_OUT_SUCCESS,
    LOG_OUT_FAILURE,
    SIGN_UP_REQUEST,
    SIGN_UP_SUCCESS,
    SIGN_UP_FAILURE,
    FOLLOW_REQUEST,
    FOLLOW_SUCCESS,
    FOLLOW_FAILURE,
    UNFOLLOW_REQUEST,
    UNFOLLOW_SUCCESS,
    UNFOLLOW_FAILURE,
    LOAD_USER_REQUEST,
    LOAD_USER_SUCCESSS,
    LOAD_USER_FAILURE,
    CHANGE_NICK_REQUEST,
    CHANGE_NICK_SUCCESS,
    CHANGE_NICK_FAILURE,
    LOAD_FOLLOWER_REQUEST,
    LOAD_FOLLOWER_SUCCESS,
    LOAD_FOLLOWER_FAILURE,
    LOAD_FOLLWING_REQUEST,
    LOAD_FOLLWING_SUCESSS,
    LOAD_FOLLWING_FAILURE,
    REMOVE_FOLLOWER_REQUEST,
    REMOVE_FOLLOWER_SUCCESS,
    REMOVE_FOLLOWER_FAILURE,
    LOAD_MYINFO_REQUEST,
    LOAD_MYINFO_SUCCESSS,
    LOAD_MYINFO_FAILURE
} from '../reducers/user'

function loadMyInfoAPI() {
    return axios.get('/user')
}

function* loadMyInfo() {
    try {
        const result = yield call(loadMyInfoAPI)
        yield put({
            type: LOAD_MYINFO_SUCCESSS,
            data: result.data,
        });
    } catch (err) {
        console.log(err);
        yield put({
            type: LOAD_MYINFO_FAILURE,
            error: err.response.data
        });
    }
}


function getUserAPI(data) {
    return axios.get(`/user/${data}`)
}

function* getUser(action) {
    try {
        const result = yield call(getUserAPI, action.data)
        yield put({
            type: LOAD_USER_SUCCESSS,
            data: result.data,
        });
    } catch (err) {
        yield put({
            type: LOAD_USER_FAILURE,
            error: err.response.data
        });
    }
}

function getFollwerAPI(data) {
    return axios.get('/user/follower', data)
}

function* getFollwer(action) {
    try {
        const result = yield call(getFollwerAPI, action.data)
        yield put({
            type: LOAD_FOLLOWER_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        yield put({
            type: LOAD_FOLLOWER_FAILURE,
            error: err.response.data
        });
    }
}

function getFollowingAPI(data) {
    return axios.get('/user/following', data)
}

function* getFollowing(action) {
    try {
        const result = yield call(getFollowingAPI, action.data)
        yield put({
            type: LOAD_FOLLWING_SUCESSS,
            data: result.data,
        });
    } catch (err) {
        yield put({
            type: LOAD_FOLLWING_FAILURE,
            error: err.response.data
        });
    }
}


function logInAPI(data) {
    return axios.post('/user/login', data)
}

function* logIn(action) {
    try {
        const result = yield call(logInAPI, action.data)
        yield put({
            type: LOG_IN_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        yield put({
            type: LOG_IN_FAILURE,
            error: err.response.data
        });
    }
}

function logOutAPI() {
    return axios.post('/user/logout');
}

function* logOut() {
    try {
        yield call(logOutAPI);
        yield put({
            type: LOG_OUT_SUCCESS,
        });
    } catch (err) {
        console.error(err);
        yield put({
            type: LOG_OUT_FAILURE,
            error: err.response.data,
        });
    }
}



function signUpAPI(data) {
    return axios.post('/user', data)
}

function* signUp(action) {
    try {
        const result = yield call(signUpAPI, action.data);
        console.log(result);
        yield put({
            type: SIGN_UP_SUCCESS,
        });
    } catch (err) {
        console.error(err);
        yield put({
            type: SIGN_UP_FAILURE,
            error: err.response.data,
        });
    }
}
function followAPI(data) {
    return axios.patch(`/user/${data}/follow`)
}

function* follow(action) {
    try {
        const result = yield call(followAPI , action.data)
        yield put({
            type: FOLLOW_SUCCESS,
            data: result.data
        });
    } catch (err) {
        yield put({
            type: FOLLOW_FAILURE,
            error: err.response.data
        });
    }
}

function unFollowAPI(data) {
    return axios.delete(`/user/${data}/follow`)
}

function* unFollow(action) {
    try {
        const result = yield call(unFollowAPI, action.data)
        yield console.log(result)
        yield put({
            type: UNFOLLOW_SUCCESS,
            data: result.data
        });
    } catch (err) {
        yield put({
            type: UNFOLLOW_FAILURE,
            error: err.response.data
        });
    }
}

function chanegeNickAPI(data) {
    return axios.patch('/user/nickname', { nickname : data })
}

function* chanegeNick(action) {
    try {
        const result = yield call(chanegeNickAPI, action.data)
        yield console.log(result)
        yield put({
            type: CHANGE_NICK_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        yield put({
            type: CHANGE_NICK_FAILURE,
            error: err.response.data
        });
    }
}

function removeFollowerAPI(data) {
    return axios.delete(`/user/${data}/following`)
}

function* removeFollower(action) {
    try {
        const result = yield call(removeFollowerAPI, action.data)
        console.log(result)
        yield console.log(result)
        yield put({
            type: REMOVE_FOLLOWER_SUCCESS,
            data: result.data,
        });
    } catch (err) {
        yield put({
            type: REMOVE_FOLLOWER_FAILURE,
            error: err.response.data
        });
    }
}

function* watchLogIn() {
    yield takeLatest(LOG_IN_REQUEST, logIn)
}

function* watchLogOut() {
    yield takeLatest(LOG_OUT_REQUEST, logOut)
}

function* watchSignUp() {
    yield takeLatest(SIGN_UP_REQUEST, signUp)
}

function* watchFollow() {
    yield takeLatest(FOLLOW_REQUEST, follow)
}

function* watchUnFollow() {
    yield takeLatest(UNFOLLOW_REQUEST, unFollow)
}

function* watchGetUser() {
    yield takeLatest(LOAD_USER_REQUEST, getUser)
}

function* watchGetFollow() {
    yield takeLatest(LOAD_FOLLOWER_REQUEST, getFollwer)
}

function* watchGetFollowing() {
    yield takeLatest(LOAD_FOLLWING_REQUEST, getFollowing)
}

function* watchChanegeNick() {
    yield takeLatest(CHANGE_NICK_REQUEST, chanegeNick)
}

function* watchRemoveFollower(){
    yield takeLatest(REMOVE_FOLLOWER_REQUEST, removeFollower)
}

function* watchLoadMyInfo(){
    yield takeLatest(LOAD_MYINFO_REQUEST, loadMyInfo)
}

export default function* userSaga() {
    yield all([
        fork(watchLogIn),
        fork(watchLoadMyInfo),
        fork(watchLogOut),
        fork(watchLogOut),
        fork(watchSignUp),
        fork(watchFollow),
        fork(watchUnFollow),
        fork(watchRemoveFollower),
        fork(watchGetUser),
        fork(watchChanegeNick),
        fork(watchGetFollow),
        fork(watchGetFollowing),
    ])
}
const express = require('express')
const bcrypt = require('bcrypt')
const { User, Post, Image, Comment } = require('../models')
const { isLoggedIn, isNotLoggedIn } = require('./middlewares')
const passport = require('passport');
const db = require('../models');

const router = express.Router();

router.get('/', async (req, res, next) => { // GET /user
    try {
        if (req.user) {
            const fullUserWithoutPassword = await User.findOne({
                where: { id: req.user.id },
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post,
                    attributes: ['id'],
                }, {
                    model: User,
                    as: 'Followings',
                    attributes: ['id'],
                }, {
                    model: User,
                    as: 'Followers',
                    attributes: ['id'],
                }]
            })
            res.status(200).json(fullUserWithoutPassword);
        } else {
            res.status(200).json(null);
        }
    } catch (error) {
        console.error(error);
        next(error);
    }
});

router.post('/login', isNotLoggedIn, (req, res, next) => {
    passport.authenticate('local', (err, user, info) => {
        if (err) {
            console.error(err)
            return next(err)
        }
        if (info) {
            return res.status(401).send(info.reason);
        }
        return req.login(user, async (loginErr) => {
            if (loginErr) {
                console.error(loginErr)
                return next(loginErr)
            }
            const fullUserWithoutPassword = await User.findOne({
                where: { id: user.id },
                attributes: {
                    exclude: ['password']
                },
                include: [{
                    model: Post,
                }, {
                    model: User,
                    as: 'Followings',
                }, {
                    model: User,
                    as: 'Followers',
                }]
            })
            return res.status(200).json(fullUserWithoutPassword)
        })
    })(req, res, next)
})

router.post('/logout', isLoggedIn, (req, res) => {
    req.logout();
    req.session.destroy();
    res.send('OK')
})

router.post('/', isNotLoggedIn, async (req, res, next) => {
    try {
        const exUser = await User.findOne({
            where: {
                email: req.body.email,
            }
        });
        if (exUser) {
            return res.status(403).send('이미 사용 중인 아이디입니다.');
        }
        const hashedPassword = await bcrypt.hash(req.body.password, 12);
        await User.create({
            email: req.body.email,
            nickname: req.body.nick,
            password: hashedPassword,
        });
        res.status(201).send('ok');
    } catch (error) {
        console.error(error);
        next(error); // status 500
    }
})

router.patch('/nickname', isLoggedIn, async (req, res, next) => {
    try {
        console.log(req.body)
        await User.update({
            nickname: req.body.nickname
        }, {
            where: { id: req.user.id }
        })
        res.status(200).json({ nickname: req.body.nickname })
    } catch (error) {
        console.log(error)
        next(error)
    }
})

router.patch('/:userId/follow', isLoggedIn, async (req, res, next) => { // 유저 팔로우
    try {
        const user = await User.findOne({ where: { id: req.params.userId } }) // 게시글 작성자 1번
        if (!user) {
            res.status(403).send('없는 유저입니다.')
        }
        await user.addFollowers(req.user.id); // 게시글 작성자를 팔로우한다 / 내 계정 정보를 넘김 // 팔로워 아이디 2번
        res.status(200).json({ userId: parseInt(req.params.userId, 10) })
    } catch (error) {
        console.log(error)
        next(error)
    }
})

router.delete('/:userId/follow', isLoggedIn, async (req, res, next) => { // 유저 팔로우 취소
    try {
        const user = await User.findOne({ where: { id: req.params.userId } })
        if (!user) {
            res.status(403).send('없는 유저입니다.')
        }
        await user.removeFollowers(req.user.id);
        res.status(200).json({ userId: parseInt(req.params.userId, 10) })
    } catch (error) {
        console.log(error)
        next(error)
    }
})

router.delete('/:userId/following', isLoggedIn, async (req, res, next) => { // 유저 팔로우 취소
    try {
        const user = await User.findOne({ where: { id: req.params.userId } })
        if (!user) {
            res.status(403).send('없는 유저입니다.')
        }
        await user.removeFollowings(req.user.id);
        res.status(200).json({ userId: parseInt(req.params.userId, 10) })
    } catch (error) {
        console.log(error)
        next(error)
    }
})

router.get('/follower', isLoggedIn, async (req, res, next) => {
    try {
        const user = await User.findOne({ where: { id: req.user.id } })
        if (!user) {
            res.status(403).send('없는 유저입니다.')
        }
        const followers = await user.getFollowers();
        res.status(200).json(followers)
    } catch (error) {
        console.log(error)
        next(error)
    }
})

router.get('/following', isLoggedIn, async (req, res, next) => {
    try {
        const user = await User.findOne({ where: { id: req.user.id } })
        if (!user) {
            res.status(403).send('없는 유저입니다.')
        }
        const followings = await user.getFollowings();
        res.status(200).json(followings)
    } catch (error) {
        console.log(error)
        next(error)
    }
})

module.exports = router;

react express nodejs redux Next.js

Answer 1

2

uphoon

아 선생님 해결했습니다! reudcer에서 swich문에 case끝날때 break 안해줘서 오류 발생했던것 같습니다 . 감사합니다!!

넥스트 버젼 질문

0

75

2

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

0

88

1

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

0

172

1

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

0

103

2

createGlobalStyle의 위치와 영향범위

0

94

2

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

0

90

2

vsc 에서 npm init 설치시 오류

0

146

2

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

0

158

1

화면 새로고침 문의

0

120

1

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

0

152

2

Next 14 사용해도 될까요?

0

452

1

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

0

348

1

url 오류 질문있습니다

0

211

1

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

0

372

1

sudo certbot --nginx 에러

0

1271

2

Minified React error 콘솔에러 (hydrate)

0

469

1

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

0

246

1

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

0

325

1

npm run build 에러

0

517

1

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

0

381

1

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

0

336

2

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

0

284

1

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

0

235

2

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

0

200

1