인프런 커뮤니티 질문&답변
다이나믹 라우터 애러 도와주시면 감사하겠습니다.
해결된 질문
작성
·
286
0
1 of 1 unhandled error
Server Error
TypeError: Cannot read properties of null (reading 'Likers')
This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source
components\PostCard.js (18:23) @ PostCard
16 | const dispatch = useDispatch();
17 | const id = useSelector((state) => state.user.me?.id)
> 18 | const liked = post.Likers.find((item) => item.id === id)
| ^
19 |
20 | const onToggleComment = useCallback(() => {
21 | setCommentFormOpened((prev) => !prev)Call Stack
Function.getInitialProps
pages\_document.tsx (91:33)
Show collapsed frames
선생님도 같은 오류 나왔었던거 있어서 그대로 오류 해결하는데도 이 오류가 안없어지네요...
//서버 메세지
GET /posts?lastId=0 200 12.677 ms - 2197
GET /post/60 200 15.146 ms - 216서버에서는 요청 잘 받는것 같은데 Likers가 계속 비어있는거 같습니다...
메인 화면에서는 작동이제 다 잘되는데 다이나믹 라우트 post/ 로 넘어가기만 하면 오류 발생하네요... 하루종일 매달리고 있는데... 선생님 깃허브 코드랑 강의 코드가 달라서 조금 헷갈려서요... 깃허브 주소 남기겠습니다 한번 봐주시면 감사하겠습니다. ㅠㅠ
https://github.com/wihyanghoon/react-nodebird
// [id].js
import React from 'react';
import { useSelector } from 'react-redux';
import { useRouter } from 'next/router';
import { END } from 'redux-saga';
import axios from 'axios';
import { LOAD_POST_REQUEST } from '../../reducers/post';
import wrapper from '../../store/configureStore';
import PostCard from '../../components/PostCard';
import AppLayout from '../../components/AppLayout';
import { LOAD_MYINFO_REQUEST } from '../../reducers/user';
const Post = () => {
const { singlePost } = useSelector((state) => state.post);
const router = useRouter();
const { id } = router.query;
// if (router.isFallback) {
// return <div>Loading...</div>
// }
return (
<AppLayout>
<PostCard post={ singlePost } />
</AppLayout>
);
};
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
const cookie = context.req ? context.req.headers.cookie : '';
console.log(context);
axios.defaults.headers.Cookie = '';
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
context.store.dispatch({
type: LOAD_MYINFO_REQUEST,
});
context.store.dispatch({
type: LOAD_POST_REQUEST,
data: context.params.id,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
});
export default Post;import { delay, all, fork, takeLatest, put, throttle, call } from "redux-saga/effects";
import {
ADD_POST_REQUEST, ADD_POST_SUCCESS, ADD_POST_FAILURE,
REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS, REMOVE_POST_FAILURE,
ADD_COMMENT_REQUEST, ADD_COMMENT_SUCCESS, ADD_COMMENT_FAILURE,
LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE,
LIKE_POST_REQUEST, LIKE_POST_SUCCESS, LIKE_POST_FAILURE,
UNLIKE_POST_REQUEST, UNLIKE_POST_SUCCESS, UNLIKE_POST_FAILURE,
UPLOAD_IMAGES_REQUEST, UPLOAD_IMAGES_SUCCESS, UPLOAD_IMAGES_FAILURE,
RETWEET_REQUEST,RETWEET_SUCCESS,RETWEET_FAILURE,
LOAD_POST_REQUEST, LOAD_POST_SUCCESS, LOAD_POST_FAILURE
} from '../reducers/post'
import { ADD_POST_TO_ME, REMOVE_POST_TO_ME } from "../reducers/user";
import axios from "axios";
import shortId from 'shortid';
function addPostAPI(data) {
return axios.post('/post', data);
}
function* addPost(action) {
try {
const result = yield call(addPostAPI, action.data);
yield console.log(result)
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({
type: ADD_POST_FAILURE,
error: err.response.data,
});
}
}
function removePostAPI(data) {
return axios.delete(`/post/${data}`)
}
function* removePost(action) {
console.log(action.data)
try {
const result = yield call(removePostAPI, action.data)
yield console.log(typeof result.data.PostId)
yield put({
type: REMOVE_POST_SUCCESS,
data: result.data
})
yield put({
type: REMOVE_POST_TO_ME,
data: result.data.PostId
})
} catch (err) {
yield put({
type: REMOVE_POST_FAILURE,
error: err.response.data
});
}
}
function addCommentAPI(data) {
return axios.post(`/post/${data.postId}/comment`, data)
}
function* addComment(action) {
try {
const result = yield call(addCommentAPI, action.data)
yield put({
type: ADD_COMMENT_SUCCESS,
data: result.data
});
} catch (err) {
yield put({
type: ADD_COMMENT_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,
});
} catch (err) {
console.error(err);
yield put({
type: LOAD_POSTS_FAILURE,
data: 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,
});
} catch (err) {
console.error(err);
yield put({
type: LOAD_POST_FAILURE,
error: err.response.data,
});
}
}
function likePostAPI(data) {
return axios.patch(`/post/${data}/like `, data);
}
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({
type: LIKE_POST_FAILURE,
data: 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({
type: UNLIKE_POST_FAILURE,
data: err.response.data,
});
}
}
function upLoadImagesAPI(data) {
return axios.post('/post/images', data);
}
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({
type: UPLOAD_IMAGES_FAILURE,
data: err.response.data,
});
}
}
function retweetApi(data){
return axios.post(`/post/${data}/retweet`);
}
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({
type: RETWEET_FAILURE,
err: err.response.data,
});
}
}
function* watchLoadPosts() {
yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts);
}
function* watchLoadPost() {
yield takeLatest(LOAD_POST_REQUEST, loadPost);
}
function* watchAddPost() {
yield takeLatest(ADD_POST_REQUEST, addPost)
}
function* watchRemovePost() {
yield takeLatest(REMOVE_POST_REQUEST, removePost)
}
function* watchCommentPost() {
yield takeLatest(ADD_COMMENT_REQUEST, addComment)
}
function* watchLikePost() {
yield takeLatest(LIKE_POST_REQUEST, likePost)
}
function* watchUnLiketPost() {
yield takeLatest(UNLIKE_POST_REQUEST, UnLikePost)
}
function* watchUpLoadImages() {
yield takeLatest(UPLOAD_IMAGES_REQUEST, upLoadImages)
}
function* watchRetweet() {
yield takeLatest(RETWEET_REQUEST, retweet)
}
export default function* postSaga() {
yield all([
fork(watchAddPost),
fork(watchCommentPost),
fork(watchRemovePost),
fork(watchLoadPosts),
fork(watchLoadPost),
fork(watchLikePost),
fork(watchUnLiketPost),
fork(watchUpLoadImages),
fork(watchRetweet),
]);
}import shortId from 'shortid';
import produce from 'immer';
import faker from 'faker';
import { LIKE_FAILURE, LIKE_REQUEST, LIKE_SUCCESS } from './user';
export const initialState = {
mainPosts: [],
imagePath: [],
hasMorePosts: true,
loadPostsLoading: false,
loadPostsDone: false,
loadPostsError: null,
loadPostLoading: false,
loadPostDone: false,
loadPostError: null,
likeLoading: false,
likeDone: false,
likeError: null,
unLikeLoading: false,
unLikeDone: false,
unLikeError: null,
addPostLoadding: false,
addPostDone: false,
addPostErr: null,
removePostLoadding: false,
removePostDone: false,
removePostErr: null,
addCommentLoadding: false,
addCommentDone: false,
addCommentErr: null,
upLoadImagesLoadding: false,
upLoadImagesDone: false,
upLoadImagesErr: null,
retweetLoadding: false,
retweetDone: false,
retweetErr: null,
singlePost: null,
}
// export const getDemmuyPost = (number) => Array(number).fill().map(() => ({
// id: shortId.generate(),
// User: {
// id: shortId.generate(),
// nickname: faker.name.findName(),
// },
// content: faker.lorem.paragraph(),
// Images: [{
// src: faker.image.image(),
// }],
// Comments: [{
// User: {
// id: shortId.generate(),
// nickname: faker.name.findName(),
// },
// content: faker.lorem.sentence(),
// }],
// }))
export const REMOVE_IMAGES_SUCSESS = 'REMOVE_IMAGES_SUCSESS';
export const LOAD_POST_REQUEST = 'LOAD_POSTS_REQUEST';
export const LOAD_POST_SUCCESS = 'LOAD_POSTS_SUCCESS';
export const LOAD_POST_FAILURE = 'LOAD_POSTS_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 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 LIKE_POST_REQUEST = 'LIKE_POST_REQUEST';
export const LIKE_POST_SUCCESS = 'LIKE_POST_SUCCESS';
export const LIKE_POST_FAILURE = 'LIKE_POST_FAILURE';
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 UNLIKE_POST_REQUEST = 'UNLIKE_POST_REQUEST';
export const UNLIKE_POST_SUCCESS = 'UNLIKE_POST_SUCCESS';
export const UNLIKE_POST_FAILURE = 'UNLIKE_POST_FAILURE';
export const RETWEET_REQUEST = 'RETWEET_REQUEST'
export const RETWEET_SUCCESS = 'RETWEET_SUCCESS'
export const RETWEET_FAILURE = 'RETWEET_FAILURE'
export const addPostAction = (data) => {
return {
type: ADD_POST_REQUEST,
data
}
}
export const addCommentAction = (data) => {
return {
type: ADD_COMMENT_REQUEST,
data
}
}
const reducer = (state = initialState, action) => {
return produce(state, (draft) => {
switch (action.type) {
case RETWEET_REQUEST:
draft.retweetLoadding = true
draft.retweetDone = false
draft.retweetErr = null
break;
case RETWEET_SUCCESS:
draft.retweetLoadding = false
draft.retweetDone = true
draft.mainPosts.unshift(action.data)
break;
case RETWEET_FAILURE:
draft.retweetLoadding = false
draft.retweetErr = action.err
break;
case REMOVE_IMAGES_SUCSESS:
console.log(action.data)
draft.imagePath = draft.imagePath.filter((item, index) => index !== action.data)
break;
case LOAD_POSTS_REQUEST:
draft.loadPostsLoading = true;
draft.loadPostsDone = false;
draft.loadPostsError = null;
break;
case LOAD_POSTS_SUCCESS:
draft.loadPostsLoading = false;
draft.loadPostsDone = true;
draft.mainPosts = draft.mainPosts.concat(action.data);
draft.hasMorePosts = draft.mainPosts.length === 10;
break;
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.addPostLoadding = true
draft.addPostDone = false
draft.addPostErr = null
break;
case ADD_POST_SUCCESS:
draft.addPostLoadding = false
draft.addPostDone = true
draft.mainPosts.unshift(action.data)
draft.imagePath = []
break;
case ADD_POST_FAILURE:
draft.addPostLoadding = false
draft.addPostErr = action.err
break;
case REMOVE_POST_REQUEST:
draft.removePostLoadding = true
draft.removePostDone = false
draft.removePostErr = null
break;
case REMOVE_POST_SUCCESS:
draft.removePostLoadding = false
draft.removePostDone = true
draft.mainPosts = state.mainPosts.filter((item) => item.id !== action.data.PostId)
break;
case REMOVE_POST_FAILURE:
draft.removePostLoadding = false
draft.removePostErr = action.err
break;
case ADD_COMMENT_REQUEST:
draft.addCommentLoadding = true
draft.addCommentDone = false
draft.addCommentErr = null
break;
case ADD_COMMENT_SUCCESS:
const post = draft.mainPosts.find((item) => { return item.id === action.data.PostId })
post.Comments.unshift(action.data)
draft.addCommentLoadding = false
draft.addCommentDone = true
break;
case ADD_COMMENT_FAILURE:
draft.addCommentLoadding = false
draft.addCommentErr = action.error
break;
case LIKE_POST_REQUEST:
draft.likeLoading = true
draft.likeDone = false
draft.likeError = null
break;
case LIKE_POST_SUCCESS: {
draft.likeLoading = false
draft.likeDone = true
const post = draft.mainPosts.find((item) => item.id === action.data.PostId)
post.Likers.push({ id: action.data.UserId })
break;
}
case LIKE_POST_FAILURE:
draft.unLikeLoading = false
draft.unLikeError = true
break;
case UNLIKE_POST_REQUEST:
draft.unLikeLoading = true
draft.unLikeDone = false
draft.unLikeError = null
break;
case UNLIKE_POST_SUCCESS: {
draft.unLikeLoading = false
draft.unLikeDone = true
const post = draft.mainPosts.find((v) => v.id === action.data.PostId);
post.Likers = post.Likers.filter((v) => v.id !== action.data.UserId);
break;
}
case UNLIKE_POST_FAILURE:
draft.unLikeLoading = false
draft.unLikeDone = true
break;
case UPLOAD_IMAGES_REQUEST:
draft.upLoadImagesLoadding = true
draft.upLoadImagesDone = false
draft.upLoadImagesErr = null
break;
case UPLOAD_IMAGES_SUCCESS:
draft.upLoadImagesLoadding = true
draft.upLoadImagesDone = false
draft.imagePath = action.data
break;
case UPLOAD_IMAGES_FAILURE:
draft.upLoadImagesLoadding = false
draft.upLoadImagesErr = action.error
break;
default:
return state
}
})
}
export default reducerconst express = require('express')
const multer = require('multer')
const path = require('path')
const fs = require('fs')
const { Post, Comment, Image, User, Hashtag } = require('../models')
const { isLoggedIn } = require('./middlewares')
const user = require('../models/user')
const router = express.Router();
try {
fs.accessSync('uploads')
} catch (error) {
console.log('폴더가 없으므로 생성합니다.')
fs.mkdirSync('uploads')
}
const upload = multer({
storage: multer.diskStorage({
destination(req, file, done) {
done(null, 'uploads');
},
filename(req, file, done) { // 제로초.png
const ext = path.extname(file.originalname); // 확장자 추출(.png)
const basename = path.basename(file.originalname, ext); // 제로초
done(null, basename + '_' + new Date().getTime() + ext); // 제로초15184712891.png
},
}),
limits: { fileSize: 20 * 1024 * 1024 }, // 20MB
});
router.post('/', isLoggedIn, upload.none(), async (req, res, next) => { // POST /post
try {
const hashtags = req.body.content.match(/#[^\s#]+/g);
const post = await Post.create({
content: req.body.content,
UserId: req.user.id,
});
if (hashtags) {
const result = await Promise.all(hashtags.map((tag) => Hashtag.findOrCreate({
where: { name: tag.slice(1).toLowerCase() },
}))); // [[노드, true], [리액트, true]]
await post.addHashtags(result.map((v) => v[0]));
}
if (req.body.image) {
if (Array.isArray(req.body.image)) { // 이미지를 여러 개 올리면 image: [제로초.png, 부기초.png]
const images = await Promise.all(req.body.image.map((image) => Image.create({ src: image })));
await post.addImages(images);
} else { // 이미지를 하나만 올리면 image: 제로초.png
const image = await Image.create({ src: req.body.image });
await post.addImages(image);
}
}
const fullPost = await Post.findOne({
where: { id: post.id },
include: [{
model: Image,
}, {
model: Comment,
include: [{
model: User,
attributes: ['id', 'nickname']
}]
}, {
model: User,
attributes: ['id', 'nickname']
}, {
model: User,
as: 'Likers',
attributes: ['id'],
}]
})
res.status(201).json(fullPost)
} catch (error) {
console.error(error)
next(error)
}
})
router.delete('/:PostId', isLoggedIn, async (req, res, next) => {
try {
await Post.destroy({
where: {
id: req.params.PostId,
UserId: req.user.id
},
})
res.status(201).json({ PostId: parseInt(req.params.PostId, 10) })
} catch (error) {
console.error(error)
next(error)
}
})
router.post('/:postId/comment', isLoggedIn, async (req, res) => {
try {
const post = await Post.findOne({
where: { id: req.params.postId }
})
if (!post) {
return res.status(403).send('존재하지 않는 게시글 입니다.')
}
const commnet = await Comment.create({
content: req.body.content,
PostId: Number(req.params.postId),
UserId: req.user.id,
})
const fullComment = await Comment.findOne({
where: { id: commnet.id },
include: [{
model: User,
attributes: ['id', 'nickname']
}]
})
res.status(201).json(fullComment)
} catch (error) {
console.error(error)
next(error)
}
})
router.get('/:postId', async (req, res, next) => {
try {
const post = await Post.findOne({
where: { id: req.params.postId },
})
if (!post) {
return res.status(404).send('존재하지않는 게시글입니다..')
}
const fullPost = await Post.findOne({
where: { id: post.id },
include: [{
model: Post,
as: 'Retweet',
include: [{
model: User,
attributes: ['id', 'nickname']
}, {
model: Image,
}]
}, {
model: User,
attributes: ['id', 'nickname']
}, {
model: User,
as: 'Likers',
attributes: ['id', 'nickname']
}, {
model: Image,
}, {
model: Comment,
include: [{
model: User,
attributes: ['id', 'nickname']
}]
}]
})
res.status(200).json(fullPost)
} catch (err) {
console.error(err)
next(err)
}
});
router.patch('/:postId/like', isLoggedIn, async (req, res, next) => { // PATCH /post/1/like
try {
const post = await Post.findOne({ where: { id: req.params.postId } });
if (!post) {
return res.status(403).send('게시글이 존재하지 않습니다.');
}
await post.addLikers(req.user.id);
res.json({ PostId: post.id, UserId: req.user.id });
} catch (error) {
console.error(error);
next(error);
}
});
router.delete('/:postId/like', isLoggedIn, async (req, res, next) => { // DELETE /post/1/like
try {
const post = await Post.findOne({ where: { id: req.params.postId } });
if (!post) {
return res.status(403).send('게시글이 존재하지 않습니다.');
}
await post.removeLikers(req.user.id);
res.json({ PostId: post.id, UserId: req.user.id });
} catch (error) {
console.error(error);
next(error);
}
});
router.delete('/:postId/like', (req, res, next) => {
})
router.post('/images', isLoggedIn, upload.array('image'), (req, res, next) => {
res.json(req.files.map((item) => item.filename))
})
router.post('/:postId/retweet', isLoggedIn, async (req, res, next) => { // POST /post/1/retweet
try {
const post = await Post.findOne({
where: { id: req.params.postId },
include: [{
model: Post,
as: 'Retweet',
}],
});
if (!post) {
return res.status(403).send('존재하지 않는 게시글입니다.');
}
if (req.user.id === post.UserId || (post.Retweet && post.Retweet.UserId === req.user.id)) {
return res.status(403).send('자신의 글은 리트윗할 수 없습니다.');
}
const retweetTargetId = post.RetweetId || post.id;
const exPost = await Post.findOne({
where: {
UserId: req.user.id,
RetweetId: retweetTargetId,
},
});
if (exPost) {
return res.status(403).send('이미 리트윗했습니다.');
}
const retweet = await Post.create({
UserId: req.user.id,
RetweetId: retweetTargetId,
content: 'retweet',
});
const retweetWithPrevPost = await Post.findOne({
where: { id: retweet.id },
include: [{
model: Post,
as: 'Retweet',
include: [{
model: User,
attributes: ['id', 'nickname'],
}, {
model: Image,
}]
}, {
model: User,
attributes: ['id', 'nickname'],
}, {
model: User, // 좋아요 누른 사람
as: 'Likers',
attributes: ['id'],
}, {
model: Image,
}, {
model: Comment,
include: [{
model: User,
attributes: ['id', 'nickname'],
}],
}],
})
res.status(201).json(retweetWithPrevPost);
} catch (error) {
console.error(error);
next(error);
}
});
module.exports = router;답변 1
0
에러메시지를 잘 읽으셔야 합니다. Likers가 비어있는게 아닙니다. post가 null인 겁니다. post가 null이라는건 서버사이드렌더링이 안 된겁니다. 리덕스에서 hydrate 액션 시 post가 null인지 확인해보시면 되겠죠?
선생님 제가 틀린게 있나요? loadPost saga 및 디스패치 계속 확인해도 ....
export const getServerSideProps = wrapper.getServerSideProps(async (context) => {
const cookie = context.req ? context.req.headers.cookie : '';
console.log(context);
axios.defaults.headers.Cookie = '';
if (context.req && cookie) {
axios.defaults.headers.Cookie = cookie;
}
context.store.dispatch({
type: LOAD_MYINFO_REQUEST,
});
context.store.dispatch({
type: LOAD_POST_REQUEST,
data: context.params.id,
});
context.store.dispatch(END);
await context.store.sagaTask.toPromise();
return { props: {} };
});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,
});
} catch (err) {
console.error(err);
yield put({
type: LOAD_POST_FAILURE,
error: err.response.data,
});
}
}function* watchLoadPost() {
yield takeLatest(LOAD_POST_REQUEST, loadPost);
}export default function* postSaga() {
yield all([
fork(watchAddPost),
fork(watchCommentPost),
fork(watchRemovePost),
fork(watchLoadPosts),
fork(watchLoadPost),
fork(watchLikePost),
fork(watchUnLiketPost),
fork(watchUpLoadImages),
fork(watchRetweet),
]);
}//서버 메세지
GET /posts?lastId=0 200 12.677 ms - 2197
GET /post/60 200 15.146 ms - 216서버쪽에서 응답은 잘받는것같습니다 ㅠㅠ 그냥 밀고 다시 해야할까요…
음.. 프론트서버쪽에서 콘솔에 리덕스 스테이트들이 ssr되는 과정이 나올텐데요. 1 2 3 4모두 singlePost가 null인가요? *loadPost에서도 console.log(result)도 해보세요.
선생님 오류 찾았습니다...
export const LOAD_POST_REQUEST = 'LOAD_POSTS_REQUEST';
export const LOAD_POST_SUCCESS = 'LOAD_POSTS_SUCCESS';
export const LOAD_POST_FAILURE = 'LOAD_POSTS_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';action 함수가 함수이름만 다르고... string값은 같아서 오류났던거였습니다..... 드디어 해결했습니다 ㅠㅠ





init 일때
hydrate 일때 state.post안에 값들 다들어오는데요? 제가 질문을 잘못이해 했을까요?
네 선생님 hydrate 확인하고 리덕스 본건데
index.js 즉 localhost:3000 에서는 잘나오고 이제
여기가 다이나믹 라우터 쪽 로직인데 제가 밑에 ServerSideProps를 잘 못 만든 걸까요?