inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

묻고 답해요

172만명의 커뮤니티!! 함께 토론해봐요.

질문있습니다.

해결됨

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

우선적으로 좋은 강의 주셔서 감사하다는 말씀 먼저 드리고싶구요 ㅎ 이전에는 next 를 사용하지 않고 react 를 사용하는 강의라던지 블로그라던지 등등을 봤었는데요 . 지금은 제로초 님 강의에서 next 를 들으면서 드는생각이 코드적인 부분에서 크게 차이가 없는데 , 물론 앞서 설명하신 부분에 대해서 이해는 했지만 코드적인 부분에서 무엇이 다른지 잘 이해가 안갑니다. import React from "react"; 를 적지 않는거,..? 라고 우선 기억을 하고있는데요 . 무슨 차이가 있을까요

  • redux
  • express
  • react
  • nodejs
  • Next.js
SJ 댓글 4 좋아요 1 조회수 284

256이 궁금합니다.

미해결

C 프로그래밍 - 입문부터 게임 개발까지

/*char str[256]; scanf_s("%s", str, sizeof(str)); printf("%s\n", str);*/ 이부분에서 왜 하필 크기를 정해줄 때 256이라는 숫자를 사용하는거에요?? 그냥 아무 숫자나 큰 걸로 고르신건가요??

  • c
cjh7302480 댓글 1 좋아요 0 조회수 238

mqtt전송

해결됨

MQTT 사물인터넷 통신 프로젝트 (Arduino, MQTT, Node.js, mongoDB, Android)

안녕하세요! 좋은 강의를 만나 프로젝트를 무난하게 진행하고 있던 와중 궁금한 점이 생겨 이렇게 질문드립니다. 혹시 publisher가 서버에 json데이터만이 아닌 이미지파일을 전송하려고 하는것도 가능할까요??!

  • nodejs
  • mongodb
  • iot
  • arduino
dodo283 댓글 1 좋아요 0 조회수 762

녹스를 제공받은 vmware 리눅스 os에서 실행을 시키는건가요 아니면 기존에 사용중인 윈도우에서 실행을 시키는건가요>??

미해결

리버스쿨 Level3 - 안드로이드 모바일 리버싱

윈도우에서 녹스를 실행시켰을 때 ip가 172로 잡히고 리눅스 ip는 192여서 adb connect 를 했을 때 연결이 안되는것 같아요.. 녹스를 리눅스로 파일을 옮기고 해봤는데도 exe라서 실행이 안되는것 같고 해결방안을 좀 알 수 있을가요.. 4일동안 구글링을 해봤는데도 안나와서 질문드려요 ㅠㅠ

  • android
  • 리버싱
이현호 댓글 1 좋아요 0 조회수 349

유니티 프로그램 다운로드 후 프로젝트 실행할 때

미해결

유니티(Unity)로 시작하는 게임개발: Part 2. C# 프로그래밍 입문

프로젝트에서 새로 생성을 클릭 후 생선버튼을 눌렀을 때 "에디터 실행 중 오류가 발생했습니다. 라이선스가 유효하지 않습니다" 라고 합니다. 어떻게 해야 되나요?

  • unity
  • C#
gkgk9034 댓글 1 좋아요 1 조회수 502

리뉴얼 강의 쿠폰 발급 부탁드립니다.

미해결

쉽게 배우는 Webpack

늦게 봐서 확인을 못헀습니다 ㅜㅜ 부탁드려도 될까요?

  • 웹팩
  • Bundling
엘프테일러 댓글 1 좋아요 1 조회수 263

GPU와 TPU차이

미해결

[개정판] 딥러닝 컴퓨터 비전 완벽 가이드

최근에 코랩에 TPU도 추가가 된거 같은데 GPU를 썻을때와 TPU를 썻을때에 성능차이가 있을까요?

  • python
  • 컴퓨터-비전
  • keras
  • 딥러닝
  • 머신러닝 배워볼래요?
  • tensorflow
Mr.M 댓글 1 좋아요 0 조회수 7708

강의등록만해놓고진도가안나가네요(@^^@)

미해결

애플 웹사이트 인터랙션 클론!

열심히 해야하는데... 업무핑계로 계속 미루고있네요 ㅠㅠ 강의는 초반만 조금 봤는데... 디테일하게 알려주셔서 너무 좋더라구요... 다음강의도 좀더 좋은 컨텐츠로 부탁드립니다.

  • 클론코딩
  • 인터랙티브-웹
  • javascript
  • HTML/CSS
  • svg
  • 웹 디자인
kain74 댓글 1 좋아요 0 조회수 145

안녕하세요. useEffect 관련 질문 드립니다!

미해결

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

안녕하세요 useEffect hooks 관련해서 질문드립니다. useEffect를 하지 않았을경우 당연히 렌더는 1번되지만 useEffect로 디스패치를 하면 렌더링이 3번됩니다. 이게 정상인지 아닌지 잘 모르겠어서 질문드립니다..

  • react
  • express
  • redux
  • nodejs
  • Next.js
정문채 댓글 18 좋아요 0 조회수 695

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

미해결

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

페이지 로딩은 정상적으로 되는데 스크롤시 에러가 발생합니다. <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;

  • redux
  • react
  • express
  • nodejs
  • Next.js
castinglife 댓글 2 좋아요 0 조회수 338

로그인후 앱 크러쉬

미해결

따라하며 배우는 노드, 리액트 시리즈 - 기본 강의

https://github.com/lhj1004ss/LoginAndRegister_react-node ERROR: Cannot set headers after they are sent to the client 포스트맨으로 로인후에 유저토큰만 완성되고 바로 앱이 크러쉬됩니다. 구글링을 좀해보니, 이 에러가 보통 콜백이 서버에서의 아웃풋이 클라이언트로 생각했던것보다? 일찍 갔을 때 나오거나 콜백을 두번불렀을 때 나오는 에러라고 하는데, 코드를 검토해도찾을 수 없어서 여기에 올립니다!

  • nodejs
  • react
newCode 댓글 2 좋아요 0 조회수 345

카트페이지에서 quantity만 못가져오는데요 왜그런걸까요 ?

미해결

따라하며 배우는 노드, 리액트 시리즈 - 쇼핑몰 사이트 만들기[전체 리뉴얼]

스테잇을 확인하면 quantity 부분이 빠져있는데 어디를 확인해야 할지 모르겠네요. 도와주세요! 감사합니다 깃헙주소입니다: https://github.com/vladastam/shop_app

  • 웹앱
  • mongodb
  • react
  • nodejs
  • redux
JH YI 댓글 0 좋아요 0 조회수 166

tdd 테스트코드 작성 B에서 아래와 같은 에러가 납니다.

미해결

파이썬 사용자를 위한 웹개발 입문 A to Z Django + Bootstrap

test 누르면 아직 게시물이 없습니다. 에서 자꾸 에러가 나요. (venv) C:\Users\user\Desktop\HTML+django\my_django>python manage.py test Creating test database for alias 'default'... System check identified no issues (0 silenced). F ====================================================================== FAIL: test_post_list (blog.tests.TestView) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:\Users\user\Desktop\HTML+django\my_django\blog\tests.py", line 30, in test_post_list self.assertIn('아직 게시물이 없습니다.', soup.body.text) AssertionError: '아직 게시물이 없습니다.' not found in '\n\n\nBootswatch\n\n\n\n\n\n\nThemes \n\nDefault\n\nCerulean\nCosmo\nCyborg\nDarkly\nFlatly\nJournal\nLitera\nLumen\nLux\nMateria\nMinty\nPulse\nSandstone\nSimplex\nSketchy\nSlate\nSolar\nSpacelab\nSuperhero\nUnited\nYeti\n\n\n\nHelp\n\n\nBlog\n\n\nFlatly \n\nOpen in JSFiddle\n\nbootstrap.min.css\nbootstrap.css\n\n_variables.scss\n_bootswatch.scss\n\n\n\n\n\n\n\n\n\n\nBlog\nby matiii\n' ---------------------------------------------------------------------- Ran 1 test in 0.037s FAILED (failures=1) Destroying test database for alias 'default'... post_list.html <div class ="col-md-8" > <h1 class ="my-4" > Blog </h1> <small> by matiii </small> </h1> <!-- Blog Post --> {% if objects_list.exist %} {% for p in object_list %} <div class ="card mb-4" > {% if p.head_image %} <img class ="card-img-top" src ="{{ p.head_image.url }}" alt ="Card image cap" > {% else %} <img class ="card-img-top" src ="https://picsum.photos/750/300/?random" alt ="Card image cap" > {% endif %} <div class ="card-body" > <h2 class ="card-title" > {{ p.title }} </h2> <p class ="card-text" > {{ p.content | truncatewords:50 }} </p> <a href ="#" class ="btn btn-primary" > Read More &rarr; </a> </div> <div class ="card-footer text-muted" > Posted on {{p.created}} <a href ="#" > {{p.author}} </a> </div> </div> <!-- Pagination --> <ul class ="pagination justify-content-center mb-4" > <li class ="page-item" > <a class ="page-link" href ="#" > &larr; Older </a> </li> <li class ="page-item disabled" > <a class ="page-link" href ="#" > Newer &rarr; </a> </li> </ul> {% endfor %} {% else %} <h3> 아직 게시물이 없습니다 . </h3> {% endif %} </div> tests.py from django.test import TestCase # Create your tests here. from django.test import TestCase, Client from bs4 import BeautifulSoup from .models import Post from django.utils import timezone from django.contrib.auth.models import User class TestView (TestCase): def setUp ( self ): self .client = Client () self .author_OOO=User.objects. create ( username = 'SAM' , password = 'none' ) def test_post_list ( self ): response = self .client. get ( '/blog/' ) self . assertEqual (response.status_code, 200 ) soup = BeautifulSoup (response.content, 'html.parser' ) title = soup.title self . assertEqual (title.text, 'Blog' ) navbar = soup. find ( 'div' , id = 'navbar' ) self . assertIn ( 'Blog' , navbar.text) self . assertIn ( 'Help' , navbar.text) self . assertEqual (Post.objects. count (), 0 ) self . assertIn ( ' 아직 게시물이 없습니다 .' , soup.body.text) post_OOO = Post.objects. create ( title = 'The First Post' , content = 'Hello World!' , created =timezone. now (), author = self .author_OOO, ) self . assertGreater (Post.objects. count (), 0 ) response = self .client. get ( '/blog/' ) self . assertEqual (response.status_code, 200 ) soup = BeautifulSoup (response.content, 'html.parser' ) body = soup.body self . assertNotIn ( ' 아직 게시물이 없습니다 .' , body.text) self . assertIn (post_OOO.title, body.text)

  • django
  • bootstrap
  • python
임찬주 댓글 1 좋아요 0 조회수 330

H2실행

미해결

스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술

안녕하세요 강사님 ! H2실행이 안돼서 여러가지 방법을 시도해봤는데 실행이 안되네요.. 브라우저 창이 열리지않고 아래 사진과 같이 보여지고 종료되는데 혹시 이유 아신다면 알려주시면 감사하겠습니다

  • java
  • MVC
  • spring-boot
  • spring
leh 댓글 2 좋아요 0 조회수 307

대응방안 코드에 대해서 질문이 있습니다.

미해결

모의해킹 실무자가 알려주는, SQL Injection 공격 기법과 시큐어 코딩 : PART 1

안녕하세요. 강의 "대응 방안" 파트의 "상세 대응 방안(2)" 강의를 보던 중 궁금한점이 생겨서 질문드립니다. 1) 첫째로, 문자 입력에 대한 대응 방안으로 강의에서 예시로 제시했던 코드가 아래와 같습니다. --------------------------------------------------------------------------- JAVA String keyword = request.getParameter("keyword"); // MSSQL, ORACLE 대응 방안 예시 keyword = keyword.replace("'", "''"); <<<<<<<<< (1) keyword = keyword.replace("\"", "\"\""); query = "select * from board where keyword like '%" + keyword + "%'"; ------------------------------------------------------------------------------- 이 부분에서 <<<<<<<< (1) 표시된 부분의 내용이 잘 이해가 되지 않아서요. 강의 하시면서 공격자가 '(작음따옴표) 하나를 입력하면 두개로 변하여 '(작은따옴표)가 검색이 된다고 말씀하셨는데요 만약 공격자가 '(작은따옴표) 하나를 입력한 경우라면 이 하나의 '(작은따옴표)가 연속된 두개의 ''(작은 따옴표)로 변하여 query 는 다음과 같이 생성될 것입니다. select * from board where keyword like '%''%' 이런식으로 되면 % 양쪽으로 그냥 '(작은따옴표)로 깜싸는 정도로 바뀔거라고 짐작하고 있는데요. 어떻게 '(작은따옴표)도 검색이 가능하게 되는지 이해가 되지 않습니다. 그리고 이 경우 mysql 경우 처럼 \' 패턴을 이용하면 우회가 가능한지도 궁금합니다. 그리고.... 2) 두번째로, 컬럼/테이블 입력에 대한 대응 방안으로 제시한 예제 코드의 일부는 다음과 같습니다. boolean flag = Pattern.matches("^[0-9a-zA-Z-]*$", tb_name); 강의에서 설명해주셨던 것 처럼 이 코드 내용은 숫자, 알파벳 대소문자 그리고 -(대시 또는 하이픈)문자를 필터링하는 것인데요. 제 생각에는 -(대시) 문자 이외의 _(언더바) 문자가 추가되어야 하지 않을까 해서요? 강의에서 사용하였던 "TB_BOARD" 처럼 _(언더바) 문자가 포함된 테이블이나 컬럼이 존재하는거로 봐서는요, 아니면 제시된 코드의 오타인걸로도 생각이 들고요. 감사합니다~

  • 모의해킹
  • 인젝션
Jeong Seob Lee 댓글 2 좋아요 1 조회수 343

IP 제어 관련 문의 드립니다.

해결됨

스프링 시큐리티

안녕하세요.. 현재 강의를 듣고 있는데요. Client IP 접근을 제어해서, 자원에 접근이 안되도록 하는 데모를 보았습니다. 이 기능을 사용할 경우, WAS 앞단에 L4 나 Proxy 와 같은 장치가 있어도, Client IP 를 정상적으로 인식 할 수 있나요? 혹시, L4 나 Proxy 가 있는 경우, 사용자의 Client IP 가 아니고, L4 IP 가 전달되지 않는 지 문의드립니다.

  • java
  • spring-boot
  • Spring Security
insu2012.lee 댓글 1 좋아요 0 조회수 220

강의자료는 없나요?????

미해결

자바 스프링 프레임워크(renew ver.) - 신입 프로그래머를 위한 강좌

3강 듣고있는데 이거 혹시 강의자료는 없나요???

  • spring
  • java
eunseo505 댓글 0 좋아요 0 조회수 171

아무리 찾아도답이안나와서 질문드립니다.

해결됨

Vue.js 완벽 가이드 - 실습과 리팩토링으로 배우는 실전 개념

https://codepen.io/mikilll94/pen/RwbYrpz 간단한 예제인데요. 데이터가 많을때 v-model 바인딩을 하면 속도가 엄청느려지는데 이유가 뭔가요 v-model 동작방식과 관련이 있을까요??.. 그리고 해결방안이 있을까요

  • vuejs
  • javascript
흰둥이 댓글 2 좋아요 1 조회수 219

like, unlike

해결됨

React로 NodeBird SNS 만들기

안녕하세요. 제로초님 다름이 아니라 axios like에 return axios . post ( `/post/ ${ postId } /like` , {}, { withCredentials : true }); 이렇게 하면 잘 날라가는데 return axios . delete ( `/post/ ${ postId } /like` , {}, { withCredentials : true }); 이렇게 unlike는 빈 데이터를 보내면 401 에러가 뜨는데 그 에러가 로그인 안했다는 isLoggedin 그 에러거든요? 근데 로그인 정보랑 다 들어있고 해서 의아했는데 저 빈 객체 를 없애니깐 잘 동작하더라고요. 이거 왜이러는걸까요?

  • javascript
  • react
이주호 댓글 2 좋아요 0 조회수 163

인기 태그

인프런 TOP Writers

주간 인기글