월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
useSelector 사용시 undefined 문제
안녕하세요, AppLayout.js 에서 const isLoggedIn = useSelector((state) => state.user.isLoggedIn); 이부분 state의 user가 undefined가 뜹니다... 어느부분을 더 확인해봐야 할까요? TypeError: Cannot read property 'user' of undefined at C:\Users\oht36\react-projects\react-nodebird\prepare\front\.next\server\pages\index.js:152:101 at useSelectorWithStoreAndSubscription (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\react-redux\lib\hooks\useSelector.js:39:30) at useSelector (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\react-redux\lib\hooks\useSelector.js:139:25) at AppLayout (C:\Users\oht36\react-projects\react-nodebird\prepare\front\.next\server\pages\index.js:152:85) at processChild (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3353:14) at resolve (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3270:5) at ReactDOMServerRenderer.render (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3753:22) at ReactDOMServerRenderer.read (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:3690:29) at renderToString (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\react-dom\cjs\react-dom-server.node.development.js:4298:27) at Object.renderPage (C:\Users\oht36\react-projects\react-nodebird\prepare\front\node_modules\next\dist\next-server\server\render.js:50:851)
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요~
안녕하세요 강좌를 마치고 홀로 하드코딩을해 react + nodejs 게시판을 만들고 배포까지 완료하였습니다. 그중에 로그인 문제가 발생하여 질문드립니다. 3일간의 검색결과 passport사용시 secure : false 속성을 사용해야된다는 것을 알아 냈는데요 이 경우 https 두개의 서버를 각각 플론트와 백엔드로해 로그인을 하면 쿠키 전송이 되지 않거나 true로하면 passport가 동작하지 못하는 현상이 발생한것으로 추측합니다. 해결 방법을 찾지 못해 질문드립니다. 배포 사이트 : https://font-techblog.herokuapp.com/ 깃헙주소 : https://github.com/ITwoo/portfolio-react-node-blog (깃헙은 배포중인 사이트와 살짝 다를수 있습니다.) id : 1@1 password 1 로 설정해 두었습니다. 감사합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Unhandled Runtime Error
아이디랑 비밀번호 입력하고 로그인 누르면 다음과 같은 에러가 표시 되네요 ㅜㅜ LoginForm.js UserProfile.js
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
넥스트를 좀더 알고싶어서 찾아봤습니다
넥스트를 알고싶어서 좀더 알아봤는데 파일이 스타일 파일이 생기더라구요 HTML 이랑 CSS가 익숙해서 꾸미려고하는데 꾸미는게 너무 익숙지않아서 그러는데 저가 styles 파일을 만들고 그안에 css를 만들어서 예시) import layoutStyle from '../styles/layout.css' 이렇게해서 컴포넌트안에 Applayout에 스타일을 적용을 따로 하려고하는데 안되더라구요 에러 메세지가 구글 폰트도 사용해서 넣으려고하니까 안되서 방법을 찾는게 어렵습니다 도와주세요 쌤 Global CSS cannot be imported from files other than your Custom <App>. Please move all global CSS imports to pages\_app.js. Or convert the import to Component-Level CSS (CSS Modules). Read more: https://err.sh/next.js/css-global 이렇게 뜨더라구요 어떻게 CSS를 해야할지 잘 모르겠습니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
'onFinish' ignored 에러 관련 질문 있습니다.
우선, 이번 강좌와 다른 내용의 질문을 드려 죄송합니다. 아래와 같이 에러 메시지가 나왔습니다. 코드는 문제가 없어 보이는데, 로그인 버튼을 클릭해도 아무런 반응이 없는 건, 아래와 같은 onFinish문제 때문일까요? antd를 다시 설치해봐도 해결이 되지 않는데 어떻게 해야 하나요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
MenuItem should not leave undefined `key`.
브라우저 콘솔 창에서 다음과 같은 에러가 뜨네요. 아마도 antd에서 제공하는 Menu.Item에 key를 넣으라는 소리 같은데.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
sequelize 랑 sequlize-cl
저 두개는 버전 6이여도 상관없나요 ? 쌤은 버전 5이시길레 여쭈어봅니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
제가 latest next js 버전 11에서 하고 있는데 PropTypes를 찾지 못한다고 나와요
[제로초 강좌 질문 필독 사항입니다]질문에는 여러분에게 도움이 되는 질문과 도움이 되지 않는 질문이 있습니다.도움이 되는 질문을 하는 방법을 알려드립니다.https://www.youtube.com/watch?v=PUKOWrOuC0c0. 숫자 0부터 시작한 이유는 1보다 더 중요한 것이기 때문입니다. 에러가 났을 때 해결을 하는 게 중요한 게 아닙니다. 왜 여러분은 해결을 못 하고 저는 해결을 하는지, 어디서 힌트를 얻은 것이고 어떻게 해결한 건지 그걸 알아가셔야 합니다. 그렇지 못한 질문은 무의미한 질문입니다.1. 에러 메시지를 올리기 전에 반드시 스스로 번역을 해야 합니다. 번역기 요즘 잘 되어 있습니다. 에러 메시지가 에러 해결 단서의 90%를 차지합니다. 한글로 번역만 해도 대부분 풀립니다. 그냥 에러메시지를 올리고(심지어 안 올리는 분도 있습니다. 저는 독심술사가 아닙니다) 해결해달라고 하시면 아무런 도움이 안 됩니다.2. 에러 메시지를 잘라서 올리지 않아야 합니다. 입문자일수록 에러메시지에서 어떤 부분이 가장 중요한 부분인지 모르실 겁니다. 그러니 통째로 올리셔야 합니다.3. 코드도 같이 올려주세요. 다만 코드 전체를 다 올리거나, 깃헙 주소만 띡 던지지는 마세요. 여러분이 "가장" 의심스럽다고 생각하는 코드를 올려주세요. 4. 이 강좌를 바탕으로 여러분이 응용을 해보다가 막히는 부분, 여러 개의 선택지 중에서 조언이 필요한 부분, 제 경험이 궁금한 부분에 대한 질문은 대환영입니다. 다만 여러분의 회사 일은 질문하지 마세요.5. 강좌 하나 끝날 때마다 남의 질문들을 읽어보세요. 여러분이 곧 만나게 될 에러들입니다.6. 위에 적은 내용을 명심하지 않으시면 백날 강좌를 봐도(제 강좌가 아니더라도) 실력이 늘지 않고 그냥 코딩쇼 관람 및 한컴타자연습을 한 셈이 될 겁니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Cannot read property 'Likers' of null
고생이 많으십니다. 위 에러가 났는데 찾기가 너무 어려워서 올렸습니다. 찾는다고 찾았는데 두개의 이메일로 각각 게시글을 10개씩 썼는데도 막상 해당 post 주소로 들어가게 되면 이렇게 존재하지 않는 게시글(404)이라고 표시되고 있습니다. 그리고 콘솔창에 계속해서 Internal Server Error 500 에러 렌더링이 반복되고 있습니다. 코드는 routes/ post.js const express = require('express') const multer = require('multer') const path = require('path') const fs = require('fs') const { Post, Image, Comment, User, Hashtag } = require('../models') const { isLoggedIn } = require('./middlewares') const router = express.Router() try { fs.accessSync('uploads') } catch (error) { console.log('uploads 폴더가 없으므로 생성합니다.') 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) // 제로초15134314.png } }), limits: { fileSize: 20 * 1024 * 1024 } // 20mb }) router.post('/', isLoggedIn, upload.none(), async (req, res) => { // 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.post('/images', isLoggedIn, upload.array('image'), (req, res, next) => { // POST /post/images console.log(req.files) res.json(req.files.map((v) => v.filename)) }) router.post('/:postId/comment', isLoggedIn, async (req, res, next) => { // POST /post/1/comment try { const post = await Post.findOne({ where: { id: req.params.postId } }) if (!post) { return res.status(403).send('존재하지 않는 게시글입니다.') } const comment = await Comment.create({ content: req.body.content, PostId: parseInt(req.params.postId, 10), UserId: req.user.id }) const fullComment = await Comment.findOne({ where: { id: comment.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) => { // GET /post/1 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: User, attributes: ['id', 'nickname'] }, { model: Image }, { model: Comment, include: [{ model: User, attributes: ['id', 'nickname'] }] }, { model: User, as: 'Likers', attributes: ['id'] }, { model: Post, as: 'Retweet', include: [{ model: User, attributes: ['id', 'nickname'] }, { model: Image }] }] }) res.status(200).json(fullPost) } catch (error) { console.error(error) next(error) } }) 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: User, attributes: ['id', 'nickname'] }, { model: Image }, { model: Comment, include: [{ model: User, attributes: ['id', 'nickname'] }] }, { model: User, as: 'Likers', attributes: ['id'] }, { model: Post, as: 'Retweet', include: [{ model: User, attributes: ['id', 'nickname'] }, { model: Image }] }] }) res.status(201).json(retweetWithPrevPost) } catch (error) { console.error(error) next(error) } }) 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', isLoggedIn, async (req, res, next) => { // DELETE /post/10 try { await Post.destroy({ where: { id: req.params.postId, UserId: req.user.id } }) res.json({ PostId: parseInt(req.params.postId, 10) }) } catch (error) { console.error(error) next(error) } }) module.exports = router reducers/ post.js import produce from 'immer' export const initialState = { mainPosts: [], singlePost: null, imagePaths: [], hasMorePosts: true, likePostLoading: false, likePostDone: false, likePostError: null, unlikePostLoading: false, unlikePostDone: false, unlikePostError: null, loadPostLoading: false, loadPostDone: false, loadPostError: null, loadPostsLoading: false, loadPostsDone: false, loadPostsError: null, addPostLoading: false, addPostDone: false, addPostError: null, removePostLoading: false, removePostDone: false, removePostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, uploadImagesLoading: false, uploadImagesDone: false, uploadImagesError: null, retweetLoading: false, retweetDone: false, retweetError: 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 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 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 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 RETWEET_REQUEST = 'RETWEET_REQUEST' export const RETWEET_SUCCESS = 'RETWEET_SUCCESS' export const RETWEET_FAILURE = 'RETWEET_FAILURE' export const REMOVE_IMAGE = 'REMOVE_IMAGE' export const addPost = (data) => ({ type: ADD_POST_REQUEST, data }) export const addComment = (data) => ({ type: ADD_COMMENT_REQUEST, data }) const reducer = (state = initialState, action) => { return 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: { const post = draft.mainPosts.find((v) => v.id === action.data.PostId) post.Likers = post.Likers.filter((v) => v.id !== action.data.UserId) draft.likePostLoading = false draft.likePostDone = true break } case UNLIKE_POST_FAILURE: draft.unlikePostLoading = false draft.unlikePostError = action.error break case LOAD_POST_REQUEST: draft.loadPostLoading = true draft.loadPostDone = false draft.loadPostError = null break case LOAD_POST_SUCCESS: draft.singlePost = action.data draft.loadPostLoading = false draft.loadPostDone = true break case LOAD_POST_FAILURE: draft.loadPostLoading = false draft.loadPostError = action.error break case LOAD_POSTS_REQUEST: draft.loadPostsLoading = true draft.loadPostsDone = false draft.loadPostsError = null break case LOAD_POSTS_SUCCESS: draft.mainPosts = draft.mainPosts.concat(action.data) draft.loadPostsLoading = false draft.loadPostsDone = true draft.hasMorePosts = action.data.length === 10 break case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false draft.loadPostsError = action.error break case ADD_POST_REQUEST: draft.addPostLoading = true draft.addPostDone = false draft.addPostError = null break case ADD_POST_SUCCESS: draft.mainPosts.unshift(action.data) draft.addPostLoading = false draft.addPostDone = true draft.imagePaths = [] 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.PostId) 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(action.data) draft.addCommentLoading = false draft.addCommentDone = true break } case ADD_COMMENT_FAILURE: draft.addCommentLoading = false draft.addCommentError = action.error break default: break } }) } export default reducer sagas/ post.js import axios from 'axios' import { all, delay, put, takeLatest, fork, throttle, call } from "redux-saga/effects"; import shortId from 'shortid'; import { ADD_POST_FAILURE, ADD_POST_SUCCESS, ADD_COMMENT_SUCCESS, ADD_COMMENT_FAILURE, ADD_POST_REQUEST, ADD_COMMENT_REQUEST, REMOVE_POST_FAILURE, REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS, LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE, LIKE_POST_REQUEST, UNLIKE_POST_REQUEST, LIKE_POST_SUCCESS, LIKE_POST_FAILURE, 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_OF_ME } from '../reducers/user'; function addPostAPI(data) { return axios.post('/post', data) } function* addPost(action) { try { const result = yield call(addPostAPI, action.data) const id = shortId.generate() yield put({ type: ADD_POST_SUCCESS, data: result.data }) yield put({ type: ADD_POST_TO_ME, data: result.data.id }) } catch (err) { yield put({ type: ADD_POST_FAILURE, error: err.response.data }) } } function retweetAPI(data) { return axios.post(`/post/${data}/retweet`, data) } function* retweet(action) { try { const result = yield call(retweetAPI, action.data) yield put({ type: RETWEET_SUCCESS, data: result.data }) } catch (err) { yield put({ type: RETWEET_FAILURE, error: 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) { yield put({ type: UPLOAD_IMAGES_FAILURE, error: err.response.data }) } } 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) { yield put({ 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) { yield put({ 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 }) } catch (err) { yield put({ 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 }) } catch (err) { yield put({ type: LOAD_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 }) yield put({ type: REMOVE_POST_OF_ME, data: action.data }) } catch (err) { yield put({ type: REMOVE_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 put({ type: ADD_COMMENT_SUCCESS, data: result.data }) } catch (err) { console.error(error) yield put({ type: ADD_COMMENT_FAILURE, error: err.response.data }) } } function* watchRetweet() { yield takeLatest(RETWEET_REQUEST, retweet) } function* watchUploadImages() { yield takeLatest(UPLOAD_IMAGES_REQUEST, uploadImages) } function* watchLikePost() { yield takeLatest(LIKE_POST_REQUEST, likePost) } function* watchUnlikePost() { yield takeLatest(UNLIKE_POST_REQUEST, unlikePost) } function* watchAddPost() { yield takeLatest(ADD_POST_REQUEST, addPost) } function* watchLoadPosts() { yield throttle(5000, LOAD_POSTS_REQUEST, loadPosts) } function* watchLoadPost() { yield takeLatest(LOAD_POST_REQUEST, loadPost) } function* watchRemovePost() { yield takeLatest(REMOVE_POST_REQUEST, removePost) } function* watchAddComment() { yield takeLatest(ADD_COMMENT_REQUEST, addComment) } export default function* postSaga() { yield all([ fork(watchRetweet), fork(watchUploadImages), fork(watchLikePost), fork(watchUnlikePost), fork(watchAddPost), fork(watchLoadPosts), fork(watchLoadPost), fork(watchRemovePost), fork(watchAddComment) ]) } pages/post/ [id].js import axios from "axios" import { useRouter } from "next/router" import { useSelector } from "react-redux" import { END } from 'redux-saga' import AppLayout from "../../components/AppLayout" import PostCard from "../../components/PostCard" import { LOAD_POST_REQUEST } from "../../reducers/post" import { LOAD_MY_INFO_REQUEST } from "../../reducers/user" import wrapper from "../../store/configureStore" const Post = () => { const router = useRouter() const { id } = router.query const { singlePost } = useSelector((state) => state.post) 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_MY_INFO_REQUEST }) context.store.dispatch({ type: LOAD_POST_REQUEST, data: context.params.id }) context.store.dispatch(END) await context.store.sagaTask.toPromise() }) export default Post 입니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
commenttForm submit 버튼 작동 에러
import { Form, Input, Button } from "antd"; import React, { useCallback, useState } from "react"; import useInput from "../hooks/useInput"; import PropTypes from "prop-types"; import { useSelector } from "react-redux"; const CommentForm = ({ post }) => { const id = useSelector((state) => state.user.me?.id); const [commentText, onChangeCommentText] = useInput(""); const onSubmitComment = useCallback(() => { console.log(post.id, commentText); }, [commentText]); return ( <Form onFinish={onSubmitComment}> <Form.Item style={{ position: "relative", margin: 0 }}> <Input.TextArea value={commentText} onChange={onChangeCommentText} rows={4} /> <Button type="primary" htmlType="submit" style={{ position: "absolute", right: 0 }} > 삐약 </Button> </Form.Item> </Form> ); }; CommentForm.propTypes = { post: PropTypes.object.isRequired, }; export default CommentForm; 문제 button에 style객체 추가 이후 console.log가 찍히지 않는 현상 style={{ position: "absolute", right: 0 }} 여기까지 쳤을 때는 작동을 하였으나 style={{ position: "absolute", right: 0, bottom: -40 }} bottom -40 을 추가하면 작동 안함 bottom 0 은 작동이 잘되는 것을 보니 마이너스 추가시 문제가 방생하는 것 같음 왜 그럴까요?ㅠㅠ
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
http-proxy-middleware 관련해서 질문 있습니다 !
cors 모듈 이용해서 access-control-allow-credentials access-control-allow-origin 을 설정해야하는건 알겠습니다. 만약에 client에 proxy -> http-proxy-middleware를 이용해서 세팅하면 cors는 잡히는데 credentials는 따로 셋팅할 필요가 없나요 ?
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
42초에서 말씀하신 서버사이드 렌더링에 대해 질문
0:38 초가량 부근에서 서버사이드렌더링은 defaultandserver?에서 html을 데이터와 합쳐서 그려준다고 그랬죠? 라고 하신 부분이 이해가 잘되지 않아서요 styledcomponent는 style을 컴포넌트로 꾸며주는 라이브러리고 렌더링할떄 작동하므로 서버에서 렌더링 하고 내려오는 SSR의 동작원리상 서버쪽에서 적용이 안됐다고 보면 되는거 같은데요 (맞나요?) 렌더링이라는거 자체가 브라우저에서 렌더링 엔진이 코드를 읽고 DOM, CSSOM하고 렌더트리로 합친다음에 리플로우 리페인트하는 과정이라고 알고 있습니다. 그럼 무조건 호스트쪽 컴퓨터의 브라우저에서 렌더링하는게 아닌가 싶은데 서버쪽에서 '렌더링'을 미리 해준다는 것이 무슨 말인지 이해가 잘되지 않습니다. 말씀하시는 '렌더링'은 화면을 구성할 코드를 만들어줬다는 말인가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
immer
이버버전이 저는 9버전인데 상관 있을까요? 선생님 버전은 7번전이라고 나와있어서요
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
혹시 antd랑 부트스트렙
혹시 앤트 디자인이랑 부트스트랩 가져와서 쓰는거랑 같이 쓸 수 있나요 ?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Next와 Redux는 사실 연동
공지 글에서 Next와 Redux는 사실 연동이 매끄럽지 않다고 하셨는데 그 부분이 자세히 궁금합니다. 참고하신 자료나 사이트 있으시면 url 또는 설명 부탁드립니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
새로운 eslintrc에서
eslintrc 파일 그리고 package json commentform 이렇게 빨간줄이 생겨요 ㅠ 계속 그러는것같습니다어디가 잘못된지 감을 못잡고있습니다 ㅜ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Cannot read properties of null (reading 'nickname')
항상 고생이 많으십니다. 메인페이지 새로고침시 위와 같은 에러가 나왔는데 어디가 문제인지 못찾고 있습니다. User에 관련된 부분이 문제인것 같은데 리덕스에선 보시다시피 User 관련이 Null로 나오고 있습니다. 어디쪽을 봐야할까요 워크벤치에서도 UserId 부분이 Null로 표시 돼 있습니다. 터미널에서 서버와 프론트 모두 에러로그 표시는 없습니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
module 정의 오류...
module이 정의 되지 않는다고 하는데 제가 혹시 빼먹은 것이 어떤 건지 알 수 있을까요....
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
데이터베이스 관련 질문 2가지
1. 예를 들어 유저가 2분류로 나누어지고 각 분류별로 저장되는 정보가 다를 경우에 user 테이블 (공통되는 정보들, ) pk: id (mail, password ...) fk : userTypeid aInfo 테이블 (a유형 유저만의 정보) pk: id (a유형 유저만의 정보들) fk: userId bInfo테이블(b유형 유저만의 정보) aInfo와 동일 이렇게 설계하면 user와 a, bInfo테이블 사이의 관계는 어떻게 정의하나요? (user는 aInfo와 bInfo중 하나와만 관계를 맺어야함.) aInfo.belongsTo(user); user.?(aInfo) 혹은 그냥 user 테이블에 모든 정보를 다 떄려박고 해당하지 않는 컬럼들의 값은 null로 저장해야하나요?? 더 나은 방법이 있는지도 궁금합니다. 2. sequelize 에서는 id 컬럼(pk)을 자동으로 만든다고 하였는데 실제로 mail 칼럼과 같이 유니크한 칼럼이 있을 경우에도 mail 칼럼을 pk로 두지않고 저 id를 쓰는 것이 더 나은 것인가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
강의와는 상관없지만 글작성해봅니다.
리액트를 이전에 배운적이 없는 저로서는 강의를 이해하기 힘드네요 물론 따라가기도 힘들고 그래서, 리액트 교재를 모두 읽은 후에 현재 강의를 다시 보려고 합니다! 강의에 내용을 이해할 수는 있지만 나중에 혼자 구글링도 하며, 이것저것 다른 프로젝트의 소스코드를 보며 공부한다고 가정했을 때 지금 강의에서 배운 내용을 한 10%도 이용할 수 없을것 같습니다. 좋은강의 강의 무료강의 고맙습니다. 항상 응원합니다.