[프로젝트 회고] 좌충우돌 팀 프로젝트

windowook·2024년 3월 31일
post-thumbnail

🌱 팀원 모집

1월 초중순에 '에브리타임'에 게시판을 이용하여 스터디를 같이 할 사람들을 구했습니다. 원래 초기의 목적은 프로젝트를 할려고 만들었던 건 아니고 코테풀이나 모각코, CS 지식 나눔 등을 하려고 했습니다. 근데 그건 혼자서도 해도 되는 것들이기도 하고 팀원들의 의견도 프로젝트를 할 수 있다면 프로젝트를 하자는 쪽으로 의견이 수렴되어 프로젝트를 1개 만들고 배포까지 하는 것으로 목표를 잡고 시작했습니다.

첫 만남은 동성로에 있는 카페에서 가지게 되었습니다. 학교 다니면서 전공 수업 안에서 하는 팀플은 여러번 해봤지만 그건 타의 반 자의 반이었고 이건 제가 직접 모은 거면서 전공자들인 새로운 사람들과 무언가를 할 거라는 생각에 설렜었습니다.

다들 도착하고나서 간단한 자기소개와 함께 프로젝트나 공부 경험에 대해서 얘기했고, 스터디를 어떻게 진행하면 좋을지에 대해 가볍게 얘기를 나눈 후 첫 만남은 마무리했습니다. 그 다음주 토요일에 정식으로 스터디를 시작했습니다.
거기서 프로젝트에 대한 의견이 나와서 합의가 됐고, 새로운 프론트엔드 팀원 1명을 영입해서 시작하자는 걸로 협의했습니다.

🌱 소개

FE 2명, BE 3명

아이디어

프로젝트 아이디어 선택을 위해 각자 그 다음 스터디 모임 날까지 아이디어와 대략적인 기능을 정리해서 가져와서 설명하고, 익명으로 투표를 해서 최다득표를 받은 프로젝트로 진행하는 방법을 선택했습니다. 결과는 백엔드 팀원 분이 갖고 오신 아이디어로 정해졌습니다. ChatGPT 3.5 오픈 API를 이용해서 유저가 재료만 입력하면 레시피를 만들어주는 서비스를 만들기로 했습니다. 글을 적는 현 시점은 이미 로컬에서 95% 정도 테스트가 끝난 상태이며 배포까지는 얼마 남지 않은 상태입니다.

애플리케이션 형태

우리가 의도하는 서비스의 경우 사용자가 심플하게 어디서나 사용할 수 있는 것이 중요하기 때문에 PC 환경에 맞추기보다 모바일 환경을 우선으로 한 애플리케이션으로 개발하고자 했습니다. 하지만 모바일 앱으로 제작하지 않은 것은 네이티브 앱이나 크로스 플랫폼을 구현할 수 있는 언어를 팀원들이 사용해 본 적이 없어서 디폴트 뷰를 모바일 화면에 맞게 PWA처럼 UI를 구현했습니다.

소요 기간

기능 구현: 1월 31일 ~ 4월 1일 + @ (10주)

중간에 팀원들 개인 스케줄이 각기 있어서 불가피하게 구현이 좀 늦춰지거나, 개발 중 발생하는 오류 및 버그 해결에 지연되는 것들이 있다보니 당초 예상했던 8주 정도의 기간보다 훨씬 미뤄졌습니다. 아쉬운 것은 저 또한 팀프로젝트가 처음이었고 다른 팀원들 또한 처음이거나 혹은 전공 수업에서 해본게 전부라 애자일이나 스크럼 같은 스케줄 탬플릿을 적용시켜 효율적인 시간 관리를 하지 못했다는 것입니다.

또, 문제가 없었지만 추가 기능이 생기면서 해당 로직과 연결하다가 문제가 생기는 곳이 있어 추
가적인 작업을 더 하다보니 완전히 이슈를 없애는 데까지 추가 소요된 시간이 좀 있었습니다.

주요 기능

  • 레시피 생성(연금술): OpenAI GPT-3.5 API를 사용한 레시피 생성과 레시피 저장 기능
  • 게시판: 레시피를 게시판에 업로드할 수 있고, 좋아요를 많이 받으면 탑 3 레시피 랭킹보드에 올라감
  • 랭킹보드: 유저들에게 좋아요를 많이 받은 탑 3 레시피를 메인페이지와 게시판 페이지에 보여줌
  • 마이페이지: 내가 작성한 레시피와 좋아요를 눌렀던 다른 유저의 레시피를 모두 확인할 수 있음
  • 로그인: 이메일 계정을 만들거나 구글, 카카오, 네이버 계정을 사용하여 간편 로그인이 가능함

커뮤니케이션

  • 노션: 자료 공유, 일정 소개, 회의 내용
  • 디스코드: 1대1 소통, 전체 회의
  • 카카오톡 오픈채팅: 상시 소통

개발 환경 및 툴

  • IDE: VSC, IntelliJ
  • 디자인: Figma
  • 협업 및 버전 관리: GitHub
  • 프론트: React.js, React Query, Tailwind
  • : Spring
  • 오픈 API: GPT-3.5
  • DB: MySQL, Redis
  • 스토리지: AWS S3
  • 배포: Docker, EC2

링크

https://github.com/Refrigerator-Alchemist/Frontend

🌱 당면했던 문제와 해결

SNS 로그인

예시로 카카오 로그인의 경우, window.href로 카카오 로그인 페이지를 열고 응답의 body에 들어오는 데이터를 저장하는 코드를 한 페이지에서 구현해야 한다고 생각하고 코드를 만들었는데 의도와는 달리 서버에서 설정한 리다이렉션 페이지로 이동해도 쿼리파라미터에 값들이 담겨오기만 할 뿐 아무 일도 일어나지 않았습니다.

알고보니 프론트에서는 처음에 카카오 로그인 페이지로 이동만 시켜주고 사용자가 로그인 완료하고 나면 백엔드가 카카오에서 받아온 데이터를 가지고 액세스 토큰, 리프레시 토큰, 소셜 타입 등을 쿼리 파라미터로 보내주면 이것을 추출하여 로컬스토리지, 로컬 변수 등에 적절히 저장하면 되는 것이었습니다.

프로필 수정

프로필 수정 페이지에서 프로필 이미지를 변경하거나, 닉네임을 변경할 수 있습니다. 처음에는 그냥 이미지 파일을 선택하고 formData에 추가하여 닉네임과 함께 바로 서버로 보내어 저장하게끔 만들었는데 서버로 데이터는 전송됐지만 닉네임을 제대로 전달받지 못해서 변경이 되지 않는 문제가 생겼습니다.

정확히는 formData에 이미지 파일과 함께 요청 body에 보내는 식으로 서버에 전달하는데 닉네임이 한글일 경우 UTF-8 인코딩이 필요하고, 형식은 json으로 가야하지만 그렇지 않았기 때문이었습니다.

이 문제는 Blob 객체를 사용해서 해결했습니다.Blob 객체는 주어진 데이터를 나타내는 불변형 객체로, 데이터는 텍스트, 배열, 혹은 이진 데이터 등 다양한 형태가 될 수 있습니다. 저는 nickName을 stringify를 사용하여 형식으로 변환하고, 그 결과를 UTF-8로 인코딩해서 formData에 추가해서 보냈고 서버에서는 닉네임을 전달받아 변환하여 제대로 변경할 수 있게 되었습니다.

게시물 상세 페이지, 게시물 수정 페이지

좋아요를 누르거나, 수정을 누르면 서버로 요청은 가는데 404 not found 에러가 계속 발생했습니다. 해당 파트는 제가 맡은 파트는 아니었는데 디버깅을 하던 도중 이 파트의 백엔드 팀원이 문제를 해결해 줄 수 있냐고 물어봐서 코드를 보고 원인을 찾아냈습니다. useEffect로 서버로부터 게시물의 정보를 받아오는 요청을 보내고 그 응답이 body에 들어왔는데 json 타입의 배열로 들어오고 있음에도 그걸 map으로 받지 않고 값에 직접 접근을 하는 코드로 구현이 되어있어서 브라우저에서 서버가 보낸 데이터를 저장하지 못하고 있었습니다. 그래서 map을 사용하여 응답으로 들어온 데이터를 상태에 저장하는 방법으로 해결했습니다.

액세스 토큰 재발급

한 번도 구현해본 적이 없어서 정말 해맸던 기능이다. 액세스 토큰이 만료되면, 서버에서 /reissue 라는 엔드포인트의 경로로 리디렉션 시켰을 때 reIssue 함수가 호출되도록 코드를 구현했는데 내가 생각한 로직이 답이 아니었습니다.

프론트에서 axios 인터셉터를 만들어서, 네트워크 status가 400일 때(토큰 만료) 인터셉터가 이 조건을 캐치하여 reIssue를 호출함으로써 액세스 토큰을 재발급 받고 원래 요청하려던 API를 다시 요청할 수 있도록 구현해야했습니다.

이렇게 문제를 어떻게 해결해야할지 알고 나서 위와 같이 코드를 구현했죠. 로그인 요청 API를 인터셉터의 조건에 걸어놓은 이유는, 처음 액세스 토큰을 발급받게 되는 요청인 로그인 요청이 실패했을 때 재발급을 시도하여 무한 루프될 수 있기 때문입니다.

  • 로그인 요청 실패 -> 토큰 재발급 -> 다시 로그인 요청 -> 실패 -> 토큰 재발급 ->...

코드의 isRefreshing 이라는 변수는 토큰을 재발급하는 중과 완료된 것을 구분하기 위해 사용했습니다.

커스텀 에러 핸들링

백엔드 팀원과 협의하여 네트워크 탭의 http status와 그 내용에 걸맞는 커스텀 에러를 작성했었습니다.

유저에게 보여줄 메세지, 콘솔에서 백엔드가 직관적으로 어떤 에러인지 알 수 있는 메세지 등을 프론트와 백이 각각 code를 통해서 주고 받는 방식입니다.

여기서 문제는 '에러 내용: '이라며 백엔드가 볼 메세지가 콘솔에서 [object Object]로 나오고 있었습니다.
원인은 이렇게 객체로 저장해놓은 값을 console.log 안에서 '.'을 이용해 직접 접근을 시도하고 있었습니다.

해당 문제를 해결하는 방법을 찾아보니, JSON.stringify를 사용하여 객체의 값을 볼 수 있다고 나왔고 이를 적용하여 코드를 수정했더니 콘솔에서 각 에러의 이름(키)를 제대로 확인할 수 있었습니다.

🌱 마무리

팀프로젝트를 하면서 혼자 강의를 듣고 사이드 프로젝트를 개발하는 것과는 전혀 다른 경험을 많이 할 수 있었습니다.
백엔드와 엔드포인트를 정해서 API 통신을 하는 것부터 로그인 기능을 만들고 유저의 데이터를 클라이언트에서 이용하는 것이라던가 처음 구현해보는 기능들 투성이였습니다.

아쉬움이 많이 남습니다. 개발을 할 때 미리 정해뒀어야 할 코드 컨벤션, 스케줄 관리, 깃 액션과 같은 CI/CD를 위한 세팅이 되어있지 않았고 프론트나 백에서 각 팀의 팀원간 소통 또한 원활히 진행되지 못해서 시간이 많이 지연되고 누수된 것도 큽니다.

어느 누구의 잘못만 있는 게 아니라 팀장인 나와 팀원 모두가 지분을 가진 것들이기 때문에 이 경험을 복기해서 다음에는 부족함이 없는 팀프로젝트를 개개인 별로 할 수 있도록 양분 삼으려고 합니다. 여러 방면에서 정말 많이 배울 수 있었던 값진 경험이었던 것 같습니다.

profile
안녕하세요

0개의 댓글