*기존에 일자별로 작성했던 회고글을 한 글로 정리해서 옮김.
개인 프로젝트를 시작하게 되었다.
아이디어는 생각한지 꽤 오래되었는데, 그동안 시간이 나지가 않아 스토리라인 정도만 잡아두고 개발은 시작하지 못하다가 드디어 시작하게 됐다!
웹 프로젝트는 해커톤에 참여해서 FE파트를 맡아본 것과, 지금 팀으로 진행하고 있는 프로젝트 2개에서 BE파트를 맡고 있는 정도가 전부다. 다만 팀으로 진행되고 있는 프로젝트 두 개가 전부 진행이 더뎌서, 개인 프로젝트를 하나 해봐야겠다는 생각에 프로젝트를 진행하게 되었다.
일단 현재 BE 공부를 하고 있기 때문에, 프로젝트를 진행하면서 공부한 부분에 대해 직접 구현해보면서 제대로 익혀보는 것이 목표이다. 개발 공부를 오래했다면 오래했고, 짧게 했다면 짧게 했지만, 내 경험상으로는 항상 책이나 글만 읽는 것보다는 직접 뭐라도 만들어보면서 부딪히는 것이 훨씬 공부하기 좋았기 때문이다.
프로젝트 주제로 선정한 것은 '칭찬 일기' 이다. 이 주제를 선택한 이유는 간단하다. 주위를 둘러보면 스스로에게 박한 사람들이 참 많았기 때문이다. 분명히 열심히 살고 있는데도 스스로에게 참 박했다. 나는 주변에서 그런 사람들을 참 많이 봤다. 그래서 그런 사람들을 위해 스스로에게 칭찬을 할 수 있는 서비스를 만들고자 했다. 칭찬 한 마디로 힘든 것이 사라지기도 힘들 것이고, 모든 문제가 해결되지도 않는다. 하지만 칭찬 한 마디가 가끔은 아주 힘든 순간을 이겨낼 힘이 되기도 한다. 나는 그 힘을 믿기 때문에, '칭찬 일기' 라는 프로젝트 주제를 선정하게 됐다.
프론트는 React, 백은 Node.js+Express로 결정했다. 프로젝트 특성상 해당 조합이 적당하고, 또한 빠르게 개발해야하기 때문이었다. DB는 일단은 MongoDB로 개발할 예정이다.
이전에 미리 짜두었던 스토리라인을 정리하고, 프로젝트에서 사용할 기술 스택을 정하였으며, Figma를 이용하여 간단하게 구조를 짰다. 그리고 개발 환경 기본 세팅을 진행했다. yarn을 이용했고, 상기서술대로 React, Node.js, Express, MongoDB 세팅을 했다. 얼마전에 맥북으로 바꿔서 그런지 환경 설정할때마다 (특히 DB) 애를 먹는다...
Atlas 이용해서 mongoDB와 백 연결했고, 페이지 첫 화면 만들었다. 다음 페이지들 구성도 했다.
html/css를 안 한지가 좀 오래돼서 다시 찾아보면서 하느라 시간이 좀 걸린 것 같다.
grid 기능을 이번 프로젝트 하면서 제대로는 처음 사용해봤다! subgrid 기능까지 사용해봤다.
로고 이미지를 간단하게 만들어서 화면에 띄웠는데
<img src="~"/>
로는 경로가 올바른데도 엑박이 뜨는 오류가 있었다.
<img src={require("~")}/>
을 사용해서 해결할 수 있었다.
새벽동안 back+front 연결하는 추가 작업을 했다.
front를 react에서 5000 포트를 사용하니까 back server는 일단 3000 포트로 했다. 분명히 3000 포트를 쓰고 있지 않은데 3000 포트를 사용하고 있다는 에러가 발생해서 확인해보니, 맥북의 경우는 AirPlay 수신 모드가 켜져있으면 해당 에러가 나는 것 같다. 모드를 해제 해주니 정상적으로 돌아갔다.
front+back 연결을 했더니 갑자기 잘 돌아가던 프론트가 안 돌아가서 애를 먹었다. 이것저것 수정해봤는데 안 돼서 뭐가 문제인건가 하고 있었는데 어느 순간에 갑자기 프론트가 다시 됐다... 어디서 잘못됐던건지 잘 모르겠다.
front에서 setupProxy.js를 추가해주는 방법으로 연결하는 글이 많았는데, 그냥 package.json에 "proxy":"~"를 추가해주면 정상적으로 작동된다.
로그인 기능을 간단하게 구현했고 서브페이지 메뉴-콘텐츠-타이틀 구조 틀을 만들었다.
Router 이용해서 프론트에서 페이지 이동을 구현했다. <Switch>를 사용하는 글들이 있었는데, v6 부터는 <Switch>가 없어져서 <Routes>를 사용해야 한다고 한다. 그리고 withRouter도 없어져서 그냥 export해주면 된다.
로그인 버튼을 누르면 서버로 입력한 아이디, 비밀번호가 넘어가는데 form을 사용해야하는 것 같다. type이 submit인 input으로는 안 되는건지 시도해봤는데 안 된다. 혹시나 다른 방법이 있는지 나중에 더 알아봐야할 것 같다.
로그인 시도중에 504 에러가 떠서 한참을 찾았는데 알고보니 프론트 서버만 돌리고 있었다. 백 서버도 같이 돌렸더니 그때는 404 에러가 떴는데, 이건 server.js에 있어야할 코드를 usermodel.js에 잘못 넣어서 그런 거였다. app.post(~) / app.get(~) 코드는 server.js로 가야한다. 정신없이 하다보니까 파일을 착각한 것 같다.
타이틀/하단 메뉴를 고정시켜두고 중간 부분에 콘텐츠가 뜰 수 있도록 하려고 했는데, 콘텐츠를 띄우려는 부분에 props.children을 넣어줘서 구현할 수 있었다. 그리고 콘텐츠로 띄우려는 각 부분을 <Menu>로 감싸줬다.
메뉴를 클릭하면 페이지가 이동하도록 하려고 했는데, Link를 이용할 수도 있지만 이미 input 태그를 걸어놓은 지라 Link로 감싸주면 구조가 무너졌다... 그래서 다른 방법을 찾았는데,
import {useNavigate} from 'react-router-dom';
...
const navigate=useNavigate();
...
< ... onClick={()=>{navigate(~)}}>
으로 구현할 수 있었다.
세부 페이지를 만들다보니 갑자기 메인페이지 구조가 이상해졌는데, 알고보니 class를 같은 이름으로 써서 그런 거였다. 중복되는 이름 쓰지 말 것.
로그인 구현을 하면서 react-redux를 사용했는데, 이걸 사용하려면 Provider으로 감싸줘야한다. 그래서 index.js에서 <App/>을 Provider으로 감싸줬다.
회원가입기능과 칭찬 기록 기능을 구현했다. 회원가입 기능을 위해서 모달창 구현을 하느라 좀 헤맸다. 그리고 react-redux도 공부했다. MVC 패턴과 비슷한 맥락으로 이해했는데 잘 이해한 건지는 잘 모르겠다.
모달창을 띄우기 위해서 모달창 위에 띄울 회원가입 페이지를 따로 파일로 만들었는데, 모달창 외부를 클릭하면 창이 꺼지고 내부에서는 창이 꺼지지 않도록 했다. 그래서 외부에 onClick 이벤트를 주고, 내부에서는 창이 꺼지지 않도록 onClick에 (e)=>e.stopProgation()을 해줬다. 그러면 이벤트가 적용되지 않는다.
회원가입을 실패하면 fail 창이 뜨고, 성공하면 success 창이 뜨도록 했는데 성공해도 fail 창만 뜬다... 수정을 해야할 것 같다. 그리고 창이 뜨고 나면 모달창이 자동으로 꺼지게 하고싶은데 이 부분은 어떻게 해야될지 잘 모르겠다...
회원가입 이후에 올바른 비밀번호로 입력을 해도 로그인이 실패하는 에러가 떴는데, bcrypt가 오류가 난 줄 알고 한참을 헤매다가 알고보니 회원가입할때 db에 비밀번호가 암호화되지 않은 것으로 잘못 올라간 거 였다...
로그인하는 고정에서 method is not function이라는 에러가 떴는데, user=require(~) 에서 {user}=require(~)으로 바꿔주니까 해결이 됐다... 정확하게 어디가 잘못됐던 건지 잘 모르겠다...
history.push가 작동을 하지 않는 것 같다. 페이지를 이동할때는 useNavigate를 이용해서 navigate함수를 사용해줘야한다.
칭찬 글 스키마를 회원 스키마와 별개로 설계했는데, 로그인 정보를 받아와서 Id값을 받아온 후에 기록창에서 뿌려줘야한다. 이 부분까지 해결하면 어느정도는 다 구현한 것 같다. 아예 같은 스키마에 넣을까 고민도 되는데 좀 더 고민해봐야될 것 같다... 그리고 비밀번호 찾기와 회원가입시에 아이디/ 비밀번호나 인증하는 조건을 다는 것도 구현하고 싶은데, 우선 남은 기능들을 전부 구현하고나서 해야할 것 같다. DB에 아예 비밀번호를 암호화해서 저장하고 있는데, 비밀번호 찾기는 어떻게 해야할지 좀 더 고민해봐야겠다...
메인 로직들은 얼추 다 구현을 했다. 이제 리팩터링하고 마무리만 하면 거의 이번 프로젝트는 마무리일 것 같다! 4일차에 칭찬을 쓰는 기능을 구현했고, 이 날에는 쓴 칭찬을 받아와서 목록으로 뿌려주는 기능을 구현했다. 그리고 회원정보를 받아와서 보여주는 기능도 구현했다.
로그인을 한 회원의 정보를 받아와서 그 회원이 작성한 칭찬글과, 광장 메뉴에서 작성한 칭찬글을 기록 메뉴에 뿌려줘야한다. 이걸 위해서 현재 로그인된 회원의 정보를 받아와야되는데, 쿠키와 세션을 이용한다는 건 알고있었는데 어떻게 구현해야될지를 모르겠어서 알아보다가 react-cookie라는 걸 알게됐다. 이 기능을 이용해서 쓰기를 할때 아예 넘겨주는 데이터값에 token을 추가해서 넘겨줬다.
기록값을 뿌리기 위해서도 db내에서 검색을 하기 위해서는 현재 로그인을 한 회원의 정보를 알아야하는데, req.headers.cookie를 써서 쿠키를 받아올 수 있었다...!!! 그래서 split해줘서 db내에서 검색을 했다. 다른 방법들도 더 있는 것 같은데 더 찾아보고 공부해봐야겠다.
mongoose에서 findOne만 사용했는데, db내에 기록된 값을 다 받아와야하니까 전체를 다 받아오는 쿼리가 필요했다. find를 이용할 수 있는데, 만약에 or 조건을 사용하려면
~.find({$or:[{조건1},{조건2}]},~)
을 이용하면 된다.
백엔드에서 값을 받아오는 것까지는 완성을 했는데, 프론트에 뿌려주는 부분에서 또 애를 먹었다. dispatch를 이용해서는 값을 업데이트해주는 거고, 업데이트돼서 저장된 값은 useSelector을 이용해서 받아와야된다. useSelector을 찾기까지도 헤맸는데 찾고나서도 값 업데이트하는게 잘 안돼서 한참을 헤맸다. 함수 호출하는 걸로 했더니 오류가 나고, useEffect를 했더니 너무 많이 호출이 되고... 결론은 useEffect를 사용해서 dispatch를 실행하는데, 두번째 인자로 useSelector을 저장한 변수를 사용해주면 된다. 그러니까
변수 = useSelector(state=>state~);
useEffect(()=>{
dispatch(action 함수);
},변수);
이런 형식으로 해주면 되는 거다. 그러면 값이 변할때만 useEffect가 호출된다... 이걸 몰라서 정말 한참을 헤맸다... 그래도 방법을 찾아서 정말 다행이다...
코드를 깃허브에 업로드하고 있는데, 잘못 업로드해서 db 비밀번호가 같이 올라가버렸다...!!!!...... 파일을 삭제해도 깃허브에는 기록이 남으니까... 기록을 없애야했다... 그래서 찾아보니
git filter-branch --force --index-filter "git rm --cached --ignore-unmatch 'file_path/file_name'" --prune-empty --tag-name-filter cat -- --all
git push origin (branch 이름) --force
을 이용하면 되는 거였다. 해당 파일을 삭제해서 푸쉬 해주고나서 위의 저 명령어를 입력하면 됐던 것 같다. config 파일을 만든다는게 미루다가 저렇게 됐다.. 역시 이런 문제는 미루면 안된다... (한 번 노출됐으니 비밀번호는 당연히 다시 바꿨다)
cannot use import statement outside a module이라는 에러가 갑자기 떴는데, 인터넷에서 package.json에 "type":"module"을 추가해주면 된다길래 했더니 다른 오류가 또 났다. 다시 보니 백 서버에 오류가 난 거였어서 이걸 고쳤더니 그 오류도 없어졌다. 내가 추가한 기억이 없는 이상한 코드가 하나 추가돼있었다.. 뭐였는지 모르겠다.
메인 기능은 얼추 다 완성을 했어서 리팩터링이나 UI 정리정도가 남아있었는데, 많이 진행을 못했다..
useNavigate를 쓰려니 usenavigate() may be used only in the context of a <router> component. 에러가 갑자기 발생했다. 원래는 App.js에서 <BrowserRouter>로 감싸줬는데 index.js에서 <App/>을 <BrowserRouter>으로 감싸주는 걸로 변경하니까 해결됐다.
input 태그에 입력한 값을 제출 이후에는 지워주려고 했는데, value에 state 변수를 넣어주고 클릭이벤트시 set함수를 이용해서 ""으로 값을 바꿔주면 되는 거였다.
회원가입 제출을 한 이후에 회원가입 창이 꺼지도록 하고 싶었는데, 파일을 별개로 만들어두고 e.stopPropagation을 걸어뒀더니 구현이 안 됐다. 제출하는 부분만 e.stopPropagation을 안 걸리게 했더니 제출이 안 됐다... 그래서 이 부분은 아직 구현을 못했다.
회원가입 시 중복 아이디는 가입을 못하게 하고, 비밀번호 길이나 이메일 형식에 제한을 걸었다. 스키마 수정하고 백에서 회원 등록하는 코드에 중복여부 확인하는 코드 추가해서 간단하게 해결할 수 있었다.
로그아웃 기능을 구현했고, 로그인 시에만 세부 메뉴 페이지에 들어갈 수 있도록 코드를 수정했다.
UI 수정을 조금 하고 배포를 시도했다. 배치가 이상한 부분이 있었는데 이것도 css 이름을 중복되게 설정해서 발생한 문제였다. 그리고 docker, docker-compose, nginx 사용을 시도했는데 어디가 꼬인건지 dockr, docker-compose로 올리는 것까진 성공을 했는데 프론트-백 사이에 통신이 안되고 백-프론트간 경로도 인식을 못하고 있다... 그냥 로컬에서 돌리면 되는데 도커로 올려서 돌리면 이러는게 뭔가 설정파일에서 문제가 있는 것 같긴한데 아직 정확하게 뭐가 문제인지 못 찾았다... 배포는 아예 처음 해보는거라 뭘 어떻게 찾아서 해결해야할지도 모르겠는데... 완전 멘붕상태였다.. 일단 코드자체는 거의 다 끝난 것 같아 여기에 의의를 두긴 하려고한다.
드디어 배포를 했다. 그런데 진짜 애를 많이 먹어서... 한참 걸렸다. 처음에는 도커를 이용하고, 이후에 reverse proxy를 이용하기 위해서 nginx를 사용했다. 그런데 오류가 너무 많이 나서... 정말 한참을 헤맸다. 그리고 나서 AWS 서버에 배포를 하려고 EC2 인스턴스를 등록했는데, 또 오류가 엄청나게 났다.. 이번엔 백은 되는데 프론트가 안 됐다... 그리고 프론티어 계정이라 서버가 용량이 작아서 그런건지 뭔지 계속 서버가 뻗었다. 그래서 결국 프론트는 vercel으로 배포하고, 백은 친구의 도움을 받아서 AWS로 배포했다. 백 서버를 가동하는 동안에도 오류가 많이 나서 정말 오래 걸렸다.
프론트나 백 구현은 조금이라도 해본적이 있었지만, 배포는 정말 아예 처음해보는 일이라 주변의 도움을 받아서 성공할 수 있었다. 구현이 정말 까다롭다는 걸 다시 한 번 느낄 수 있었고... 다음 번에는 좀 더 배포까지도 능숙하게 할 수 있었으면 좋겠다. 그리고 어쨌든간에 프로젝트를 약 8일만에 마무리한 것에 대해서 너무 다행이라고 생각한다! 1차적으로 마치긴 했지만 앞으로도 추가할 기능이 있으면 조금씩 계속 추가해보고, 코드도 다듬을 수 있으면 주기적으로 다듬을 예정이다.