월 17,600원
5개월 할부 시다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
서버사이드 랜더링시 초기화 오류.. 서버포트 80?
안녕하세요.. 서버사이드 랜더링 이후에 데이터가 계서 InitialState로 덮어쓰길래.. 어떤 문제인지 확인해보기위해 로그를 찍어주니 80번 포트를 바라보면서 오류가 나더라구요.. 왠지 api 호출시에 포트번호를 자동으로 80으로 설정해주는 거 같은데... 노드버드는 프록시서버가 아니라 CORS 설정을 벡엔드에서 해주는 경우여서 알기가 어렵네요.. 혹시 이런경우 서버사이드 랜더링 서버포트를 따로 설정해야하나요? (현재 next start로 개발서버 돌리고 있으며, 3000번 포트) 참고로 axios로 api 서버(8080포트서버) 를 next.config.js 에서 rewrites 로 프록시서버설정을 통해 사용하고 있습니다.
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
jwt 인증 질문..
1. 쿠키는 모든 http 요청메시지에 포함되어서 보내지게 되는데 jwt 토큰을 쿠키에 보관한다면 매 요청마다 jwt토큰값이 서버로 넘어가는건데 그렇다면 왜 따로 쿠키에 담긴 jwt 토큰값을 꺼내서 http request header의 authorization속성에 담아 보내는 것인지 궁금합니다 2. 1번질문과 연관된 질문입니다 Cookie의 HttpOnly 속성을 설정하게되면 document.cookie와 같은 자바스크립트로 쿠키를 조회하는 것을 막고 서버로 Http request 요청을 보낼때만 쿠키를 전송하는 걸로 알고 있는데요 쿠키 조회를 막는다면 요청헤더에 authorization 속성값으로 jwt 토큰을 넣어줄 때 쿠키에 담아둔 jwt 토큰값 조회가 필요할 거같은데 어떻게 꺼내서 사용하는 건가요? 혹시 개발코드는 접근이 가능하고 브라우저에서만 쿠키에 접근을 막는것인가요?? 3. 자동 로그아웃처리를 구현할때 토큰의 만료기한을 지정하는것인가요 아니면 쿠키의 만료기한을 지정하는 것인가요?? 4. 로그인유지라는 기능을 구현한다면 서버에서 jwt 토큰의 만료기한을 지정하지 않는건가요?? 그렇게되면 로그아웃 api 콜 하지 않고 클라이언트에서 쿠키만 삭제하였을때 서버에서 보관하는 리프래쉬 토큰을 어느시점에 지우게되는지 궁금합니다.
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
상태관리 라이브러리 트렌드
안녕하세요. 강의 토대로 이것저것 만들어보고 있습니다. 올려주신 리액트쿼리 버전으로 따라하다가 클라이언트 상태관리도 필요한것 같아서 강의에 나온대로 NPM트렌즈에서 어떤게 잘나가나 검색을 해봤습니다.. 리덕스나 모백스등 인기 상태관리 라이브러리 그래프가 급격한 하락세던데 혹시 2022년에 새로운 트렌드를 주도하는 라이브러리가 나왔나요? context-api 얘기는 많이 들어봤는데 npm트렌즈에선 검색이 잘 안되네요.. 리덕스-리덕스 사가를 그냥 쓸까 하는데 선생님이 Next와 연동이 매끄럽지 않다고 하신부분도 있고 리덕스로 하다보니 초기 코딩량이 많은데다가.. 최근 이용자수도 급격히 떨어지는것 같아서 궁금합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
배포 방법에 대한 질문드립니다.
1. 제가 알기로 무중단 배포는 새로 작성한 코드를 추가로 배포할 때, 그 파일을 가져오고 다시 실행하는 동안 끊기지 않게 해주는 것으로 알고있습니다. 그런데 강의에서 ec2는 해당 우분투 shell을 나가면 자동으로 종료되므로 pm2를 이용해 무중단 배포를 하시는 것을 봤습니다. ec2는 끊기지 않기 위해서라도 무조건 무중단 배포를 해야하는 걸까요? 2. 노드버드 강의에서 배운 내용을 토대로 배포 과정을 공부하기 위해 작은 프로젝트를 혼자 진행하고 있는데, aws에도 ec2, s3, elasticbeanstalk등 다양한 옵션이 있고, 이걸 무중단 배포를 해야하느냐, 한다면 pm2를 이용할지 docker와 nginx를 이용할지 등.. 옵션이 많아 어떤 것을 선택해야할지 고민입니다. 제가 원하는건 단순히 프론트 서버를 aws에 배포해서 언제든 실제 주소로 잘 들어가지고, github action을 이용하여 깃허브에 push했을 때 자동으로 테스트 후 배포되는 ci/cd를 구축하는 것 까지가 목표입니다. 현재 스택은 nextjs가 아닌 그냥 리액트입니다. 무중단 배포를 해보고는 싶지만 지금은 고려대상이 아닙니다. 이때 aws에서 어떤 옵션을 선택해야 할지, 무중단 배포를 꼭 해야하는지 등, 가장 알맞은 배포 방법이 어떤 것일지 조언을 구하고 싶습니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
styled-componens 가 antd 의 스타일일 오버라이딩하지 못하는데 왜그럴까요?
분명 ButtonWrapper 의 martion-top 속성은 잘 먹었는데왜 FormWrapper 의 padding:10px 속성이 안 먹는지 모르겠습니다.. 제가 콘솔로 확인해본 결과 스타일이 들어간 건 맞는데 .ant-form 자체의 padding:0 속성이 더 우세더라구요그걸 체크해제해주면 제가 준 10px 속성이 들어갑니다.어떤 것이 문제일까요? 아래가 styled 로 준 스타일이 들어간건 맞는데 antd 의 속성보다 밀리는 것 같습니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
문자열 배열 콤마 단위 특정 문자열 삭제하기
제로초님 강의 듣고 혼자 개인 프로젝트 하고 있는데 제가 백단에서 이미지를 json으로 get 요청해서 가져왔습니다. 그리고 이미지를 <img src={value.image} /> 이런 식으로 가져왔는데, 이미지가 여러 개 인것도 있습니다. console.log(value) 1. http://localhost:8000/upload/image/1, 2. http://localhost:8000/upload/image/2,http://localhost:8000/upload/image/3,http://localhost:8000/upload/image/4, 3. http://localhost:8000/upload/image/5, 4. http://localhost:8000/upload/image/6, 5. http://localhost:8000/upload/image/7, http://localhost:8000/upload/image/8, http://localhost:8000/upload/image/9, 여러 개 있는 이미지는 현제 (,)와 여러 이미지가 짬뽕으로 묶여서 있어서 2,5 번은 지금 화면에 액박으로 나오고 있습니다. 그래서 제가 좀 생각 해 보았는데 repllace, substring 다 생각 해 보았는데, (,) 단위로 맨 첫번쨰 만 보이고 뒤에 있는 것들은 삭제 하고 싶습니다. http://localhost:8000/upload/image/2 http://localhost:8000/upload/image/7 (,) 단위로 첫 번쨰 껏만 출력하고 뒤에 있는 것들은 다 없애버리고 싶은데 어떻게 해줘야 할지 모르겠습니다..
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
앱에서 디스패치를 할경우 자동디버그로 일시중지됩니다
코드에서는 중간에 일시정지를 하는 디버그 관련코드가 전혀없는데 로그인버튼이나 로그아웃버튼 클릭시 디스패치가되면서 일시중지가 되며 자동으로 개발자도구에 react-dom 파일에 디스패치 소스부분이 띄워집니다 해결루트가 있을까요 이것때문에 진도가 나가지 않네요 ㅠㅠ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
백엔드에서 로그인 인증
우선 로그인시 setCookie잘 작동하고 로그인 성공시 connect.sid가 쿠키에 잘 저장이됩니다. 그 이후 로그인이 되어있어야 할 수 있는 기능을 사용 시 로그인이 필요하다고 합니다. 프론트에서 withCredentials 옵션은 true이고 백에서 req.signedCookies랑 req.user를 찍어보면 각각 암호화된 connect.sid와 undefined가 뜹니다. 실제로 요청헤더에도 쿠키가 잘 들어가 있고 백에서도 이 쿠키를 잘 받아옵니다. 쿠키가 백까지 잘 넘어오는데 로그인 인증이 안될 경우 어떻게 접근해야하나요?쿠키파서랑 세션에서 시크릿키 모두 process.env.COOKIE_SECRET으로 같습니다,
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
import 문법 오류
안녕하세요 다름이 아니라 .eslintrc를 적용하는 와중에 다른 js파일들에서 에러가 생겨서 문의드립니다. vscode 실행 시, js 파일 import구문에 빨간 밑줄이 쳐지면서 Parsing error: Must use import to load ES Module: /Users/seolranlee/study/react/react-nodebird/front/node_modules/eslint-scope/lib/definition.js require() of ES modules is not supported. require() of /Users/seolranlee/study/react/react-nodebird/front/node_modules/eslint-scope/lib/definition.js from /Users/seolranlee/study/react/react-nodebird/front/node_modules/babel-eslint/lib/require-from-eslint.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules. Instead rename definition.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/seolranlee/study/react/react-nodebird/front/node_modules/eslint-scope/package.json.eslint 상기와 같은 에러 설명이 뜨는데요..ㅠㅠ 스택 오버 플로우에 검색하니 비슷한 현상이 있어서 따라 해결해 보았는데도 문제현상이 해결되지않아서 직접 문의드려요. https://stackoverflow.com/questions/69554485/eslint-error-must-use-import-to-load-es-module 현재 프로젝트 내 .eslintrc 내용도 함께 전달드립니다. { "parser": "babel-eslint", "parserOptions": { "ecmaVersion": 2020, "sourceType": "module", "ecmaFeatures": { "jsx": true } }, "env": { "browser": true, "node": true, "es6": true }, "extends": [ "airbnb" ], "plugins": [ "import", "react-hooks" ], "rules": { "jsx-a11y/label-has-associated-control": "off", "jsx-a11y/anchor-is-valid": "off", "no-console": "off", "no-underscore-dangle": "off", "react/forbid-prop-types": "off", "react/jsx-filename-extension": "off", "react/jsx-one-expression-per-line": "off", "object-curly-newline": "off", "linebreak-style": "off" // "no-param-reassign": "off" } } 혹시 몰라 package.json 내용도 전달드려요. { "name": "react-nodebird-front", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "dev": "next -p 3060", "build": "next build" }, "author": "seolranlee", "license": "ISC", "dependencies": { "@ant-design/icons": "^4.7.0", "antd": "^4.16.13", "axios": "^0.24.0", "faker": "^5.5.3", "immer": "^9.0.6", "next": "^9.5.5", "next-redux-wrapper": "^7.0.5", "prop-types": "^15.7.2", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^7.2.6", "react-slick": "^0.28.1", "redux": "^4.1.2", "redux-devtools-extension": "^2.13.9", "redux-saga": "^1.1.3", "shortid": "^2.2.16", "styled-components": "^5.3.3" }, "devDependencies": { "babel-eslint": "^10.1.0", "eslint": "^8.1.0", "eslint-config-airbnb": "^18.2.1", "eslint-plugin-import": "^2.25.2", "eslint-plugin-jsx-a11y": "^6.4.1", "eslint-plugin-react": "^7.26.1", "eslint-plugin-react-hooks": "^4.2.0" } }
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
npm run build 시 멈춤현상..(?)
안녕하세요 제로초님~! aws 배포 강의를 앞두고 build를 하는 과정에서 위처럼 3/7 지점에서 멈춰서 더이상 동작하지 않는 현상이 나타납니다.. 원인이 뭘까요 ㅠㅠ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
리렌더링 질문입니다
리렌더링 설명 중 return( ) 안에 있는 것 중에서바뀐 부분만 리렌더링 된다고 말씀하셨는데어떻게 하면 바뀐 부분만 렌더링 되는지 궁금합니다무엇을 했기 때문에또는useCallback 으로 OOO 인해서 바뀐 부분만 렌더링이 된다how 설명이 궁금합니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
antDesign Warning
앤트디자인 사용 하니까 이런 경고 가 뜨는데 이건 왜뜨는 걸까요? react에서 사용할땐 이런거 없었는데 왜 이런건가요? 새로고침 할때마다 나타납니다. 에게 도움이 되는 질문과 도움이 되지 않는 질문이 있습니다.도움이 되는 질문을 하는 방법을 알려드립니다.https://www.youtube.com/watch?v=PUKOWrOuC0c0. 숫자 0부터 시작한 이유는 1보다 더 중요한 것이기 때문입니다. 에러가 났을 때 해결을 하는 게 중요한 게 아닙니다. 왜 여러분은 해결을 못 하고 저는 해결을 하는지, 어디서 힌트를 얻은 것이고 어떻게 해결한 건지 그걸 알아가셔야 합니다. 그렇지 못한 질문은 무의미한 질문입니다.1. 에러 메시지를 올리기 전에 반드시 스스로 번역을 해야 합니다. 번역기 요즘 잘 되어 있습니다. 에러 메시지가 에러 해결 단서의 90%를 차지합니다. 한글로 번역만 해도 대부분 풀립니다. 그냥 에러메시지를 올리고(심지어 안 올리는 분도 있습니다. 저는 독심술사가 아닙니다) 해결해달라고 하시면 아무런 도움이 안 됩니다.2. 에러 메시지를 잘라서 올리지 않아야 합니다. 입문자일수록 에러메시지에서 어떤 부분이 가장 중요한 부분인지 모르실 겁니다. 그러니 통째로 올리셔야 합니다.3. 코드도 같이 올려주세요. 다만 코드 전체를 다 올리거나, 깃헙 주소만 띡 던지지는 마세요. 여러분이 "가장" 의심스럽다고 생각하는 코드를 올려주세요.4. 이 강좌를 바탕으로 여러분이 응용을 해보다가 막히는 부분, 여러 개의 선택지 중에서 조언이 필요한 부분, 제 경험이 궁금한 부분에 대한 질문은 대환영입니다. 다만 여러분의 회사 일은 질문하지 마세요.5. 강좌 하나 끝날 때마다 남의 질문들을 읽어보세요. 여러분이 곧 만나게 될 에러들입니다.6. 위에 적은 내용을 명심하지 않으시면 백날 강좌를 봐도(제 강좌가 아니더라도) 실력이 늘지 않고 그냥 코딩쇼 관람 및 한컴타자연습을 한 셈이 될 겁니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
next js pm2 배포 관련 질문이 있습니다.
현재 배포중인 next js 서비스에 수정사항이 생겨서 새로 코드를 받은 후 npm run build를 할 때, build를 하는 기간 동안 서비스 접속을 하면 500 Error: Cannot find module 가 먼저 나오고, 새로고침을 계속 할 시에 404 Application error: a client-side exception has occurred (see the browser console for more information). 해당 오류가 나옵니다. build가 끝나면 서비스 접속은 되지만 pm2 reload를 하기 전까지는 아래와 같이 매니패스트 파일을 불러오지 못하고, pm2 reload를 하고 나면 정상적으로 서비스가 작동합니다. 해당 사항을 해결하려면 어떻게 해야할지 궁금합니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
리액트 리랜더링의 과정에 관한 질문
안녕하세요, 강의 잘 듣고 있습니다. 리액트 노드버드 sns 강좌 중 "리랜더링 이해하기" 편을 보면서 애매하게 이해하고 있는 부분이 있어 확실하게 하고자 질문을 남깁니다. 크게 두가지 질문인데요, 1. 이 사진은 강의 중 로그인 컴포넌트를 만드는 코드입니다. class명이 'number2'인 태그에 loading이라는 prop을 전달하고 있습니다. 나중에 로그인 정보를 입력하고 이 로그인 버튼을 눌렀을때 axios와 같은 방법으로 서버에 요청을 하고 그 결과값을 받는 동안 loading props를 true로 바꾸는 식으로 진행이 되겠죠? 제가 궁금한거는 이때 prop이 바뀌는 부분은 'number2' 이 부분이니까 정확이 이 부분만 리랜더링이 되는건가요? 아니면 이것을 감싸고 있는 ButtoWrapper(class명 'number1') 이 부분 전체가 리랜더링이 되는걸까요? 2. 리액트 툴을 통해서 리랜더링 되는 부분을 이렇게 색깔로 확인할 수가 있는데, 여기서 색깔로 표시되는 부분은 정확하게 리랜더링이 되고 있다는 의미일까요? 아니면 이 중에 리랜더링이 안되고 있는데 버그처럼 계속 반짝이고 있는 경우도 있는걸까요? 질문이 허접해서 죄송합니다.. 확실히 알고자 이렇게 질문남깁니다 ㅠ
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
로그인 시 대기중 오류
안녕하세요 제로초님 로그인 시 네트워크탭에서 대기중으로 계속 나오는 문제가 발생했습니다. 다른 질문들을 봤을 때, 관계성과 관련된 코드들에서 오타를 찾고, 테이블을 다시 생성하는 방법을 취해봤는데요. 다시해도 계속 대기중으로 나타나서요 ㅜ 서버 실행 중 Executing (default): CREATE TABLE IF NOT EXISTS `Users` (`id` INTEGER NOT NULL auto_increment , `email` VARCHAR(30) NOT NULL UNIQUE, `nickname` VARCHAR(30) NOT NULL, `password` VARCHAR(100) NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci; Executing (default): SHOW INDEX FROM `Users` FROM `react-nodebird` Executing (default): CREATE TABLE IF NOT EXISTS `Posts` (`id` INTEGER NOT NULL auto_increment , `content` TEXT NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `UserId` INTEGER, `RetweetId` INTEGER, PRIMARY KEY (`id`), FOREIGN KEY (`UserId`) REFERENCES `Users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY (`RetweetId`) REFERENCES `Posts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; Executing (default): SHOW INDEX FROM `Posts` FROM `react-nodebird` Executing (default): CREATE TABLE IF NOT EXISTS `Comments` (`id` INTEGER NOT NULL auto_increment , `content` TEXT NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `UserId` INTEGER, `PostId` INTEGER, PRIMARY KEY (`id`), FOREIGN KEY (`UserId`) REFERENCES `Users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, FOREIGN KEY (`PostId`) REFERENCES `Posts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; Executing (default): SHOW INDEX FROM `Comments` FROM `react-nodebird` Executing (default): CREATE TABLE IF NOT EXISTS `Hashtags` (`id` INTEGER NOT NULL auto_increment , `name` VARCHAR(20) NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; Executing (default): SHOW INDEX FROM `Hashtags` FROM `react-nodebird` Executing (default): CREATE TABLE IF NOT EXISTS `Images` (`id` INTEGER NOT NULL auto_increment , `src` VARCHAR(200) NOT NULL, `createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `PostId` INTEGER, PRIMARY KEY (`id`), FOREIGN KEY (`PostId`) REFERENCES `Posts` (`id`) ON DELETE SET NULL ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci; Executing (default): SHOW INDEX FROM `Images` FROM `react-nodebird` Executing (default): CREATE TABLE IF NOT EXISTS `PostHashtag` (`createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `PostId` INTEGER , `HashtagId` INTEGER , PRIMARY KEY (`PostId`, `HashtagId`), FOREIGN KEY (`PostId`) REFERENCES `Posts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (`HashtagId`) REFERENCES `Hashtags` (`id`) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; Executing (default): SHOW INDEX FROM `PostHashtag` FROM `react-nodebird` Executing (default): CREATE TABLE IF NOT EXISTS `Like` (`createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `PostId` INTEGER , `UserId` INTEGER , PRIMARY KEY (`PostId`, `UserId`), FOREIGN KEY (`PostId`) REFERENCES `Posts` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (`UserId`) REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; Executing (default): SHOW INDEX FROM `Like` FROM `react-nodebird` Executing (default): CREATE TABLE IF NOT EXISTS `Follow` (`createdAt` DATETIME NOT NULL, `updatedAt` DATETIME NOT NULL, `FollowingId` INTEGER , `FollowerId` INTEGER , PRIMARY KEY (`FollowingId`, `FollowerId`), FOREIGN KEY (`FollowingId`) REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (`FollowerId`) REFERENCES `Users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_general_ci; Executing (default): SHOW INDEX FROM `Follow` FROM `react-nodebird` db 연결성공 Executing (default): SELECT `id`, `email`, `nickname`, `password`, `createdAt`, `updatedAt` FROM `Users` AS `User` WHERE `User`.`email` = 'test@test.com'; Executing (default): INSERT INTO `Users` (`id`,`email`,`nickname`,`password`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?,?,?); 터미널에는 위와 같이 나오고 회원가입은 잘 되었습니다. mysql 테이블에서도 데이터가 들어온 것을 확인했습니당. 그런데 로그인을 하면 아래처럼 대기중이다가 시간지나면 저렇게 에러메세지가 나옵니다. 저는 포트번호를 5001로 한 상태입니다.
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
parms를 가져오는 과정
request 객체 안의 params에 postId가 언제 어떻게 담겨지는 건지 궁금합니다. Express 서버에서 요청을 받는 순간에 요청의 엔드포인트를 파싱해서 :postId 부분을 params로 인식하고 request 객체의 params에 초기화해주는건가요?
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
saga 제너레이터 함수에서 catch를 탔을때
function *addPost(action){ try{ yield delay(2000); const result = yield call(addPostAPI,action.data); yield put({ type:"ADD_POST_SUCCESS", data:result.data }); }catch (err) { yield put({ type:"ADD_POST_FAILURE", data:err.response.data }); } } 이런식으로 addPost 가 있고 실제로 axios 로 네트워크 요청하게하면 오류가 떠서 catch 로 갈수도 있는데 이때 보통 어떤식으로 처리하나요? reducer 에서 initialState 에는 logInError 가 있는데 UserProfile.js에서는 loginError 관련 처리가 없는것같아서요
- 해결됨[리뉴얼] React로 NodeBird SNS 만들기
안녕하세요 제로초님 nextjs로 빌더를 만들어볼까 하는데 힌트를 찾지못해 질문드립니다.
안녕하세요 제로초님. 지금 포트폴리오를 만드는김에 빌더까지 확장해서 만들어 볼까 싶은데 힌트를 찾지 못해서 질문 드립니다. 일단 각 게시판 별로 스킨을 만들어서 DB에 스킨명을 저장할 생각입니다. fs으로 스킨명의 js파일을 찾아 저장할 생각인데 문제는 해당 컴포넌트를 어떻게 import해서 가져와서 뿌려주느냐 입니다. 위 스샷처럼 list에 Basic 이라는 리스트 컴포넌트와 Thumbnail이라는 리스트 컴포넌트 2개를 가지고 있습니다. 예를들어서 notice게시판과 photo게시판 2개의 게시판이 있고, db에 게시판 설정 테이블을 만들고 notice 게시판은 리스트 스킨을 Basic으로 저장하고, photo게시판은 Thumbnail로 저장합니다. notice라는 게시판에서는 Basic 컴포넌트를 , photo라는 게시판에서는 Thumbnail컴포넌트를 불러와 사용하게 하려 하는데 제가 node를 다루기 전엔 php만 다뤄봐서 php에선 그냥 단순히 스킨명을 string으로 가져와서 include로 가져왔으면 됐는데 node에서는 어떻게 하면 따로 불러와서 사용할수있는지 궁급합니다. 혹시나 제가 만들려는 접근방식이 틀렸다면, 검색 키워드좀 부탁드리겠습니다. 감사합니다
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
무한정 back으로부터 post 데이터를 불러와요!
안녕하세요 zerocho 선생님... 다른게 아니라 이 부분에서 아주 크나큰 문제에 맞닥뜨려 작업을 못하고 있습니다. pages/index.js에서 useEffect를 사용해서 첫 페이지에 오면 로그인 유무를 판단하고 또 기존에 있던 게시글을 불러와야 하는것을 잘 아는데 문제는 로그인할 경우에는 딱 한번만 LOAD_MY_INFO_REQUEST를 잘 수행하는데 LOAD_POST_REQUEST를하는순간 무한 응답을 합니다. post man에서 받아온 데이터를 다음과 같습니다. 사실 저는 springboot를 back으로 사용하고 있는데 이유는 JPA를 좀 사용해보고 싶어서 였습니다. next.js를 아예 안쓰는것이 나앗던 것 같지만 처음에 강의를 접하고 할때는 아무것도 몰라서... 무진 후회중.. 그냥따라할걸... 하여간... 기존의 데이터도 이런 식으로 받아오는데 문제는 무한 응답을 요청하니까 도대체 어디서 잘못된건지 모르겠다는 것입니다. ㅠㅠ 제발좀 도와주십시오.;.. pages/index.js import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import AppLayout from "../components/AppLayout"; import PostCard from "../components/middleComponent/Post/PostCard"; import PostForm from "../components/middleComponent/Post/PostForm"; import { LOAD_POST_REQUEST } from "../reducers/post"; import { LOAD_MY_INFO_REQUEST } from "../reducers/user"; const Home = () => { const dispatch = useDispatch(); const { me } = useSelector((state) => state.user); const { mainPosts, hasMorePost, loadPostLoading } = useSelector((state) => state.post); useEffect(() => { console.log("useEffect in index.js") dispatch({ type: LOAD_MY_INFO_REQUEST }); // dispatch({ // type: LOAD_POST_REQUEST, // }); }, [hasMorePost, loadPostLoading]); useEffect(() => { function onScroll() { if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300){ if (hasMorePost && !loadPostLoading) { dispatch({ type: LOAD_POST_REQUEST, }); } } } window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); } }, []); console.log(mainPosts); return ( <AppLayout> {me && <PostForm />} {mainPosts.map((post) => <PostCard key={post.id} post={post}/>)} </AppLayout> ); } export default Home; reducers/post.js import shortId from 'shortid'; import produces, { produce } from 'immer'; import faker from 'faker'; import { ConsoleSqlOutlined } from '@ant-design/icons'; export const initialState = { mainPosts: [], imagePaths: [], hasMorePost: true, loadPostLoading: false, loadPostDone: false, loadPostError: null, addPostLoading: false, addPostDone: false, addPostError: null, addCommentLoading: false, addCommentDone: false, addCommentError: null, removePostLoading: false, removePostDone: false, removePostError: null, } export const generateDummyPost = (number) => Array(number).fill().map(() => ({ id: shortId.generate(), User: { id: shortId.generate(), nickname: 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_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 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_POST_REQUEST = 'REMOVE_POST_REQUEST'; export const REMOVE_POST_SUCCESS = 'REMOVE_POST_SUCCESS'; export const REMOVE_POST_FAILURE = 'REMOVE_POST_FAILURE'; export const ADD_POST_TO_ME = 'ADD_POST_TO_ME'; export const REMOVE_POST_OF_ME = 'REMOVE_POST_OF_ME'; 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: 'thelovedaejeon', // }, // Images: [], // Comments: [], // }); // const dummyComment = (data) => ({ // id: shortId.generate(), // content: data, // User: { // id: 1, // nickname: 'thelovedaejeon', // }, // }) //이전 상태를 action을 통해 다음 상태로 만들어 내는 함수 (불변성을 지키면서) const reducer = (state = initialState, action) => { return produce (state, (draft) => { switch (action.type){ 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.mainPosts = action.data.concat(draft.mainPosts); draft.hasMorePost = false; 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(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, // mainPosts, // addCommentLoading: false, // addCommentDone: true, // }; } case ADD_COMMENT_FAILURE: draft.addCommentLoading = false; draft.addCommentError = action.error; break; default: break; } }); }; export default reducer; sagas/post.js import { delay, fork, all, takeLatest, put, call} from "redux-saga/effects"; import shortId from "shortid"; import axios from 'axios'; // import Axios from 'axios'; // import qs from 'query-string'; import Cookies from 'universal-cookie'; import { ADD_COMMENT_FAILURE, ADD_COMMENT_REQUEST, ADD_COMMENT_SUCCESS, ADD_POST_FAILURE, ADD_POST_REQUEST, ADD_POST_SUCCESS, ADD_POST_TO_ME, generateDummyPost, LOAD_POST_FAILURE, LOAD_POST_REQUEST, LOAD_POST_SUCCESS, REMOVE_POST_FAILURE, REMOVE_POST_REQUEST, REMOVE_POST_SUCCESS } from "../reducers/post"; import { REMOVE_POST_OF_ME } from "../reducers/user"; const cookies = new Cookies(); function addPostAPI(data) { const accessToken = cookies.get("accessToken"); const userEmail = cookies.get("userEmail"); const newObj = { description : data, email : userEmail } return axios.post('/auth/post', newObj,{ headers:{ 'Authorization': `Bearer ${accessToken}`, "Content-Type": "application/json" } }); } function* addPost(action) { try { const result = yield call(addPostAPI, action.data); yield put({ type: ADD_POST_SUCCESS, data: { id : result.data.postId, content: action.data } }) yield put({ type: ADD_POST_TO_ME, data: result.data.postId, }) } catch (error) { console.log(error); yield put({ type: ADD_POST_FAILURE, data: error.data }) } } function loadPostAPI(data) { return axios.get('/api/posts'); } function* loadPost(action) { try { const result = yield call(loadPostAPI); console.log(result); console.log(result.data); yield put({ type: LOAD_POST_SUCCESS, data: result.data.result, }); } catch (error) { console.log(error); yield put({ type: LOAD_POST_FAILURE, data: error.data }) } } function removePostAPI(data) { return axios.post('/api/post', data); } function* removePost(action) { try { delay(1000); // const result = yield call(addPostAPI, action.data); const id = shortId.generate(); yield put({ type: REMOVE_POST_SUCCESS, data: action.data }); yield put({ type: REMOVE_POST_OF_ME, data: action.data }) } catch (error) { yield put({ type: REMOVE_POST_FAILURE, data: error.data }) } } function addCommentAPI(data) { return axios.post('/api/post/${data.postId}/comment', data); } function* addComment(action) { try { delay(1000); // const result = yield call(addPostAPI, action.data); yield put({ type: ADD_COMMENT_SUCCESS, data: action.data }) } catch (error) { yield put({ type: ADD_COMMENT_FAILURE, data: error.data }) } } function* watchAddPost(){ yield takeLatest(ADD_POST_REQUEST, addPost); // 첫번째것만 하고 싶으면 takeLeading } function* watchLoadPost(){ yield takeLatest(LOAD_POST_REQUEST, loadPost); // 첫번째것만 하고 싶으면 takeLeading } function* watchRemovePost(){ yield takeLatest(REMOVE_POST_REQUEST, removePost); // 첫번째것만 하고 싶으면 takeLeading } function* watchAddComment(){ yield takeLatest(ADD_COMMENT_REQUEST, addComment); // 첫번째것만 하고 싶으면 takeLeading } export default function* postSaga(){ yield all([ fork(watchLoadPost), fork(watchAddPost), fork(watchRemovePost), fork(watchAddComment), ]) };
- 미해결[리뉴얼] React로 NodeBird SNS 만들기
jwt 관련 질문
안녕하세요 제로초님 jwt로 구현을 해보려고 시도 중입니다!이해가 안되는 것들이 많아 질문 남깁니다 ㅠㅠ 1. 서버에서 jwt토큰을 발급할때 클라이언트에서 사용할 데이터들(이름, 주소, 프로필 이미지 등)을 jwt 토큰 페이로드에 넣어서 보내고 클라이언트에선 토큰만으로 해당 데이터를 추출하여 사용하는 방식이 맞는건가요? 2. refresh 토큰은 서버에서 별도로 저장하지만 클라이언트에서도 access 토큰이 만료되면 refresh 토큰을 보내야 해서 클라이언트에서도 별도로 저장을 해야할지 싶은데 로컬스토리지나 쿠키에 저장을 한다면 결국 탈취당할 위험이 있기에 클라이언트에서 refresh토큰을 어디에 보관해야하는지 궁금합니다. 3. XSS 공격을 막기위해 cookie에 HttpOnly 옵션으로 자바스크립트에서 접근을 못하게 막는다면 클라이언트 개발하는 코드에서도 document.cookie 로 쿠키에 접근을 못하는게 맞나요??4. 쿠키에 저장하게 된다면 모든 요청마다 헤더에 쿠키정보가 자동으로 담겨서 보내지는 걸로 알고있는데 그렇기에 쿠키보다 로컬스토리지를 사용하는 방법이 더 선호되는지 궁금합니다!