월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
게시물 불러올때 map 함수로 인한 게시물 복제?
1.처음 해당 페이지로 이동시 back에있는 데이터만 정상적으로 불러와집니다.2.두번쨰 사진과 세번쨰 사진은 다른페이지로 이동했다가 다시 해당페이지로 돌아올때 useEffect 가 정상적으로 실행, map 함수를 통해 새로운 배열을 만들시에 전에있던 배열데이터가 남아있어 동일한 데이터가 계속 쌓이는 상황입니다. 새로고침시에는 다시 정상적으로 한번만 불러와진 데이터만 보인느데 다른 페이지를 다녀와도 map 함수가 실행될때 전에 있던 데이터는 빈배열로 다시 세팅하고 map 함수가 실행되는 방법이 무엇인지 아무리 찾아봐도 해결이 안되네요.. ㅠㅜ 구원의 손길 부탁드립니다import React, { useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { Col, Row } from 'antd' import SystemLayout from '../../components/System/SystemLayout'; import SystemCard from '../../components/System/SystemCard'; import { LOAD_SYSTEMS_REQUEST } from '../../reducers/system-post'; const Test = () => { const dispatch = useDispatch(); const { mainSystems } = useSelector((state) => state.systempost); useEffect(() => { dispatch({ type: LOAD_SYSTEMS_REQUEST, }); }, []); return ( <SystemLayout> <Row justify="space-evenly"> {mainSystems.map((systempost) => ( <Col xs={6}> <SystemCard key={systempost.id} systempost={systempost} /> </Col> ))} </Row> </SystemLayout> ); }; export default Test;
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
post, put, delete 할때 data 보내는 이유?
안녕하세요 제로초님.post, put, delete 할때 백엔드에서 response에 data를 담아 돌려보내는 보내는 이유가 궁금합니다.프론트에서 post, put, delete 요청을 보내면, 백엔드에서는 db에 업데이트만 하고, 아무것도 안 돌려주고 나서,프론트에서는 response 200 온거 확인한 후에, 프론트 안에서 data를 변경해도 되지 않나요??새로고침 할때는 mounted 할때 일괄적으로 list 보내면 되지 않나요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
NicknameEditForm, FollowList가 렌더되지 않습니다
안녕하세요. 강의 잘 듣고 있습니다.현재 섹션 1까지 모두 수강하였습니다. 다만 제목과 같이 NicknameEditForm, FollowList 구현부터 웹에 렌더가 되지 않아 진도가 막힌 상태입니다...ㅜㅜantd나 스타일 컴포넌트의 문제 같아 환경설정도 변경해보고 VS code를 재다운로드하거나 버전별로 테스트 해보기도 했는데 아직 해결 방법을 찾지 못한 상태입니다. 코드 실행한 결과입니다. 회원 정보 카드 옆에 닉네임 수정 폼이나 팔로우 리스트가 생성되어야 하는데 전혀 뜨질 않네요. 혹시 어떤 해결 방법이 있을까요?아래는 환경과 작성한 코드입니다.{ "name": "react-nodebird-front", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "next" }, "author": "kde", "license": "ISC", "dependencies": { "@ant-design/icons": "^5.1.3", "antd": "^4.3.1", "next": "^9.5.5", "prop-types": "^15.8.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-test-renderer": "^17.0.2", "styled-components": "^5.3.11" }, "devDependencies": { "eslint": "^8.41.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-react": "^7.32.2", "eslint-plugin-react-hooks": "^4.6.0" } }import React, { useCallback } from 'react'; import { Avatar, Button, Card } from 'antd'; const UserProfile = ({ setIsLoggedIn }) => { const onLogOut = useCallback(() => { setIsLoggedIn(false); }, []); return ( <Card actions={[ <div key="twit">짹짹<br/>0</div>, <div key="following">팔로잉<br/>0</div>, <div key="following">팔로워<br/>0</div>, ]} > <Card.Meta avatar={<Avatar>test</Avatar>} title="test" /> <Button onClick={onLogOut}>로그아웃</Button> </Card> ); }; export default UserProfile;import React, { useMemo } from "react"; import { Form, Input } from 'antd'; const NicknameEditForm = () => { return ( <Form style={{ marginBottom: '20px', border: '1px solid #d9d9d9', padding: '20px' }}> <Input.Search addonBefore="닉네임" enterButton="수정"/> </Form> ); }; export default NicknameEditForm;import React from "react"; import PropTypes from 'prop-types'; import { Button, List, Card } from 'antd'; import { StopOutlined } from '@ant-design/icons'; const FollowList = ({ header, data }) => { return ( <List style={{ marginBottom: '20px' }} grid={{ gutter: 4, xs: 2, md: 3 }} size="small" header={<div>{header}</div>} loadMore={<div style={{ textAlign: 'center', margin: '10px 0' }}><Button>더보기</Button></div>} bordered dataSource={data} renderItem={(item) => ( <List.Item style={{ marginTop: '20px' }}> <Card actions={[<StopOutlined key="stop"/>]}> <Card.Meta description={item.nickname}/> </Card> </List.Item> )} /> ); }; FollowList.propTypes = { header: PropTypes.string.isRequired, data: PropTypes.array.isRequired, }; export default FollowListimport React, { useState } from 'react'; import PropTypes from 'prop-types'; import Link from 'next/link'; import { Menu, Input, Row, Col } from 'antd'; import UserProfile from '../components/UserProfile'; import LoginForm from '../components/LoginForm'; // const SearchInput = styled(Input.Search)` // vertical-align: middle; // `; const AppLayout = ({ children }) => { const [isLoggedIn, setIsLoggedIn] = useState(false); return ( <div> <Menu mode="horizontal"> <Menu.Item> <Link href="/"><a>노드버드</a></Link> </Menu.Item> <Menu.Item> <Link href="/profile"><a>프로필</a></Link> </Menu.Item> <Menu.Item> <Link href="/signup"><a>회원가입</a></Link> </Menu.Item> <Menu.Item> <Input.Search enterButton style={{ verticalAlign: 'middle' }}/> </Menu.Item> </Menu> <Row gutter={8}> <Col xs={24} md={6}> {isLoggedIn ? <UserProfile setIsLoggedIn={setIsLoggedIn}/> : <LoginForm setIsLoggedIn={setIsLoggedIn}/>} </Col> <Col xs={24} md={12}> </Col> <Col xs={24} md={6}> <a href="http://google.com" target="_blank" rel="noreferrer noopener">메인화면</a> </Col> </Row> </div> ); }; AppLayout.propTypes = { children: PropTypes.node.isRequired, }; export default AppLayout;import React, { useState, useCallback, useMemo } from 'react'; import { Form, Input, Button } from 'antd'; import Link from 'next/link'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import useInput from '../hooks/useInput'; // const ButtonWrapper = styled.div` // margin-top: 10px; // `; // const FormWrapper = styled(Form)` // padding: 10px; // `; function LoginForm({ setIsLoggedIn }) { const [id, onChangeId] = useInput(''); const [password, onChangePassword] = useInput(''); // const style = useMemo(() => ({ marginTop: 10 }), []); const onSubmitForm = useCallback(() => { console.log(id, password); setIsLoggedIn(true); }, [id, password]); return ( <Form onFinish={onSubmitForm} style={{ padding: '10px' }}> <div> <label htmlFor="user-id">아이디</label> <br /> <Input name="user-id" value={id} onChange={onChangeId} required /> </div> <div> <label htmlFor="user-password">비밀번호</label> <br /> <Input name="user-password" type="password" value={password} onChange={onChangePassword} required /> </div> <div style={{ marginTop: '10px' }}> <Button type="primary" htmlType="submit" loading={false}>로그인</Button> <Link href="/signup"><a><Button>회원가입</Button></a></Link> </div> </Form> ); } LoginForm.propTypes = { setIsLoggedIn: PropTypes.func.isRequired, } export default LoginForm;/* eslint-disable react/jsx-no-undef */ /* eslint-disable no-unused-vars */ import React from 'react'; import Head from 'next/head'; import AppLayout from '../components/AppLayout'; import NicknameEditForm from '../components/NicknameEditForm'; import FollowList from '../components/FollowList'; const Profile = () => { const followerList = [{ nickname: 'test1' }, { nickname: 'test2' }, { nickname: 'test3' }]; const followingList = [{ nickname: 'test1' }, { nickname: 'test2' }, { nickname: 'test3' }]; return ( <> <Head> <title>내 프로필 | NodeBird</title> </Head> <AppLayout> <NicknameEditForm/> <FollowList header="팔로잉 목록" data={followingList}/> <FollowList header="팔로워 목록" data={followerList}/> </AppLayout> </> ); }; export default Profile;
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
antd 5버전 부터는 css import가 필요 없는거 같습니다
import React from "react"; import PropTypes from "prop-types"; import Link from "next/link"; import { Menu } from "antd"; const AppLayout = ({ children }) => { return ( <div> <Menu mode="horizontal"> <Menu.Item> <Link href="/">노드버드</Link> </Menu.Item> <Menu.Item> <Link href="/profile">프로필</Link> </Menu.Item> <Menu.Item> <Link href="/signup" legacyBehavior> <a>회원가입</a> </Link> </Menu.Item> </Menu> {children} </div> ); }; AppLayout.propTypes = { children: PropTypes.node.isRequired, }; export default AppLayout;이렇게만 해도 디자인 잘 적용 됩니다!
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
(질문X 제보O) antd 최신버전에서 리스트 문법이 변경된듯 합니다
PostCard 컴포넌트에서 댓글목록 부분을 강의에 나와있는 대로 작성하면 파라미터가 undefined라고 에러가 뜨더군요 코드는 아무리 살펴봐도 틀린 부분이 없어서 package.json 보고 하나하나 지웠다 깔았다 하면서 검사했는데 antd를 강의랑 같은 버전으로 깔면 에러가 안 나오더군요. (대신 디자인이 하나도 적용이 안됩니다)
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Log out 이슈
middlewares까지 구현하고, 로그아웃 router에 isLoggedIn 필터링 해서 실행했는데 로그아웃을 누를때마다 '로그인이 필요합니다.' 라는 Message를 받습니다.로그인하는 과정이 문제라는 가설을 세워서 passport 세팅도 확인했고, login route도 확인했습니다. 하지만 로그아웃 했을때 같은 에러 메세지를 받았습니다.middlewares를 적용 안하고 terminal 확인을 했을때 이런 메시지를 받았습니다.req.logout에 콜백함수가 필요하다고 해서router.post('/logout', (req, res) => { req.logout((err)=>{ console.error(err) }); req.session.destroy(); res.send('ok'); });logout router를 이렇게 바꾸고 로그아웃 해봤는데, LOG_OUT_SUCCESS는 뜨는데, 서버가 crushed 됬다고 하고, 그 후 로그인이 안됩니다.음..혹시 이 부분 외에 어딜 더 체크해봐야 하는지 조언 해주실 수 있을까요?항상 좋은 강의에 감사합니다 :)
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
우분투에서 빌드 시 오류가 생깁니다..
back과 front 둘다 우분투 서버 연결 해놓고,node -v / npm -v 해서 버전 확인도 했습니다.기본 node는 버전이 19이고 우분투에 설치한건 14버전입니다.front에서 빌드 하려는데npm ERR! missing script: buildnpm ERR! A complete log of this run can be found in:npm ERR!/home/ubuntu/.pm/_logs/2023-05-19714_54_17_6527-debug. log이런 오류의 원인은 뭘까요.. ㅠㅠ몇시간동안 방법을 찾지못해서 글남깁니다 ㅠㅠ제 깃허브 주소도 남깁니다..+레파지토리는 미리 생성해둔거라서 그냥 여기있는 git init을 하지 않고, 주소를 그대로 사용하여 clone 했는데도 이것도 문제가 될까요?https://github.com/nuring9/next.js-redux-saga-SSR-nodebird.git
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
로그인을하면 'Missing credentials' 이 뜹니다
회원가입을 끝내고 똑같이 이메일과 비밀번호 입력하고 로그인을 하면 401에러가 뜹니다.그래서 routes/user.js에서 info값을 콘솔로 보니 이렇게 info값이 들어오더라구요제 코드는 이렇습니다.< local.js ><routes/user.js><passport/index.js>에러를 인터넷에 서칭해보니까 local.js 안에new LocalStrategy 안에 usernameField와 passwordField를 제대로 값이 바인딩 됐는지 여부를 보라고 되어있는데 틀린게 없어보입니다...어떤걸 잘못한걸까요 ㅠㅠ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
Application > Cookies 에 front에만 저장이 안 돼요...
안녕하세요 제로초님,Application > Cookies 에 sid가 front에만 저장이 안 되는 문제가 있습니다. sid가 back에는 저장이 됩니다.제가 아직 쿠키와 세션 이해가 부족한 건지.. axios config에 뭔가 문제가 있는건지,,, 구글링 해봐도 별 거 안 나오고.. 제가 이미 다 한 거고...https://stackoverflow.com/questions/71036779/cookies-sent-from-backend-but-not-set-correctly-on-frontendhttps://stackoverflow.com/questions/72105765/axios-doesnt-create-a-cookie-even-though-set-cookie-header-is-therefront: config/config.tsimport axios from "axios"; import { backUrl } from "../../config/config"; axios.defaults.baseURL = backUrl; axios.defaults.withCredentials = true; const instance = axios.create({ baseURL: backUrl, // cors withCredentials: true, // cookie (user info) }); // instance.interceptors.request.use(function (instance) { // const kakao_authorization = localStorage.getItem("kakao_authorization"); // const Token = localStorage.getItem("Authorization"); // const Retoken = localStorage.getItem("refresh-Token"); // instance.headers["Authorization"] = Token; // instance.headers["refresh-Token"] = Retoken; // instance.headers["kakao_authorization"] = kakao_authorization; // return instance; // }); export default instance; front - Network 탭front - Application > Cookies 탭back: app.jsconst express = require('express'); const morgan = require('morgan'); const cors = require('cors'); const session = require('express-session'); const cookieParser = require('cookie-parser'); const passport = require('passport'); const dotenv = require('dotenv'); const path = require('path'); const hpp = require('hpp'); const helmet = require('helmet'); const userRouter = require('./routes/user'); const postRouter = require('./routes/post'); const postsRouter = require('./routes/posts'); const db = require('./models'); const passportConfig = require('./passport'); dotenv.config(); const app = express(); db.sequelize.sync() .then(()=>{ console.log('db 연결 성공'); }) .catch(console.error); passportConfig(); app.use(express.json()); // axios로 data보낼 때 app.use('/', express.static(path.join(__dirname, 'uploads'))); // multipart form data app.use(express.urlencoded({ extended: true })); // 일반 form 일 때에는 url encoded로 받음 if (process.env.NODE_ENV === 'production') { app.use(morgan('combined')); app.use(hpp()); app.use(helmet({ contentSecurityPolicy: false })); app.use(cors({ origin: 'http://shinyoungyou.com', credentials: true, })); } else { app.use(morgan('dev')); app.use(cors({ origin: true, credentials: true, })); } app.use(cookieParser(process.env.COOKIE_SECRET)); app.use(session({ saveUninitialized: false, resave: false, secret: process.env.COOKIE_SECRET, cookie: { httpOnly: true, secure: false }, domain: process.env.NODE_ENV === 'production' && '.shinyoungyou.com' })); app.use(passport.initialize()); app.use(passport.session()); app.get('/', (req, res) => { res.send('Express + TypeScript Server'); }); app.post('/api/post', (req, res) => { res.send('this is post http method'); }); app.use("/user", userRouter); app.use("/post", postRouter); app.use("/posts", postsRouter); module.exports = app; back - Application > Cookies 탭
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
node. app.js 실행시 error 발생.
안녕하세요!models 구성하고, app.js 로 db import도 다 했는데, node app.js 실행하면 다음과 같은 에러 메세지가 뜹니다.Post.belongsToMany called with something that's not a subclass of Sequelize.Modelmodels에서 user, hashtag export 가 잘못된거 같아서 다 확인했는데, 문제를 못찾았습니다.어떤 부분이 잘 못된걸까요?감사합니다.
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
로그인시 LOG_IN_REQUEST 부분
이와 비슷한 문제가 많아서 참고해서 복붙도 해보면서 했는데도 해결되지 않아질문 남깁니다. 로그인 버튼을 누르면 리덕스 데브툴에이와 같이 나오고 saga가 연결이 되지 않는 거 같아요(코드 첨부)sagas/index.jsimport { all, fork } from 'redux-saga/effects'; // import postSaga from './post'; import userSaga from './user'; export default function* rootSaga() { console.log('rootSaga') yield all([ fork(userSaga), // fork(postSaga), ]); } sagas/user.jsimport { all } from "axios"; import { delay, fork, put, takeLatest } from "redux-saga/effects"; import { LOG_IN_FAILURE, LOG_IN_REQUEST, LOG_IN_SUCCESS, LOG_OUT_FAILURE, LOG_OUT_REQUEST, LOG_OUT_SUCCESS } from "../reducers/user"; // function logInApi(data) { // // 비동기 처리 함수는 일반함수로 정의 // return axios.post("api/login", data); // } // function logOutApi() { // // 비동기 처리 함수는 일반함수로 정의 // return axios.post("api/login"); // } function* logIn(action) { console.log('login') 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, data: err.response.data, }); } } function* logOut() { console.log('login') try { // const result = yield call(logOutApi); //요청의 결과 yield delay(1000); yield put({ type: LOG_OUT_SUCCESS, }); } catch (err) { console.error(err); yield put({ type: LOG_OUT_FAILURE, data: err.response.data, }); } } function* watchLogIn() { console.log('watchLogIn') yield takeLatest(LOG_IN_REQUEST, logIn); } function* watchLogOUT() { console.log('watchLogOUT') yield takeLatest(LOG_OUT_REQUEST, logOut); } export default function* userSaga() { yield all([ fork(watchLogIn), fork(watchLogOUT) ]) } const g = watchLogIn(); console.log(g.next())sagas/posts.js import { all } from "axios"; import { delay, fork, put, takeEvery } from "redux-saga/effects"; // function addPostsApi(data) { // // 비동기 처리 함수는 일반함수로 정의 // return axios.post("api/login", data); // } function* addPosts(action) { console.log('addPosts') try { // const result = yield call(addPostsApi, action.data); //요청의 결과 yield delay(1000); yield put({ type: "ADD_POST_SUCESS", data: action.data, }); } catch (err) { yield put({ type: "ADD_POST_FAILURE", data: err.response.data, }); } } function* watchAddPost() { yield takeEvery("ADD_POST_REQUEST", addPosts); } export default function* postSaga() { yield all([fork(watchAddPost)]); } reducers/index.jsimport { HYDRATE } from "next-redux-wrapper"; import { combineReducers } from "redux"; import user from "./user"; import post from "./post"; // (이전 상태, 액션) => 다음 상태 const rootReducer = combineReducers({ index: (state = {}, action) => { switch (action.type) { case HYDRATE: console.log("HYDRATE", action); return { ...state, ...action.payload }; default: return state; } }, user, post, }); export default rootReducer; reducers/user.jsexport const initialState = { isLoggingIn: false, //로그인 시도(로딩)중 isLoggedIn: false, // 로그인된 상태 isLoggingOut: false, // 로그아웃 시도(로딩)중 me: null, //내정보 signUpData: {}, loginData: {}, }; 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 loginRequestAction = (data) => ({ type: LOG_IN_REQUEST, data }); export const logOutRequestAction = () => ({ type: LOG_OUT_REQUEST, }); const user = (state = initialState, action) => { switch (action.type) { case LOG_IN_REQUEST: console.log('reducer login') return { ...state, isLoggingIn: true, }; case LOG_IN_SUCCESS: return { ...state, isLoggingIn: false, isLoggedIn: true, me: {...action.data, nickname: 'skylove1004'}, }; case LOG_IN_FAILURE: return { ...state, isLoggingIn: false, isLoggedIn: false, }; case LOG_OUT_REQUEST: return { ...state, isLoggingOut: true, }; case LOG_OUT_SUCCESS: return { ...state, isLoggingOut: false, isLoggedIn: false, me: null, }; case LOG_OUT_FAILURE: return { ...state, isLoggingOut: false, }; default: return state; } }; export const userState = (state) => state.user; export default user;reducers/post.jsexport const initialState = { mainPosts: [ { id: 1, User: { id: 1, nickname: "skyblue5030", }, content: "첫 번째 게시글 #express #react", Images: [ { src: "https://bookthumb-phinf.pstatic.net/cover/137/995/13799585.jpg?udate=20180726", }, { src: "https://gimg.gilbut.co.kr/book/BN001958/rn_view_BN001958.jpg", }, { src: "https://gimg.gilbut.co.kr/book/BN001998/rn_view_BN001998.jpg", }, ], Comments: [ { User: { nickname: "nero", }, content: "우와 개정판이 나왔군요~", }, { User: { nickname: "hero", }, content: "얼른 사고싶어요~", }, ], }, ], imagePaths: [], //이미지업로드시 추가됨 postAdded: false, }; const ADD_POST = "ADD_POST"; export const addPost = { type: ADD_POST, }; const dummyPost = { id: 2, content: "더미데이터입니다.", User: { id: 1, nickname: "제로초", }, Images: [], Comments: [], }; // 데이터를 먼저 구성, 화면은 작성한 데이터나 데이터 변경을 기준으로 구성 // 데이터 구조는 서버측과 합의해서 구성해야 나중에 수정할 일이 없음 const post = (state = initialState, action) => { switch (action.type) { case 'ADD_POST_SUCCESS': return { ...state, mainPosts: [dummyPost, ...state.mainPosts], // 데이터을 앞에 추가해서 게시글이 위로 올라가게 postAdded: true, }; default: return state; } }; export const postState = (state) => state.post; export default post; store/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"; const loggerMidddleware = ({ getState }) => (next) => (action) => { console.log({ action, getState }); return next(action); }; const configureStore = () => { const sagaMiddleware = createSagaMiddleware(); const middlewares = [sagaMiddleware, loggerMidddleware]; 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; }; const wrapper = createWrapper(configureStore, { debug: process.env.NODE_ENV === "development", }); export default wrapper;
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
로그아웃 하고 로그인 할 때 좋아요 기능 실행되는 문제
안녕하세요 제로초님! 게시글 좋아요 부분 듣고 다시 작성해보고 있는데처음 로딩 시에는 좋아요 버튼 눌렀을 때 로그인 하지 않은 상태면 에러 메시지가 잘 나오는데 ( 로그인 안 하면 좋아요 못 누르게)첫 번째 문제는 로그인 했다가 로그아웃을 한 뒤에 좋아요 버튼을 누르면 화면에는 변화가 없지만 리덕스에는 LIKE_POST_REQUEST -> LIKE_POST_SUCCESS가 되어서 이전에 로그인했던 사용자의 아이디가 LIKERS에 들어갑니다.// middlewares.js exports.isLoggedIn = (req, res, next) => { if (req.isAuthenticated()) { next(); } else { res.status(401).send('로그인이 필요합니다.'); } }여기 middlewares에 콘솔을 띄어보니 로그인 했다가 로그아웃을 한 상태에서 버튼을 누르면 401에러 부분이 아니라 if ( req.isAuthenticated()) 부분이 실행됩니다.!! // 에러 메시지 출력부분 PostCard.js// PostCard.js const { removePostLoading, likePostError } = useSelector((state) => state.post); useEffect(() => { if (likePostError) { alert(likePostError); } }, [likePostError]);// routes/user.js - 로그아웃 부분router.post('/logout', isLoggedIn, (req, res, next) => { req.logout((err) => { if (err) { return next(err); } }); req.session.destroy(); res.send('ok'); });
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
서버 요청 시 request cookie 가 2개가 생성이 됩니다..
안녕하세요 제로초님!최근 다시한번 노드버드를 참고하여 따로 사이드 프로젝트를 진행중에 있습니다.진행은 nginx 설정까지 마무리 하고 배포까지 성공적으로 진행이 되었습니다. 실제 사용도 했었고, 어제까지만 해도 큰 문제가 없었습니다. 다만 오늘 이미지를 로드해오는 과정에서 gateout 에러가 좀 발생하는것 같아 방법을 찾아보면서 pm2의 ecosystem.config.js 설정 등을 하다가 잘 안되는것 같아 다시 지우고 재 실행을 했습니다.실행을 다시 하니 로그인은 유지가 되는데, 데이터를 가져오려니 401 인증 에러가 발생하여 질문 드립니다..오류가 뜨는 이유가 무엇일까 생각했었는데, 우선 쿠키가 관련이 있겠다 생각해서 application - cookie 탭에서 확인을 해봤습니다.실제 도메인 주소, secure 설정까지 다 맞춰서 들어와있음을 확인했고, 그렇기에 새로고침을 했을 시 로그인은 유지가 되었습니다.네트워크 쪽을 살펴보니분명 쿠키가 같이 request 되고 있었습니다. 근데 이상하게도 쿠키가 application 에 저장된 문자와 다름을 확인했고 request 의 cookie 탭을 확인해보니이렇게 2가지 도메인으로 쿠키가 생성되어 전달되고 있음을 확인했습니다.하나는 api. 가 붙어있었고 하나는 실제 로그인을 유지시켜주는 도메인이었습니다.계속 고민을 해봐도 왜 request 에 위와같이 2개의 쿠키가 전달이 되는지 이유를 알 수 없어서 질문드립니다..도메인은 강의처럼 .주소 형식으로 했습니다.백엔드 nginx.conf 입니다프론트 nginx.conf 입니다프론트 pm2 list 입니다백엔드 pm2 list 입니다.답변 부탁드리겠습니다 ㅜㅜ...
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
백엔드 https 설정
안녕하세요 제로초님 혹시 백엔드도 /etc/nginx/nginx.conf 이것만 따로 설정해주고 /etc/nginx/sites-enabled/default 이건 프론트랑 똑같이 설정해주면 되는건가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
nginx + https 적용하기 강의중
sudo service nginx start 할때 마다 Job for nginx.service failed because the control process exited with error code.See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.이에러가 뜹니다https://www.zerocho.com/category/NodeJS/post/5ef450a5701d8a001f84baeb 이블로그 보면서 했습니다 ㅠㅠ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
이미지 업로드 할때 한글이름 파일 500에러
안녕하세요 제로초님 이미지 업로드할때 한글이름으로 된 이미지를 업로드하면 화면에 보여지고 s3에도 업로드는 되는데 post할때는 500에러가 나네요 ㅠㅠ 백엔드에 encodeURIComponent하고 normalize("NFC") 까지 써봤는데 고쳐지지가 않아서 문의 남깁니다 ㅠㅠconst upload = multer({ storage: multerS3({ s3: new AWS.S3(), bucket: "react-sansbook-aws", key(req, file, cb) { cb( null, `original/${Date.now()}_${path.basename( encodeURIComponent(file.originalname) )}` ); }, }), limits: { fileSize: 20 * 1024 * 1024 }, // 20MB
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
npm run dev 에러
script에서 dev를 못 찾는 다고 해서 scripts에서 s가 문제인가 해서 script 로 바꿨는데 이게 아닌가 봅니다 어떻게 하나요?{ "name": "front", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "next" }, "author": "Seung Won", "license": "ISC" }npm ERR! npm ERR! To see a list of scripts, run: npm ERR! npm run npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\USER\AppData\Local\npm-cache\_logs\2023-04-29T14_33_46_373Z-debug-0.log PS C:\Users\USER\Desktop\1080\new react\front> npm run dev npm ERR! Missing script: "dev" npm ERR! npm ERR! To see a list of scripts, run: npm ERR! npm run npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\USER\AppData\Local\npm-cache\_logs\2023-04-29T14_34_59_051Z-debug-0.log
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
swr ssr 관련 질문드립니다.
getServersideProps에서 작성한 api는 ssr이든 csr이든 무조건 실행되는데 ssr시에만 실행하고 csr에서는 막을 방법이 없을까요?페이지에서 컴포넌트로 props 넘기지 않고 swr 설정해서 초기에 ssr은 잘 되는데 그 이후 클릭으로 csr 방식으로 다른페이지 이동했다가 다시 돌아왔을때swr 사용중이고 캐싱중임에도 불구하고 getServersideProps에서 작성한 api가 매번 호출되어 25.json?id=25이런식으로 데이터 받아와서 리렌더링되는데 막을 방법이 없을까요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
람다강의에서 런타임 설정 편집부분
안녕하세요 제로초님람다 런타임 설정 편집부분 화면이 좀 바뀐거같아서 질문 남깁니다! 저는 이렇게 보입니다일단 index를 exports로 바꿨는데 이대로 그냥 저장하고,진행했더니 thumb파일도 안만들어지고 이미지도 평소에 잘뜨던게 undefined로 떠서 문제를 찾아보고 있었습니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
lamda폴더
안녕하세요 제로초님 저는 폴더 구조를 한 폴더안에 프론트 백엔드 담겨있지않고 다 따로 두고 있는데 그럴경우에 람다도 따로 보관해도 되는지 궁금합니다 아니면 지금 임시로 백엔드 폴더 안에 두고 진행하고있는데 괜찮은지 궁금합니다!