수강이 제한됩니다.
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결React로 NodeBird SNS 만들기
인피니트 스크롤링에 관한 질문입니다.
안녕하세요 :) 강의 7-11에서 LOAD_MAIN_POSTS_REQUEST가 중복 요청이 되어 useRef를 사용하여 해결을 하셨는데, 해시태그 페이지에서도 같은 문제가 발생해서 git에 올려놓으신 구버전(old branch) 소스코드나 강좌에서 참고하려했지만 useRef를 적용하는 것에 대한 설명이 없었습니다. 그래서 배운 내용을 스스로 적용해보던 와중에 문제가 생겨서 이렇게 질문을 남깁니다. 먼저 제가 수정한 코드는 아래와 같습니다. (제가 코드를 첨부하는데 제가 잘 다루지 못해서 그런지 javascript로 설정을 해도 계속 글씨가 제대로 보이지 않아서 굵은 글씨로 변경했습니다ㅜㅜ) 해시태그 페이지에서도 마찬가지로 LOAD_HASHTAG_POSTS_REQUEST 가 중복요청이 돼서 일단 saga에는watchLoadHashtagPosts의 takeLatest를 throttle로 수정하여 아래와 같은 코드로 바드로 바꿨습니다. function* watchLoadHashtagPosts() { yield throttle(2000, LOAD_HASHTAG_POSTS_REQUEST, loadHashtagPosts); } 그리고 Hashtag.js 는 useRef를 사용하여 아래와 같이 바꿨습니다. import React, { useEffect, useCallback, useRef } from 'react'; import PropTypes from 'prop-types'; import { useDispatch, useSelector } from 'react-redux'; import { LOAD_HASHTAG_POSTS_REQUEST } from '../reducers/post'; import PostCard from '../components/PostCard'; const Hashtag = ({ tag }) => { const dispatch = useDispatch(); const { mainPosts, hasMorePost } = useSelector(state => state.post); const countRef = useRef([]); const onScroll = useCallback(() => { if (window.scrollY + document.documentElement.clientHeight > document.documentElement.scrollHeight - 300) { const lastId = mainPosts[mainPosts.length - 1] && mainPosts[mainPosts.length -1].id; if (!countRef.current.includes(lastId)) { if (hasMorePost) { dispatch({ type: LOAD_HASHTAG_POSTS_REQUEST, lastId, data: tag, }) } countRef.current.push(lastId); } } }, [hasMorePost, mainPosts.length, tag]); useEffect(() => { window.addEventListener('scroll', onScroll); return () => { window.removeEventListener('scroll', onScroll); } }, [mainPosts.length, hasMorePost, tag]); return ( <div> {mainPosts.map(c => ( <PostCard key={+c.createdAt} post={c} /> ))} </div> ); }; Hashtag.propTypes = { tag: PropTypes.string.isRequired, }; Hashtag.getInitialProps = async (context) => { const tag = context.query.tag; context.store.dispatch({ type: LOAD_HASHTAG_POSTS_REQUEST, data: tag, }) return { tag }; }; export default Hashtag; 그리고 버그는 아래와 같은 순서로 실행시키면 발생합니다. 1. 예를 들어 게시물이 25개 가량 있다고 가정을 합니다. 2. http://localhost:3060/hashtag/인프런 페이지에서 10개씩 게시물을 보여주는 인피니티 스크롤링을 한 번 해서 21번째 게시글의 #인프런 해시태그를 클릭합니다. 3. http://localhost:3060/hashtag/인프런 페이지가 로드될 때, 리덕스 devtools를 통해 액션어 어떻게 실행이 되는지 확인해보면 LOAD_HASHTAG_POSTS_REQUEST LOAD_HASHTAG_POSTS_REQUEST LOAD_HASHTAG_POSTS_SUCCESS LOAD_HASHTAG_POSTS_SUCCESS 이런 식으로 동작을 해서 게시물을 2번 로드해서 중복된 게시물이 보이게 됩니다. 4. console.log를 작성해서 확인해보면, 위와 같은 과정으로 실행시켰을 때 Hashtag.getInitialProps에서 뿐만 아니라 onScroll이 한 번더 실행이 돼서 dispatch로 인해 LOAD_HASHTAG_POSTS_REQUEST를 요청하는 것을 확인할 수 있었습니다. 왜 onScroll이 페이지가 새로 로드가 될 때 같이 실행이 되는지 모르겠습니다. 인피니트 스크롤링을 하지 않은 상태의 최신 10개 글 내에서 해시태그를 클릭하거나, 해당 해시태그 페이지를 새로고침하면 위와 같은 버그가 발생하지 않았고, 인피니트 스크롤링을 실행한 후 생성되는 게시물의 해시태그를 클릭하면 버그가 발생합니다.
- 미해결React로 NodeBird SNS 만들기
안녕하세요 라우팅 관련 질문드립니다
안녕하세요 아래 동적 라우팅 관련 질문한 회원입니다 계속 시도를 했지만, getServerside~ 는 gettet/setter를 반환을 했습니다. 스택오버플로우에서는 리턴값에 toJS를 넣어보라거나, 다른 방법도 제시했지만 똑같았네요 ㅠ 다른 커뮤니티에도 글을 올렸지만, 이렇다 할 해결책은 못 찾았습니다 ㅠㅠ 그래서 제가 생각을 한 것이, useRouter가 있다면, pages/[id]로 갈 때, 해당 url을 이용할 수도 있을 거 같았습니다. 실제로, 이를 사용해서 '동적 라우팅 스러운' 기능을 구현은 했습니다. 이 코드들이 next, react에 맞는 원리? (정확한 표현을 모르겠네요..) 일까요? 다시 말해, '기능 구현에만 급급한 건 아닌지' 궁금합니다. 제가 현재 프로젝트에서 다른 게시판도 이렇게 만들 거 같은데, 작성한 로직이 좋은 로직이 아니면 다시 공부해서 시도해 보려고 합니다 아래는 전체 코드구요, home화면에서 부터 게시판 클릭, 게시글 클릭해서 하는 케이스와 주소창에서 바로 id 값을 쳐서 게시글로 접속하는 케이스, 둘 다 정상적으로 읽혀집니다
- 미해결React로 NodeBird SNS 만들기
안녕하세요 동적 라우팅 관련 질문드립니다
안녕하세요 next, react, mobx, express, sequelize로 웹 개발을 하고 있습니다 게시판 기능 중에서 각 게시글을 동적 라우팅을 통해서 렌더링하려고 합니다. getInitialProps를 하면은 이 함수 안에서는 값이 잘 읽힙니다만, 이거를 동적 라우팅 컴포넌트에 props로 전달할 때 undefined가 됩니다. Post 컴포넌트가 각 게시글들을 렌더링하는 컴포넌트입니다. div안의 내용은 잘 출력되고 있습니다. ((1, 2, 3번 함수가 있는데, 각각 주석처리 해 가면서 테스트했습니다)) 1번. 콘솔창에도 getInitialProps 관련 값들은 제가 클릭한 게시글의 db값들이 나옵니다, 다만, 이것이 Post컴포넌트의 props로 전달 될떄, 값이 undefine로 나옵니다. ((getInitialProps보다는 getServerSideProps를 사용하고 싶습니다)) 2번. 콘솔 자체가 안 찍힙니다... 3번. 2번과 마찬가지입니다.. 공식문서의 dynamic routes를 찾아봐도 3번 내용으로 시작하는 거 뿐이었는데, 제가 다른 문서도 봐야 할 것들이 있을까요? 아래는 백엔드의 코드입니다 (오른쪽 req의 더 오른쪽에는 res, next가 있습니다) 아래는 mobx의 다이내믹 관련 스토어입니다
- 미해결React로 NodeBird SNS 만들기
antd 추가 질문드립니다
안녕하세요 아래 질문에 이어서 추가 질문드립니다 (컴포넌트로 변경했습니다 ㅎㅎ...알려주셔서 감사합니다 ) 아래 처럼 메뉴 버튼이 있습니다 study-logs에 마우스를 클릭하는 것이 아닌, 마우스를 올렸을 때 아래처럼 파랗게 변합니다 css의 hover 효과가 antd Menu에 내장 되 있는 거 같은데요, 이와 마찬가지로, 아래 사진도 보여드릴게요 위 사진에서는 'react' 를 클릭했을 때 파랗게 강조가 되는 효과가 있습니다 이 antd에 내장되 있는 1. hover 효과, 2. 클릭했을 시 파란색 강조 효과 같은 것들을 없애기 위해서는 어떻게 해야 하는 지 궁금합니다.
- 해결됨React로 NodeBird SNS 만들기
안녕하세요 antd 관련 질문드립니다
안녕하세요 antd를 사용해서 개인 웹 사이트를 만들고 있습니다 클릭했을 때 발생하는 antd 의 효과를 없애고 싶어서 styled-components를 사용했습니다 예를 들어서, 토글키를 클릭하면 하위 메뉴가 보이는 A버튼이 있다고 했을 때, A버튼을 눌렀을 때 파란색으로 글자색이 바뀌는 효과를 없애고 싶었습니다. 연습 삼아서 폰트 크기만 변경했는데, 변경이 되었습니다. 문제는, 토글 관련 함수들이 먹통이 되는 것입니다. antd의 효과를 없애기 위해서는, 스타일드 컴포넌트로 변경을 하는게 아니라, 그냥 처음부터, antd를 쓰지 않고, 제가 원하는 효과를 적용하면서 메뉴를 구성하는 방법 뿐일까요? 아래는 코드입니다 ( 어떤 코드냐면, 'study-logs'라는 버튼을 누르면 react, next, js 라는 하위 메뉴 버튼이 토글 형식으로 생성되는 것입니다 ) <SubMenu key="sub2" icon={<LaptopOutlined />} title="study-logs"> <Menu.Item key="5">react</Menu.Item> <Menu.Item key="6">next</Menu.Item> <Menu.Item key="7">js</Menu.Item></SubMenu>위의 코드를 아래처럼 바꾸었습니다.-- const TextSubmenu = styled('SubMenu')` font-size: 24px;` <TextSubmenu key="sub2" icon={<LaptopOutlined />} title="study-logs"> <Menu.Item key="5">react</Menu.Item> <Menu.Item key="6">next</Menu.Item> <Menu.Item key="7">js</Menu.Item></TextSubmenu>여기서 폰트사이즈를 변경한 것은, 실제css가 적용이 되는지 테스트 해 보기 위함이었고,실제로 폰트 사이즈가 변경은 되었습니다.하지만, 기존에 있던 기능들이 작동을 안 합니다.
- 미해결React로 NodeBird SNS 만들기
제로초님
제로초님 안녕 하세요 공부 하다가 궁금 한 점이 생겨 질문을 답니다. react-creact-app으로 만들었을 때 새로 고침시 서버 에러가 나지 않고 직접 처음 부터 만들었을 때는 새로 고침시 서버 에러가 나더라구여 이게 react-creact-app으로 개발을 했을 때는 기초 셋팅을 해줘서 그런 것 같은데 직접 처음 부터 만들 때는 새로 고침시 에러가 나지 않으려면 웹팩에서 historyApiFallback: true 같은 걸로 설정을 해줘야 하나요? 아니면 프록시 설정을 해줘야 하는건지 어떤 방법이 있는지 해서 질문 남깁니다
- 해결됨React로 NodeBird SNS 만들기
안녕하세요 리액트 관련 질문드립니다
리액트 강의를 듣고, 성능에 관심이 생겼습니다 1. 성능을 올리기 위해서는 React.memo, useCallback, useMemo로 할 수 있는 것 말고도 다른 방법이 있을까요? 2. 추가적으로 공부해야 하는 부분이 있을까요? 3.성능을 올린다고 했을 때, 그것을 전후 수치로 나타내는 툴이 있는 지 궁금합니다
- 미해결React로 NodeBird SNS 만들기
공부하다가 하도 해결안되어 질문 남깁니다 ㅠㅠ
안녕하세요 저는 제로초님의 강의를 통해 리액트를 공부하고있는 사람입니다. 혼자 공부하던중 도무지 해결안되는 부분이 있어서 이렇게 질문을 남깁니다. ------------------------------------------------------------------------------------ const [isChecked, setIsChecked] = useState([]); const [amllShopList, setAmllShopList] = useState([]); const handleAddRow = (e) => { //추가버튼 e.preventDefault(); const newRow = { shopNm : '', shopTel : '', shopAddr : '', shopBrn : '' } setAmllShopList([...amllShopList,newRow]); } const handleListEdit = (index, name, value) => { //내용수정 setAmllShopList(produce(amllShopList,(draft) => {draft[index][name]=value})); } const handleDelRow = (e) => { //삭제버튼(체크박스 선택한것들) e.preventDefault(); //if(isEmpty(isChecked)) return showAlert('삭제할 지점을 선택해주세요.'); // const resData = amllShopList.filter((data,i)=> !isChecked[i]); // setAmllShopList(resData); setAmllShopList(amllShopList.filter((data,i)=>!isChecked[i])); setIsChecked([]); } const handleCheckRow = (e) => { //체크박스 선택 const index = parseInt(e.target.name); isChecked[index] = !isChecked[index]; setIsChecked([...isChecked]); }; <> <div> <h5 className="mb8 mt40" style={{ display: 'inline-block', verticalAlign: 'top' }}> 지점 등록 </h5> <Button size="sml" background="gray" title="추가" width={80} height={40} buttonMarkup={true} onClick={handleAddRow} /> <Button size="sml" background="gray" title="삭제" width={80} height={40} buttonMarkup={true} onClick={handleDelRow} /> ----------렌더링하는부분-------- {amllShopList.map((row, i) => <AmllShopList key={i} rowNum={i} isChecked={isChecked[i]} onCheckRow={handleCheckRow} onEdit={handleListEdit}/> )} </tbody> </table> </> -------------------------------- ---------컴포넌트----------------------------- export const AmllShopList = ({rowNum, isChecked, onCheckRow, onEdit}) => { const handleAmllCheck = (e) => { onCheckRow(e); }; const handleInputChange = (e) => { const name = e.target.name; const value = e.target.value; onEdit(rowNum, name, value); }; return( <tr> <td><CheckBox id={'chk_row-'+rowNum} name={rowNum} checked={isChecked} onChange={handleAmllCheck}/></td> <td><Input type="text" id={'shopNm-' + rowNum} name="shopNm" onChange={handleInputChange} /></td> <td><Input type="text" id={'shopTel-' + rowNum} name="shopTel" onChange={handleInputChange} /></td> <td><Input type="text" id={'shopAddr-' + rowNum} name="shopAddr" onChange={handleInputChange} /></td> <td><Input type="text" id={'shopBrn-' + rowNum} name="shopBrn" onChange={handleInputChange} /></td> </tr> )} ----------------------------------- 원하는 형태는 추가버튼을 통해 amllShopList 배열에 객체 데이터를 추가하여 값을 입력할수있고, 체크박스를 통해 삭제할수있는 기능을 구현하는것이었으나, 현재 상황은 추가하고 데이터의 입력은 가능하지만 체크박스로 체크하여 삭제를 할경우 실제 상태값은 정상적으로 삭제가 되고있으나, 화면상으로는 가장 마지막 데이터가 지워지는것처럼 보이게 됩니다. ex) 추가버튼을 5번 눌러 객체를 5개 추가하여 3,4번째 객체를 체크하여 삭제할경우, 화면상으로는 1,2,3번 데이터가 보이게되지만 실제 데이터는 1,2,5번 데이터가 남아있는상태로 api에 들어가게됨. 해결법좀 알려주세요 ㅠㅠ
- 미해결React로 NodeBird SNS 만들기
혹시 디비 테이블을 그린다면 이게 맞는걸까요?
그리고 workbench 에 테이블이 5개생기는데 나머지 테이블은 뷰같은 개념인건가요? ㅜ 시퀄라이즈 헷갈리네요..
- 미해결React로 NodeBird SNS 만들기
좋아요 버튼 눌리는 문제... 상세페이지와 연동이 이상한 것 같습니다
깃허브 주소 https://github.com/jinne202/maplebird 질문이 조금 복잡할 수 있는데, 좋아요 버튼을 메인페이지에서 누르면 fail이 뜨면서 눌러지지 않는데, 좋아요 버튼을 상세페이지에서 누르면 (post/id) 눌러집니다. 또한 같은 상태에서 메인 페이지에 다시 돌아오면 다시 또 좋아요 버튼이 눌러집니다 ㅠㅠreducer에서 singlePost : {~~} 이 부분을 주석처리하면 메인페이지에서도 버튼이 잘 눌러지는데, 어떤 문제인지 짐작하기가 어렵습니다. consle.log로 눌릴 때 마다 post id를 찍어봤는데 메인 페이지에서도 post id를 잘 체크하고 있어서 어떤 부분에서 오류가 나는지 체크하기가 어렵습니다 ㅠㅠ 메인 페이지에서는 post.id랑 singlePost에서 id를 파악하는데 뭔가 문제가 있는건지 ㅠㅠ... 혹시 몰라서 깃 주소도 같이 올립니다...! 코드 <reducer> <saga> <back singlePost>
- 미해결React로 NodeBird SNS 만들기
const liked = post.Likers.find((v) => v.id === id) 에러문제
안녕하세요 다른사람의 글을 트윗버튼을 누르면 find함수를 못찾는다는 에러가뜹니다. 분명히 포스트내에는 Likers라는 객체가있는데 말이죠 도와주세요
- 해결됨React로 NodeBird SNS 만들기
댓글 검색 쿼리문으로
안녕하세요. 지금 현재 쿼리문으로 한번 구현을하고 있는데 아래 처럼 query를 하고싶은데 JOIN을 하는건 알겠는데 이게 무슨 JOIN을 해야지 이렇게 같이 데이터가 들어가는지 잘 모르겠네요. where: { PostId: req.params.id, }, order: [[ 'createdAt', 'ASC' ]], include: [{ model: db.User, attributes: ['id', 'nickname'], }],
- 해결됨React로 NodeBird SNS 만들기
게시글 이미지 query문으로
let post = req.body; let sql = 'INSERT INTO test(`title`, `desc`, `addFile`) VALUES(?, ?, ?)'; let params = [ post.forum_select, post.forum_title, post.forum_content, ]; if(post.post_data_imfl) { if(Array.isArray(post.post_data_imfl)) { //프론트에서 보낸 이미지가 배열이라면 await Promise.all(post.post_data_imfl.map((image) => { params.push(image); })); await conn.query(sql, params, (err, rows) => { err ? console.error(err) : res.json(rows) }) } else { await params.push(post.post_data_imfl ) await conn.query(sql, params, (err, rows) => { err ? console.error(err) : res.json(rows) }) } 제로초님 강의를 보고 그냥 쿼리를 해봤는데 제가 한거는 몇개를 올려도 첫번째 이미지만 올라가네요.... 시퀄라이즈만 되는건가요??
- 미해결React로 NodeBird SNS 만들기
fs not found 에러
마지막으로 잘 실행하고 다시 컸는데 다음과 같은 에러가 뜨는데 혹시 무슨 에러인가요..?ㅠㅠ
- 미해결React로 NodeBird SNS 만들기
오류 제보 및 lambda 관련 문제 ㅠㅠ
1. 오류 제보 간단한 오류지만...! 테스트 해보면서 이미지가 딱 두 개 올라갈때 같은 이미지가 올라가서 다시 보니 postImages.js에서 배열이 잘못 표기된 것 같습니다 https://github.com/ZeroCho/react-nodebird/blob/old/ch8/front/components/PostImages.js img src에서 하나는 [0], 하나는 [1]로 표기되어야 할 것 같습니다~~ 2. lambda를 설정해봤는데, ec2에 표기되지 않아 질문 드립니다. 처음 생성해본 코드 []때문에 수정 후 다시 git으로 받아서 한 부분 딱히 오류가 표시되는 부분은 없는데 ㅠㅠ... 왜 ec2에는 함수로 등록이 되지 않는지 모르겠습니다 ㅠㅠ sharp도 잘 설치되었는데 오류가 나서... ㅠㅠㅠ...
- 해결됨React로 NodeBird SNS 만들기
제너레이터를 직접호출하지 않아도 액션 기다렸다 실행 되는 이유는..
sagas/user.js 의 export default function* userSaga(){ yield helloSaga(); } 이함수가 pages/index.js 에서 dispatch한 액션을 받을수 있는 이유는.. ? (userSaga 제너레이터를 직접어디선가 호출하지 않아도..) _app.js에서 미들웨어 써서 rootSaga로 연결해줬기 때문인가요?
- 해결됨React로 NodeBird SNS 만들기
like, unlike
안녕하세요. 제로초님 다름이 아니라 axios like에 return axios.post(`/post/${postId}/like`, {}, { withCredentials: true }); 이렇게 하면 잘 날라가는데 return axios.delete(`/post/${postId}/like`, {}, { withCredentials: true }); 이렇게 unlike는 빈 데이터를 보내면 401 에러가 뜨는데 그 에러가 로그인 안했다는 isLoggedin 그 에러거든요? 근데 로그인 정보랑 다 들어있고 해서 의아했는데 저 빈 객체 를 없애니깐 잘 동작하더라고요. 이거 왜이러는걸까요?
- 미해결React로 NodeBird SNS 만들기
2020.08.09 antd
import React, { useState } from 'react'; import AppLayout from "../components/AppLayout"; import Head from "next/head"; import { Form, Input, Checkbox, Button } from 'antd'; const Signup = () => { const [id, setId] = useState(''); const [nick, setNick] = useState(''); const [password, setPassword] = useState(''); const [passwordCheck, setPasswordCheck] = useState(''); const [term, setTerm] = useState(false); const [passwordError, setPasswordError] = useState(false); const [termError, setTermError] = useState(false); const onFinish= () => { if (password !== passwordCheck) { return setPasswordError(true); } if (!term) { return setTermError(true); } console.log({ id, nick, password, passwordCheck, term, }); }; const onChangeId = (e) => { setId(e.target.value); }; const onChangeNick = (e) => { setNick(e.target.value); }; const onChangePassword = (e) => { setPassword(e.target.value); }; const onChangePasswordChk = (e) => { setPasswordError(e.target.value !== password); setPasswordCheck(e.target.value); }; const onChangeTerm = (e) => { setTermError(false); setTerm(e.target.checked); }; return ( <> <Head> <title>NodeBird</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/3.16.2/antd.css" /> </Head> <AppLayout> <Form onFinish={onFinish} style={{ padding: 10 }}> <Form.Item label={"아이디"} name={"user-id"} rules={[{ required: true, message: "Please input your user ID!"}]} > <Input name="user-id" value={id} required onChange={onChangeId} /> </Form.Item> <Form.Item label={"닉네임"} name={"user-nick"} rules={[{ required: true, message: "Please input your nickname!"}]} > <Input name="user-nick" value={nick} required onChange={onChangeNick} /> </Form.Item> <Form.Item label={"비밀번호"} name={"user-password"} rules={[{ required: true, message: "Please input your password!"}]} > <Input name="user-password" type={"password"} value={password} required onChange={onChangePassword} /> </Form.Item> <Form.Item label={"비밀번호 체크"} name={"user-password-check"} > <Input name="user-password-check" type={"password"} value={passwordCheck} required onChange={onChangePasswordChk} /> {passwordError && <div style={{ color: 'red' }}>비밀번호가 일치하지 않습니다.</div>} </Form.Item> <Form.Item name={"user-term"}> <Checkbox name={"user-term"} checked={term} onChange={onChangeTerm}>서비스 이용 동의</Checkbox> {termError && <div style={{ color: 'red' }}>약관에 동의하셔야 합니다.</div>} </Form.Item> <Form.Item> <Button type="primary" htmlType="submit">가입하기</Button> </Form.Item> </Form> </AppLayout> </> ); }; export default Signup;
- 미해결React로 NodeBird SNS 만들기
제로초님~
제로초님 안녕 하세요 저번에 추천 해주셨던 'react-data-table-component' 잘 사용 하고 있습니다. 그런데 react-data-table-component 사용중 질문이 있어서 글을 남기게 되었습니다. 제로초님 블로그도 react-data-table-component 로 serversidedatatable을 사용 하셨다고 하셨는데 페이지 네이션 처리 할때 지금 이렇게 사용 하고 있는데 지금 코드에서 옵션은 이렇게 사용 하고 있거든요 혹시 넘버링으로 커스텀 하실때 몇일을 찾아 봐도 나오질 않아서 옵션이 있었는지 해서 글을 남기게 되었습니다.
- 미해결React로 NodeBird SNS 만들기
배포시 pm2와 bcrypt 관련 오류입니다
https://www.inflearn.com/questions/24208 아마 이거랑 비슷한 질문이지 않을까 싶은데 pm2 reload all && pm2 monit을 했을 때 이런 오류가 발생합니다. 위에 질문에서 node_modules 폴더를 지우고 git에 commit한 다음, npm i를 하라고 되어있는데 아예 nodebird 폴더 자체에서 node_modules 폴더를 지우고, git commit 후 npm i를 기존 폴더와 ubuntu에서 둘 다 해줘야하는 것인가요? 아니면 ubuntu 접속 후 node_modules를 지우고 다시 npm i를 해줘야할지 ㅠㅠ... bcrypt는 3.0.6 버전으로 설치되어 있습니다!