칭찬일기 프로젝트 회고

Y·2022년 7월 9일
0

프로젝트

목록 보기
1/5

*기존에 일자별로 작성했던 회고글을 한 글로 정리해서 옮김.

개인 프로젝트를 시작하게 되었다.
아이디어는 생각한지 꽤 오래되었는데, 그동안 시간이 나지가 않아 스토리라인 정도만 잡아두고 개발은 시작하지 못하다가 드디어 시작하게 됐다!


들어가기에 앞서

웹 프로젝트?

웹 프로젝트는 해커톤에 참여해서 FE파트를 맡아본 것과, 지금 팀으로 진행하고 있는 프로젝트 2개에서 BE파트를 맡고 있는 정도가 전부다. 다만 팀으로 진행되고 있는 프로젝트 두 개가 전부 진행이 더뎌서, 개인 프로젝트를 하나 해봐야겠다는 생각에 프로젝트를 진행하게 되었다.

이번 프로젝트의 목표

일단 현재 BE 공부를 하고 있기 때문에, 프로젝트를 진행하면서 공부한 부분에 대해 직접 구현해보면서 제대로 익혀보는 것이 목표이다. 개발 공부를 오래했다면 오래했고, 짧게 했다면 짧게 했지만, 내 경험상으로는 항상 책이나 글만 읽는 것보다는 직접 뭐라도 만들어보면서 부딪히는 것이 훨씬 공부하기 좋았기 때문이다.

주제, '칭찬 일기'

프로젝트 주제로 선정한 것은 '칭찬 일기' 이다. 이 주제를 선택한 이유는 간단하다. 주위를 둘러보면 스스로에게 박한 사람들이 참 많았기 때문이다. 분명히 열심히 살고 있는데도 스스로에게 참 박했다. 나는 주변에서 그런 사람들을 참 많이 봤다. 그래서 그런 사람들을 위해 스스로에게 칭찬을 할 수 있는 서비스를 만들고자 했다. 칭찬 한 마디로 힘든 것이 사라지기도 힘들 것이고, 모든 문제가 해결되지도 않는다. 하지만 칭찬 한 마디가 가끔은 아주 힘든 순간을 이겨낼 힘이 되기도 한다. 나는 그 힘을 믿기 때문에, '칭찬 일기' 라는 프로젝트 주제를 선정하게 됐다.

기술 스택

프론트는 React, 백은 Node.js+Express로 결정했다. 프로젝트 특성상 해당 조합이 적당하고, 또한 빠르게 개발해야하기 때문이었다. DB는 일단은 MongoDB로 개발할 예정이다.


Day1

이전에 미리 짜두었던 스토리라인을 정리하고, 프로젝트에서 사용할 기술 스택을 정하였으며, Figma를 이용하여 간단하게 구조를 짰다. 그리고 개발 환경 기본 세팅을 진행했다. yarn을 이용했고, 상기서술대로 React, Node.js, Express, MongoDB 세팅을 했다. 얼마전에 맥북으로 바꿔서 그런지 환경 설정할때마다 (특히 DB) 애를 먹는다...

Day2

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":"~"를 추가해주면 정상적으로 작동된다.

Day3

로그인 기능을 간단하게 구현했고 서브페이지 메뉴-콘텐츠-타이틀 구조 틀을 만들었다.

  • 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으로 감싸줬다.

Day4

회원가입기능과 칭찬 기록 기능을 구현했다. 회원가입 기능을 위해서 모달창 구현을 하느라 좀 헤맸다. 그리고 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에 아예 비밀번호를 암호화해서 저장하고 있는데, 비밀번호 찾기는 어떻게 해야할지 좀 더 고민해봐야겠다...

Day5

메인 로직들은 얼추 다 구현을 했다. 이제 리팩터링하고 마무리만 하면 거의 이번 프로젝트는 마무리일 것 같다! 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"을 추가해주면 된다길래 했더니 다른 오류가 또 났다. 다시 보니 백 서버에 오류가 난 거였어서 이걸 고쳤더니 그 오류도 없어졌다. 내가 추가한 기억이 없는 이상한 코드가 하나 추가돼있었다.. 뭐였는지 모르겠다.

Day6

메인 기능은 얼추 다 완성을 했어서 리팩터링이나 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을 안 걸리게 했더니 제출이 안 됐다... 그래서 이 부분은 아직 구현을 못했다.

  • 회원가입 시 중복 아이디는 가입을 못하게 하고, 비밀번호 길이나 이메일 형식에 제한을 걸었다. 스키마 수정하고 백에서 회원 등록하는 코드에 중복여부 확인하는 코드 추가해서 간단하게 해결할 수 있었다.

  • 로그아웃 기능을 구현했고, 로그인 시에만 세부 메뉴 페이지에 들어갈 수 있도록 코드를 수정했다.

Day7

UI 수정을 조금 하고 배포를 시도했다. 배치가 이상한 부분이 있었는데 이것도 css 이름을 중복되게 설정해서 발생한 문제였다. 그리고 docker, docker-compose, nginx 사용을 시도했는데 어디가 꼬인건지 dockr, docker-compose로 올리는 것까진 성공을 했는데 프론트-백 사이에 통신이 안되고 백-프론트간 경로도 인식을 못하고 있다... 그냥 로컬에서 돌리면 되는데 도커로 올려서 돌리면 이러는게 뭔가 설정파일에서 문제가 있는 것 같긴한데 아직 정확하게 뭐가 문제인지 못 찾았다... 배포는 아예 처음 해보는거라 뭘 어떻게 찾아서 해결해야할지도 모르겠는데... 완전 멘붕상태였다.. 일단 코드자체는 거의 다 끝난 것 같아 여기에 의의를 두긴 하려고한다.

Day8

드디어 배포를 했다. 그런데 진짜 애를 많이 먹어서... 한참 걸렸다. 처음에는 도커를 이용하고, 이후에 reverse proxy를 이용하기 위해서 nginx를 사용했다. 그런데 오류가 너무 많이 나서... 정말 한참을 헤맸다. 그리고 나서 AWS 서버에 배포를 하려고 EC2 인스턴스를 등록했는데, 또 오류가 엄청나게 났다.. 이번엔 백은 되는데 프론트가 안 됐다... 그리고 프론티어 계정이라 서버가 용량이 작아서 그런건지 뭔지 계속 서버가 뻗었다. 그래서 결국 프론트는 vercel으로 배포하고, 백은 친구의 도움을 받아서 AWS로 배포했다. 백 서버를 가동하는 동안에도 오류가 많이 나서 정말 오래 걸렸다.

  • 처음에 front-nginx-back-DB 4개를 따로따로 도커로 묶고 도커 컴포즈를 이용해서 빌드하니 계속 오류가 났다. 결국 front-nginx를 묶어서 web, 그리고 back (DB는 로컬 환경이 아니었기 때문에 따로 도커를 안 써도 되는 거였다.)으로 총 두개 컨테이너로 도커 컴포즈를 이용하니 돌아갔다. nginx에서 오류가 계속 났는데, 아직도 정확하게 어떤 부분이 문제인지는 파악을 못 했고, 다음과 같은 구조로 해결했다: nginx를 listen 80으로 하고, upstream으로 back 서버를 5000포트로 설정했다. 이후에 location /은 기본 index.html, /api는 앞서 upstream으로 설정했던 back 서버를 proxy-pass로 설정해주었다. docker-compose에서 nginx 포트를 80:80으로 하고 백은 expose 5000만 설정해줬다. (다만 이전에도 구조는 같았고 포트 설정만 좀 달랐는데 왜 오류가 났던건지 잘 모르겠다... 아무래도 conf 파일을 잘못 작성했거나 포트 설정이 꼬였던게 아닌가 싶다.)
  • 로컬에서까지는 돌아갔는데, AWS로 올리고나니 백은 되는데 프론트가 안됐다.. 도커로 실행했는데도 프론트가 전혀 안 돌아갔다. 아마 nginx의 권한이나 파일 경로 문제일 것 같긴한데... 어쨌든 그래서 친구에게 도움을 요청했고, 프론트는 vercel을 이용해 따로 배포하고 백만 AWS 서버에 올리기로 했다. vercel은 깃 저장소와 연동만 해주면 자동으로 배포를 해주고 커밋을 하면 새로 빌드도 해줘서, 별달리 기록할 것이 없고... 백을 올리는데에는 또 오류가 많았다.
  • AWS 프리티어 계정을 사용해서 EC2를 썼는데, 서버가 중간 중간에 계속 뻗었다... 아무래도 도커를 올리기엔 좀 무리였던건가 추정하고 있다. 그래서 친구가 쓰던 AWS 서버를 사용했는데, 이미 사용하고 있던 포트가 있어서 포트를 좀 변경을 했다. 그런데 그러고나서 프론트에서 백으로 요청을 보낼때 자꾸 프론트 주소로 호출을 하는 에러가 발생하는 것(Day7에서 언급하였다)이다. 분명히 proxy 설정을 다 해줬는데 왜 그런가... 했더니 알고보니 axios에서 baseURL 설정을 별도로 해줘야되는 거였다. 이전에는 baseURL을 따로 설정 안해줬어도 로컬으로 돌리는 거였어서 정상적으로 잘 돌아갔었던 모양이다. 그래서 axios의 baseURL을 따로 설정해주는 파일을 작성하고, axios를 이용하는 파일에는 이를 import 해와서 사용하도록 했다.
  • 백/프론트 서버를 따로 사용하니 cors 에러가 나서 백에서 corsOption을 설정해줬다.
  • 이제 다른 건 다 잘 돌아갔는데, 쿠키에서 문제가 발생했다. 기존에는 로그인을 하면 쿠키로 token값을 보내주고, 그를 통해서 글을 기록할때 token값을 req에 보내줘서 글 내용과 함께 token값을 DB에 저장하고, 유저 정보를 보거나 작성한 글을 볼때 쿠키값을 받아와서 해당 토큰과 일치하는 유저 정보/글을 확인하는 형식이었다. 그런데 친구가 쓰고있던 서버가 아파치를 쓰고있어서 쿠키가 넘어오지를 않았다! ProxyPassReverseCookieDomain 같은 것도 이용해 봤는데 안 됐다... 결국 아파치를 안 쓰는 다른 서버에서 nginx만 써서 해봤는데도 쿠키가 안 넘어왔다... 뭘 해도 쿠키가 안 넘어왔다.. 분명히 백에서 넘어온 res header를 보면 set cookie를 했는데 프론트에서 확인이 안 됐다... 그래서 결국 쿠키를 사용하지 않고 local storage를 사용하는 방식으로 코드를 아예 변경했다. 로그인을 하기 위해 api를 호출하면 백에서 res로 token 값을 함께 넘겨주고, 그 값을 프론트에서 local storage에 저장을 하는 것이다. 그리고 글을 작성할때 헤더에 해당 token 값을 넘겨주면 백에서는 DB에 넘겨준 헤더값을 token 값으로 사용한다. 이런 방식으로 해서 쿠키 없이도 기능들을 구현할 수 있었다.

프론트나 백 구현은 조금이라도 해본적이 있었지만, 배포는 정말 아예 처음해보는 일이라 주변의 도움을 받아서 성공할 수 있었다. 구현이 정말 까다롭다는 걸 다시 한 번 느낄 수 있었고... 다음 번에는 좀 더 배포까지도 능숙하게 할 수 있었으면 좋겠다. 그리고 어쨌든간에 프로젝트를 약 8일만에 마무리한 것에 대해서 너무 다행이라고 생각한다! 1차적으로 마치긴 했지만 앞으로도 추가할 기능이 있으면 조금씩 계속 추가해보고, 코드도 다듬을 수 있으면 주기적으로 다듬을 예정이다.

profile
개발자, 학생

0개의 댓글