월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
강의중 궁금한게 있습니다.!!
++ 혹시 ... 서버 실행 중 Executing (default): SELECT 1+1 AS result db 연결 성공 ------------------------------------------------------------------------------------------------ 초기 테이블 생성 중 create 관련 메시지가 안뜨고... Executing (default): SELECT 1+1 AS result 저 메시지가 뜨는데 이유가 뭘까요 ㅠㅠ. 초기 테이블 작성하고 첫 시도 입니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Network 표시와 action 표시
1. 강사님처럼 Network 가 안뜨는데 이유가 뭘까요?? 2. 그리고 예전에 밑에 그림처럼 콘솔에 액션이 뜨도록 설정했었잖아요, 제가 그 코드를 지웠다가 다시 하고싶어서 설정하려니 안되네요 왜이럴까요 강사님 화면 제 코드 입니다 (액션 표시가 안됨 ㅠ) // configureStore.js import { applyMiddleware, compose, createStore } from "redux"; import createSagaMiddleware from 'redux-saga'; import { createWrapper } from "next-redux-wrapper"; import { composeWithDevTools } from 'redux-devtools-extension'; import reducer from "../reducers"; import rootSaga from '../sagas'; // ex) 엄청 간단한 미들웨어: const loggerMiddleware = ({ dispatch, getState }) => (next) => (action) => { console.log(action) // action 을 실행하기 전에 console.log() 를 한번 실행해주는 미들웨어 return next(action); } const configureStore = () => { const sagaMiddleware = createSagaMiddleware() // const middlewares = [sagaMiddleware]; const middlewares = [sagaMiddleware, loggerMiddleware]; // enhancer: 리덕스의 기능이 확장된 것 const enhancer = process.env.NODE_ENV === 'production' // 개발용 미들웨어와 배포용 미들웨어는 다름 ? compose(applyMiddleware(...middlewares)) // 배포용 : composeWithDevTools(applyMiddleware(...middlewares)) // 개발용 const store = createStore(reducer, enhancer); store.sagaTask = sagaMiddleware.run(rootSaga); return store; // state , reducer 를 포함한 것 }; const wrapper = createWrapper(configureStore, { debug: process.env.NODE_ENV === "development", }); // configureStore 옆으로 두 번째 인자: optional 객체 // 개발시 다음과 같이 debug 를 true 로 하는 것이 설명도 뜨고 편리하다. export default wrapper;
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
antd
안녕하세요 강의랑은 예외질문인 것같지만 antd 에서 아이콘들 때문에 밑에 이미지같은 결과가 나오는 것같은데, 자기가 사용할 아이콘들 다운받아서 static폴더 따로 만들어서 사용 해도 별 문제 없을까요? 아이콘을 따로 받아서 사용할경우 license라든가 렌더 속도문제 같은 부가적인 문제들은 없을까요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
제로초님
제로초님, 설치 하려는중에 문제가 발생되서요ㅠㅠ 문의드립니다 이렇게 비밀번호가 틀리다고 나와서 이 방법을 따라하여 비번변경하고, 비밀번호를 치면 틀리다고 나옵니다... ㅠ_ㅠ 혹시 왜 그러는지 어림짐작이라도 되는게 있으실까요...? 저는 정말 모르겠어서 문의드립니다...
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
3시간동안봤는데 찾질 못했습니다 ㅠㅠ
4셉터 마지막 팔로우 부분 질문입니다. 팔로우 버튼 클릭시 모든 글 들이 로딩중 표시가 뜨구요 제가 sagas followrequest type 에서 action.data를 console로 받아본 결과 제가 클릭한 post id값이 들어오긴하는데 그 값이 계속 무한루프로 들어와요 리덕스 diff 값은 FOLLOW_REQUEST 만 계속 도는 에러입니다. 버튼 클릭과 동시에 모든 글의 버튼에서 로딩중 표시가 뜬다는건 followButton.js onclick 부분이 잘못되나 싶다가도 클릭한 해당 글의 id값은 잘들어오는데 똑같은 값이 무한루프로 해당 액션 type에 값이 들어오는경우는 도대체 뭔지모르겠네요ㅠㅠ.. FollowButton.js, reduce user.js saga user.js 파일 해당 관련 로직 올려 드리겠습니다. ㅠㅠ const FollowButton = ({ post }) => { const dispatch = useDispatch(); const { me, followLoading, unfollowLoading } = useSelector((state) => state.user); const isFollowing = me?.Followings.find((v) => v.id === post.User.id); const onClickButton = useCallback(() => { if (isFollowing) { dispatch({ type: UNFOLLOW_REQUEST, data: post.User.id, }); } else { dispatch({ type: FOLLOW_REQUEST, data: post.User.id, }); } }, [isFollowing]); return ( <Button loading={followLoading || unfollowLoading} onClick={onClickButton}> {isFollowing ? '언팔로우' : '팔로우'} </Button> );}; case FOLLOW_REQUEST : // 팔로우 요청 draft.followLoading = true; draft.followError = null; draft.followDone = false; break;case FOLLOW_SUCCESS : //팔로우 성공 draft.followLoading = false; draft.followDone = true; draft.me.Followings.push({ id: action.data }); break;case FOLLOW_FAILURE : //팔로우 실패 draft.followLoading = false; draft.followError = action.error; //로그인 실패 확인 break; function* follow(action) { try { yield delay(1000); //const reuslt = yield call(followAPI, action.data); yield put({ type: FOLLOW_SUCCESS, data : action.data, }); } catch (e) { console.log(e); yield put({ type: FOLLOW_FAILURE, data: e.response.data, }); }}
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
불변성 유지하면서 변수 만들 때 질문이 있습니다.
불변성 유지하기 위해서 변수 만들떄 const postIndex = state.mainPosts.findIndex((v) => v.id === action.data.postId); const post = {...state.mainPosts[postIndex]} post.Comments = [dummyComments(action.data.content), ...post.Comments] const mainPosts = [...state.mainPosts] mainPosts[postIndex] = post 이러한 과정을 거치던데 const post = {...state.mainPosts[postIndex]} 부분이 왜 {}로 감싸져 있는걸까요?? 대괄호인 []로 감싸져야 하는게 아닐까요?? 그 아래인 const mainPosts = [...state.mainPosts]로 []로 감싸져 있는데 왜그런걸까요
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요 강의 시청 중 질문이 있습니다.
다름이아니라 강의중 또 궁금한게 생겼습니다.! 다름이아니라 back 단 express 던 nest던 프론트에 redux 같이 에러잡기 좋은 방법이나 라이브러리있나요? 자스는 워낙 다이나믹해서.. ㅎㅎ.. 타입스크립트 말고도 있을까요.... 강의 정말 잘 보고 있습니다. ㅎㅎ
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
리덕스 툴킷 + axios 에러핸들링 질문드립니다
안녕하세요. 지난번에 리덕스 툴킷관련해서 addCass로 여러 상태관리에 대해 많은 공부가 됐습니다. 단순 상태관리까지는 성공했는데 이제 axios를 붙이다보니 에러핸들링에 관련해서 궁금한점이 생겼습니다. export const loginAsync = createAsyncThunk('user/login', async(action) => { const { data } = action; return axios .post('http://localhost:5000/login', data) .then((res) => res.data) .catch((e) => e); }); 이렇게 createAsyncThunk를 만들어서 통신을 하는데 대충 테스트용으로 만들어놓은 api에서 app.post('/login', (req, res, next) => { const { userId, userPassword } = req.body; if (userId !== 'test' || userPassword !=='123' ) { return res.status(403).json({ login : false }); } else { return res.json({ userName : '이름', userAge : 00, userJob : '초보 프론트엔드' }); } }) 이렇게 내보냈습니다.성공했을때는 문제가 없으나 실패했을때가 문제입니다. 로그인 실패시 export const userSlice = createSlice({ name : 'user', initialState, reducers : {}, extraReducers : (builder) => { builder .addCase(loginAsync.pending, (state) => { state.logInLoading = true; state.logInError = null; state.logInDone = false; }) .addCase(loginAsync.fulfilled, (state, action) => { console.log('성공') // state.logInLoading = false; // state.userInfo = action.payload; // state.logInDone = true; }) .addCase(loginAsync.rejected, (state, action) => { console.log('실패') // state.logInLoading = false; // state.userInfo = null; // state.logInError = action.payload.error; }) } }); 실패해도 fulfilled에 걸리게 됩니다. 403에러를 띄웠다면 saga에서는 failure액션에 걸렸던거같은데 툴킷은 다른거같아서 좀 검색해보니rejectedValue? 라는 함수가 있다는데 임포트 해와도 에러만 날뿐입니다. 제가 청크 미들웨어를 한번도 안써봐서 이렇게 헤메는걸까요? 아니면 툴킷은 따로 에러핸들링 하는 방법이 있나요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
로그인이 안돼서 질문 남겨요!
제로초님 로그인이 안돼서 질문드립니다. 분명 전 강좌까지 잘 되던 로그인이 로딩표시만 나오고 로그인은 안돼서 무엇이 잘못된건지 햇갈려 질문남겨요 ㅜ 터미널에는 TypeError: Cannot read property 'forEach' of null at end (C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:1017:18) at Object.abort (C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:817:5) at C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:598:22 at immediately (C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:60:12) at runForkEffect (C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:588:3) at runEffect (C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:1208:7) at digestEffect (C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:1275:5) at C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:677:5 at Array.forEach (<anonymous>) at runAllEffect (C:\Users\moonj\OneDrive\문서\test-project\practice\react-nodebird\REACT.SNS\front\node_modules\@redux-saga\core\dist\redux-saga-core.dev.cjs.js:676:8) The above error occurred in task watchAddPost created by postSaga created by watchAddComment created by postSaga created by rootSaga Tasks cancelled due to error: postSaga postSaga userSaga 이러한 에러가 뜨고 콘솔에서는 이런 에러가 뜨는데 무엇이 해결책인지 모르겠어서 질문 남겨요 user saga import { all, fork, put, delay, takeLatest } from 'redux-saga/effects' import axios from 'axios' import { LOG_IN_REQUEST, LOG_IN_SUCCESS, LOG_IN_FAILURE, LOG_OUT_REQUEST, LOG_OUT_SUCCESS, LOG_OUT_FAILURE, SIGN_UP_REQUEST, SIGN_UP_SUCCESS, SIGN_UP_FAILURE } from '../reducers/user' function logInAPI (data) { return axios.post('/api/login', data) } function* logIn (action) { try { console.log('saga login') // const result = yield call(logInAPI, action.data) yield delay(1000) yield put({ type: LOG_IN_SUCCESS, data: action.data }) } catch (err) { console.error(err) yield put({ type: LOG_IN_FAILURE, error: err.response.data }) } } function logOutAPI (data) { return axios.post('/api/logout', data) } function* logOut (action) { try { // const result = yield call(logOutAPI, action.data) yield delay(1000) yield put({ type: LOG_OUT_SUCCESS // data: result.data }) } catch (err) { yield put({ type: LOG_OUT_FAILURE, error: err.response.data }) } } function signUpAPI (data) { return axios.post('/api/logout', data) } function* signUp (action) { try { // const result = yield call(logOutAPI, action.data) yield delay(1000) yield put({ type: SIGN_UP_SUCCESS // data: result.data }) } catch (err) { yield put({ type: SIGN_UP_FAILURE, error: err.response.data }) } } function* watchLogIn () { yield takeLatest(LOG_IN_REQUEST, logIn) } function* watchLogOut () { yield takeLatest(LOG_OUT_REQUEST, logOut) } function* watchSignUp () { yield takeLatest(SIGN_UP_REQUEST, signUp) } export default function* userSaga() { yield all ([ fork(watchLogIn), fork(watchLogOut), fork(watchSignUp) ]) } configureStore.js import { createWrapper } from 'next-redux-wrapper' import { applyMiddleware, createStore, compose } from 'redux' import { composeWithDevTools } from 'redux-devtools-extension' import rootReducer from '../reducers'; import createSagaMiddleware from 'redux-saga' import rootSaga from '../sagas'; const loggerMiddlware = ({dispatch, getState}) => (next) => (action) => { console.log(action) return next(action) } const configureStore = () => { const sagaMiddleware = createSagaMiddleware() const middlewares = [sagaMiddleware, loggerMiddlware]; const enhancer = process.env.NODE_ENV === 'production' ? compose(applyMiddleware(...middlewares)) : composeWithDevTools(applyMiddleware(...middlewares)) const store = createStore(rootReducer, enhancer); store.sagaTask = sagaMiddleware.run(rootSaga) return store; } const wrapper = createWrapper(configureStore, { debug: process.env.NODE_ENV === 'development' }) export default wrapper usersaga나 configureStore도 잘 설정되 있는데 무엇이 문제인가 여 ㅜ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
배포 질문 드립니다.
안녕하세요. 배포 관련 질문 드립니다. 저같은 경우 Azure DevOps에서 CI/CD 를 사용해서 자동 빌드 배포를 하려고 합니다. git에 push 하면 자동 빌드 되고 배포까지 되는데 문제는 .env 파일의 경우 git에 올리지 않았기 때문에 빌드 시 오류가 발생하거든요. 이런 경우는 그냥 .env 파일을 git에 올려야 하는지, 아니면 다른 방법이 있는지 궁금합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
LOAD_POSTS_REQUEST가 무한정 일어나요.,
보시다 시피 GET /posts 304 에러로 인해 정상 작동하지 않습니다. 사진상으로는 멈춰있지만 무시무시하게 많은 에러가 제 신성한 VSC 터미널창을 도배하고 있습니다. 처음엔 REQUEST - SUCCESS를 번갈아 보내는 모습을 보고, 'page/index.js'에서 요청을 보내는 클라이언트의 잘못이라고 생각했습니다. REQUEST를 무한정 보내고 있으니 보내는 쪽의 문제일 거라고요.. front/page/index.js ... ... import { LOAD_POSTS_REQUEST } from '../reducerss/post'; import { LOAD_USER_REQUEST } from '../reducers/user'; const Home = () => { const dispatch = useDispatch(); const { me } = useSelector(state => state.user); const { mainPosts, hasMorePosts, loadPostsLoading } = useSelector( state => state.post, ); useEffect(() =>{ dispatch({ type: LOAD_USER_REQUEST, }); dispatch({ type: LOAD_POSTS_REQUEST, }) }, []); const [ref, inView] = useInView(); // useEffect(() => {}, []) : 뒤에 빈배열을 넣을 경우.componentDidMount()같은 효과를 줄 수 있다. useEffect(() => { if (inView && hasMorePosts && !loadPostsLoading) { const lastId = mainPosts[mainPosts.length - 1]?.id; dispatch({ type: LOAD_POSTS_REQUEST, lastId, }); } }, [inView, hasMorePosts, loadPostsLoading, mainPosts]); return ( <AppLayout> {me && <PostForm />} {mainPosts.map(post => ( <PostCard key={post.id} post={post} /> ))} ....... ....... </AppLayout> ); }; export default Home; 그런데 딱히 특이사항이 발견되지 않아서 front/sagas와 front/reducers 쪽을 보았습니다. front/sagas import { all, delay, fork, put, call, takeLatest, throttle, } from 'redux-saga/effects'; import axios from 'axios'; import { ...... LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE, } from '../reducers/post'; function loadPostsAPI(data) { //실제로 서버에 요청을 보냄 return axios.get('/posts', data); } function* loadPosts(action) { try { const result = yield call(loadPostsAPI, action.data); yield put({ type: LOAD_POSTS_SUCCESS, data: result.data, // data에 게시글들 배열 -> 이것은 reducer로 간다 }); } catch (err) { yield put({ type: LOAD_POSTS_FAILURE, data: err.response.data, }); } }; .... function* watchLoadPosts() { yield takeLatest(LOAD_POSTS_REQUEST, loadPosts); } .... export default function* postSaga() { yield all([ fork(watchAddPost), fork(watchLoadPosts), fork(watchRemovePost), fork(watchAddComment), ]); } front/reducers const reducer = (state = initialState, action) => produce(state, draft => { switch (action.type) { 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.loadPostsError = null; draft.hasMorePosts = draft.mainPosts.length < 50; break; case LOAD_POSTS_FAILURE: draft.loadPostsLoading = false; draft.loadPostsError = action.error; break; 304 에러로 보아 서버 쪽 문제일 수도 있어서 라우터 쪽도보았습니다. const express = require('express'); const { Post, Image, User, Comment } = require('../models'); const router = express.Router(); router.get('/', async (req, res, next) => { // GET /posts try { const posts = await Post.findAll({ limit: 10, order: [['createdAt', 'DESC']], include: [{ model: User, }, { model: Image, }, { model: Comment, }], }); res.status(200).json(posts); } catch (error) { console.error(error); next(error); } }); module.exports = router; 어느쪽 문제인지 사실 잘 모르겠습니다.저런식으로 무한 로딩 에러가 난다면 어디에서 에러를 찾아야 하나요? 제가 생각하기엔 프론트였는데 예상이 빗나가서 2시간째 헤매고 있습니다 도와주세요
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
next는 creat next app도 괜찮은가요?
리액트 프로젝트를 create react app으로 만들경우 웹팩을 직접 설정할 수가 없어서 현업에서는 대부분 직접 프로젝트를 세팅하는 것으로 알고 있는데 create next app로 만든 next 프로젝트는 웹팩을 직접 설정해서 덮어씌울수 있고 노드 모듈도 최소한으로만 설치를 하더라고요요 그렇다면 next는 그냥 cna로 해도 괜찮을까요? 현업에서는 주로 어떻게 하시는지 궁금합니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
로컬에서 권한문제? 같은 에러가 있습니다.
안녕하세요 제로초님 다름아니라 사이트 배포후에 계속 업데이트를 하다가 git 에러가 발생하여 수정한 이후에 개발자모드에서 다시금 사이트를 돌려보는데 이상한 에러가 떠서 문의드립니다. 무언가 권한문제인 것 같은데 front에서 back으로 연동이 안되는 문제가 발생한 것 같습니다. (SSR부터 안됩니다) 리덕스 개발툴에서 보니 Host must be 127.0.0.1:3065라는 글이 떠서 Postman 작동시켜서 보니 백앤드는 따로 작동잘 하는 것 같습니다. console창에서 확인해보니 이렇게 문제가 뜨는 것 같은데 아무래도 권한문제인건 아닌지.. 확인좀 부탁드립니다. ㅠ 프론트앤드에서 화면은 잘 나옵니다!
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요 리덕스, saga 관련 질문이 있습니다.
안녕하세요 다름이아니라 반복적인 request, success, failure 작성하다 진이 빠진 1인 입니다... 확실히 이렇게하면 말씀대로 diff 에서 이벤트 동작하나하나 마다 3개의 상태로 관리할 수 있어 오류에 대한 유지보수할 때 편할 것 같긴한데 너무 코드가 길어지는 노가다성 ... (제가 개발자인지 복붙만 하는 사람인지 헷갈리게 되는...), 코드가 길어짐에 따른 성능 저하? 가 있는거 같아요... 회사급에 실제 서비스를 만들때도 리덕스 이렁형태의 상태관리 코드로 관리하나요? 친절한 강의 정말 잘 보고있습니다.
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
faker 실행이 안됩니다.
terminal에서 npm i faker 설치하고 post.js 상단에 import 해주었는데 계속 오류메시지가 떠서요,, 어떻게 해결할수 있을까요? reducers > post.js import shortId from 'shortid'; import produce from 'immer'; import faker from 'faker'; export const initialState = { mainPosts: [{ id: 1, User: { id: 1, nickname: '레니', }, content: '첫 번째 게시글 #해시태그 #익스프레스', Images: [{ id: shortId.generate(), src: 'https://cdn.pixabay.com/photo/2022/02/01/11/48/woman-6986050_960_720.jpg', }, { id: shortId.generate(), src: 'https://cdn.pixabay.com/photo/2019/03/27/15/24/animal-4085255__340.jpg', }, { id: shortId.generate(), src: 'https://cdn.pixabay.com/photo/2022/02/09/20/52/labrador-retriever-7004193__340.jpg', }], Comments: [{ id: shortId.generate(), User: { id: shortId.generate(), nickname: 'nero', }, content: '너무 반가워요!', }, { id: shortId.generate(), User: { id: shortId.generate(), nickname: 'hero', }, content: '유익한 내용입니다', }] }], mainPosts: [], imagePaths: [], hasMorePosts: true, loadPostsLoading: false, loadPostsDone: false, loadPostsError: null, addPostLoading: false, addPostDone: false, addPostError: null, removePostLoading: false, removePostDone: false, removePostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, }; initialState.mainPosts = initialState.mainPosts.concat( Array(20).fill().map(() => ({ id: shortId.generate(), User: { id: shortId.generate(), name: faker.name.findName() }, content: faker.lorem.paragraph(), Images: [{ src: faker.image.imageUrl(), }], Comments: [{ User: { id: shortId.generate(), nickname: faker.name.findName() }, content: faker.lorem.sentence(), }], })), ); export const LOAD_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 addPost = (data) => ({ // 동적액션크리에이터 type: ADD_POST_REQUEST, data, }) export const addComment = (data) => ({ type: ADD_COMMENT_REQUEST, data, }) const dummyPost = (data) => ({ id: data.id, content: data.content, User: { id: 1, nickname: '레니', }, Images: [], Comments: [], }); const dummyComment = (data) => ({ id: shortId.generate(), content: data, User: { id: 1, nickname: '레니', }, }); // 리듀서 : 이전상태를 액션을 통해 다음 상태로 만들어내는 함수( 불변성은 지키면서 ) const reducer = (state = initialState, action) => produce(state, (draft) => { switch (action.type) { case ADD_POST_REQUEST: draft.addPostLoading = true; draft.addPostDone = false; draft.addPostError = null; break; case ADD_POST_SUCCESS: draft.addPostLoading = false; draft.addPostDone = true; draft.mainPosts = unshift(dummyPost(action.data)); break; case ADD_POST_FAILURE: draft.addPostLoading = false; draft.addPostError = action.error; break; case REMOVE_POST_REQUEST: draft.removePostLoading = true; draft.removePostDone = false; draft.removePostError = null; break; case REMOVE_POST_SUCCESS: draft.removePostLoading = false; draft.removePostDone = true; draft.mainPosts = draft.mainPosts.filter((v) => v.id !== action.data); break; case REMOVE_POST_FAILURE: draft.removePostLoading = false; draft.removePostError = action.error; break; case ADD_COMMENT_REQUEST: draft.addCommentLoading = true; draft.addCommentDone = false; draft.addCommentError = null; break; case ADD_COMMENT_SUCCESS: { const post = draft.mainPosts.find((v) => v.id === action.data.postId); post.Comments.unshift(dummyComment(action.data.content)); draft.addCommentLoading = false; draft.addCommentDone = true; break; // const postIndex = state.mainPosts.findIndex((v) => v.id === action.data.postId); // const post = { ...state.mainPosts[postIndex] }; // post.Comments = [dummyComment(action.data.content), ...post.Comments]; // const mainPosts = [ ... state.mainPosts]; // mainPosts[postIndex] = post; // return { // ...state, // addCommentLoading: false, // addCommentDone: true, // }; } case ADD_COMMENT_FAILURE: draft.addCommentLoading = false; draft.addCommentError = action.error; break; default: break; } }); export default reducer;
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
useEffect 를 쓰고 안쓰고의 차이
리액트 무료강좌도 다 듣고 본 강좌를 듣고있는건데도 아직까지 useEffect 에 대한 이해가 쉽게 되지 않네요ㅜ 1. useEffect 를 쓰는 이유가 컴포넌트의 라이프싸이클을 다를 수 있는것, 그리고 리렌더링 될 때마다 함수가 불필요하게 실행되는걸 막기위해 [] 안의 dependency 값이 바뀔 때만 실행되도록 할 수 있어서가 맞을까요? 수정) 계속 공부해보니 함수 컴포넌트에서 return 구문 밖에서의 함수 실행은 거의 모두 useEffect 내에 작성한다고 되어있는데 그럼 if (!me) { return null; } 이부분은 왜 useEffect 로 감싸지 않았나요? 용량이 크지 않아서 그런걸까요? 2. 어차피 로그인의 여부인데 굳이 me.id 가 아닌 me 의 여부로 넣어되지 않나요? 동작은 똑같이 합니다.
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
immur 적용하고 npm run dev 실행했는데 로그인이 안되서 질문드립니다.
terminal창에 Warning: MenuItem should not leave undefined `key`. 이 오류가 계속 보이는데요 어떻게 해결해야 할까요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
"logInDone" state값이 다른 이유가 궁금합니다.
안녕하세요. 먼저 이런 좋은 강의를 듣게 해주셔서 감사합니다. 질문) pages/index.js 와 components/UserProfile.js 에서 state값이 다른 이유가 궁금해서 질문드립니다. 아래 사진 순서) pages/index.js components/UserProfile.js 콘솔창
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요 리덕스 툴킷 관련질문입니다.
안녕하세요. 리덕스 툴킷을 활용하려고 공부를 막 시작했습니다. cra템플릿을 인스톨해서 보고있는데 비동기요청하는 부분이 조금 헷갈려 질문드립니다. // typically used to make async requests. export const incrementAsync = createAsyncThunk( 'counter/fetchCount', async (amount) => { const response = await fetchCount(amount); // The value we return becomes the `fulfilled` action payload return response.data; } ); 위 함수를 extraReducers 내에서 extraReducers: (builder) => { builder .addCase(incrementAsync.pending, (state) => { state.status = 'loading'; }) .addCase(incrementAsync.fulfilled, (state, action) => { state.status = 'idle'; state.value += action.payload; }); }, 대기, 성공, 실패를 addCase해줘야한다 로 이해는 했는데요 그 다움 궁금한점은 api요청이 한가지 가 아닐텐데 많은 api요청들을 어떻게 추가해야하나요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Redux + onClick
제로초님 안녕하세요 강의 잘 보고있습니다 감사합니다 ^^ 제로초님 리덕스 강의를 보고 응용하여 제 나름 컴포넌트를 하나 만들어보고 있는데요, 아래와 같이 2개의 객체 배열을 이용하여 맵함수로 헤더메뉴를 그리는 코드입니다. onClick 이벤트 사용 시 화살표 함수로 선언해주면 해당 onClick 이벤트를 클릭하지 않는 이상 렌더링 이후 실행되지 않는 걸로 알고 있습니다. 1. 최초 렌더링 시에는 아래 두개 onClick이벤트가 실행 되지는 않습니다. 2. 이후 첫번째 onClick={(e)=>onPrimary(index, e)} 클릭 시 두번째 onClick={(e)=>onSecondary(index, num, e)}도 실행 되지는 않습니다. [문제 위치] 3. 두번째 onClick={(e)=>onSecondary(index, num, e)} 클릭 시 해당 클릭된 함수 실행 이후 첫번째 onClick이벤트도 같이 실행되는데 원인이 뭔지 모르겠네요, 이틀째 붙잡고 헤매다 질문드립니다.