기능 정리
-로그인/회원가입
1. 로그인
2. 회원가입
- 메인 페이지
1. 무한 스크롤
2. autocomplete
3. 검색 후 매장 등록 및 투표
4. 카테고리 필터
- 맵 페이지
1. 식당 등록
2. 기존 식당 마커 표시
- 상세 페이지
1. 저장 추가/삭제
2. 댓글 페이지네이션
3. 댓글 추가/삭제
4. 가게 사진 업로드
- 마이페이지
1. 드래그앤드랍
2. 아카이브 무한스크롤
- 회원 정보 수정 페이지
1. 유저 사진 변경
2. 유저 정보 변경
김민지 | 지난 3주간 12시간씩 붙어서 고생했던 팀원들 다들 너무 수고했고 리팩토링을 통해서 앞으로 더 완성도 있는 프로젝트로 발전시키고 싶다. |
---|---|
박경민 | 제대로 된 협업을 해봤다고 느낄 수 있는 프로젝트였다. 애플리케이션의 기능들을 팀원들과 나누어 개발하고 이를 합치는 과정에서 많을 것을 배울 수 있었다. |
서예빈 | 3주안에 프로젝트를 성공적으로 마무리 할 수 있을까? 걱정했는데 다행히 만족스러운 결과를 낼 수 있어서 좋았다. 비록 초기에는무리하게 계획했지만 다들 의욕을 가지고 끝까지 열심히 해주었기에 잘 마무리 할 수 있었던 것 같다. 끝나고도 다같이 모여서 공부하며 리팩토링을 하고 있어서 좋다 :) |
손규성 | 개발 공부를 시작한 후 처음 진행해본 프로젝트였기에 의미가 깊고, 능력 좋은 팀원들과 함께 할 수 있어서 영광이었다. 지금까지 배운 것들을 복합적으로 사용해볼 수 있는 좋은 경험이었고, 더 나아가 하나의 애플리케이션을 가동시키기 위해 프론트엔드와 백엔드가 어떻게 소통하는지 잘 알게 되었다. 끝난 후 되돌아보니 아쉬운 점들도 보이지만, 개인적으로 오래 시간 동안 기억에 남을 프로젝트인 건 분명하다. |
| 김민지 | 가게 상세 페이지의 프론트엔드와 백엔드, 그리고 다크모드를 구현하였다.
가게 상세 페이지 • 낙관적 업데이트를 이용한 가게 저장 및 댓글 추가/삭제 구현 • useQuery와 페이지 queryKey를 이용한 댓글 페이지네이션 구현 • multer를 이용한 가게 사진 업로드 구현
다크 모드 • useTheme 커스텀 훅과 css 변수를 이용해 다크모드 구현 | | --- | --- | | 박경민 | - JWT을 사용한 회원가입 및 로그인 구현
SearchMap Page, ProfilePage를 구현하였으며 이와 관련된 프론트엔드 및 백엔드를 구현하였다.
SearchMap Page
추가적으로 메인페이지와 가게 정보페이지에서 나타나는 SideBanner와 가게 정보 페이지에서 이용되는 Map을 구현하였다. | | 손규성 | 팀 빌딩 단계에서는 프로젝트 아이디어를 제공했고, 개발 단계에서는 메인페이지의 프론트엔드와 관련된 백엔드 로직을 주로 담당했다.
아무래도 메인페이지에서 사용되는 컴포넌트가 추후 다른 팀원들로부터 사용될 확률이 높았기 때문에 범용성 좋은 공통 컴포넌트를 만들기 위한 많은 노력을 했다.
메인페이지에서 사용된 주요 기능을 나열해보자면 아래와 같다.
| 김민지 | react-query를 통해 댓글이나 저장한 데이터를 낙관적 업데이트하고, 백엔드와 프론트엔드 상태를 동기화하는 작업이 기억에 남는다. 프론트엔드에서는 데이터 추가, 삭제에 필요한 id를 알 수 없다는 문제가 있었다. 백엔드에서 필요한 id를 생성할 수 있지만 이후 프론트엔드에서는 해당 id를 어떻게 사용해야 할지도 고민이었다.
이를 해결하기 위해 공식 문서와 공식 문서의 블로그에서 낙관적 업데이트와 관련된 내용을 꼼꼼하게 읽어보는 시간을 가졌다. 내가 고민했던 부분이 낙관적 업데이트의 일반적 어려움인 것을 알게 되었고, 서버에 mutation를 한 이후에 invalidateQueries
또는 setQueryData
를 통해 프론트와 백엔드의 데이터 상태를 동일하게 유지할 수 있다는 것을 배웠다. |
| --- | --- |
| 박경민 | 매장 이미지 및 프로필 이미지를 업로드하기 위해 multer를 같이 공부했던 것이 가장 기억에 남는다. |
| 서예빈 | 프로젝트 중간에 코드 컨벤션을 맞추는데에 하루를 보냈던 경험이 기억에 남는다.
각자 필요한 백엔드 로직을 구현하다보니 불필요한 함수가 생성되는 경우도 있었고, 함수이름이나 매개변수, 파라미터에 대한 컨벤션이 맞지 않아 있어 어떤 역할을 하는 함수인지 잘 판단하지 못하는 경우도 있었다. 따라서 중간 과정에서 칠판에 백엔드에 대한 모든 정보를 기록한 뒤, 모두 함께 컨벤션을 맞춰가며 수정하는 시간을 가졌다. 생각보다 수정하는데에 시간도 오래 걸렸고, 프론트엔드까지 수정해야 해서 복잡하기도 했다. 하지만 수정 후에 코드를 파악하기 더 쉬워졌고, 코드 컨벤션이 맞춰져서 더 깔끔했다.
팀 프로젝트를 하면서 코드 컨벤션의 중요성을 깨달았고, 이를 위해서 기획 단계에서 미리 컨벤션을 맞추는 시간이 필요하며, 프로젝트 중간 중간 팀원 간의 코드 리뷰와 리팩토링 시간을 가져야겠다고 생각하는 계기가 되었다. |
| 손규성 | 무한 스크롤 기능이 제대로 동작하지 않아 고생했던 하루가 제일 기억에 남는다. useInfiniteQuery
로직도 잘 구현했고, PAGE_SIZE
도 잘 전달했는데, 정작 서버에서 넘어오는 데이터는 예상과 너무 다른 정보가 넘어왔다. 덕분에 React Query 공식 문서를 하루 종일 뒤져가며 애먼 useInfiniteQuery
로직을 계속 이리저리 수정해보다가 시간을 많이 낭비했다. 결국 문제는 query string으로 전달받은 PAGE_SIZE
값을 문자열에서 숫자 타입으로 변환해주지 않아서 발생한 단순한 문제였다. 결국 에러를 고치고 났을 때는 선 희열 후 허망함을 느꼈던 기억이 강하게 남는다. |
| 김민지 | 프로젝트를 진행하다가 어려운 문제가 있거나 고민되는 부분을 논의했을 때 문제가 작든 크든 팀원들이 적극적으로 의견을 공유하고 도와주려고 했던 점이 가장 고맙고 좋았다.
특히, 의견이 충돌할 때도 경청하고 함께 장단점을 분석해 최선의 선택을 하기 위해 노력하는 과정에서 자유롭게 의사소통할 수 있었고 좋은 결과를 낼 수 있었던 것 같다. | | --- | --- | | 박경민 | 동료들이 모두 열심히 해주고 많은 도움을 줘서 동료들에게 의지할 수 있었다. | | 서예빈 | 모든 팀원들이 의욕적으로 프로젝트에 참여해서 좋았다. 중간 중간 소통하는 시간도 많았으며, 서로의 의견을 경청하고 피드백 할 수 있어서 좋았다. 그리고 조금씩 다른 각자의 장점을 모두 잘 살려 프로젝트를 진행하고 마무리 할 수 있었던 것 같아 뿌듯하다.
정해진 것 없이 자유로운 환경에서 우리의 프로젝트를 진행할 수 있어서 좋았다. 더 나은 프로젝트를 위해 고민하고 팀원들과 소통할 수 있어서 좋았다.
과제 형식의 코딩과는 다르게 프로젝트를 진행하다 보니 기능을 구현하기 위해 필요한 새로운 지식과 여러 라이브러리를 사용해야 했다. 프로젝트를 진행하면서 새로운 것을 공부하고 사용해보면서 경험을 쌓을 수 있어서 좋았다. | | 손규성 | 가장 좋았던 건 멋진 팀원들이었다. 의견을 자유롭게 공유할 수 있는 팀 컬쳐가 형성되어 있어서 서로의 코드를 리뷰해주거나 프로젝트 방향을 바꾸는 과정에서 트러블이 없었다.
두 번째로는 프론트와 백엔드가 어떻게 하나의 애플리케이션을 가동시키는지에 대해 더 깊게 이해하게 된 것 같다. 프론트엔드 공부로 개발에 입문하게 되면서 종종 “백엔드는 그럼 무엇을 하는지?”, “내가 작성하는 코드가 어떻게 유의미해지는 것인지?” 같은 질문을 많이 헀는데, 이번 프로젝트를 통해 조금이나마 더 큰 그림을 보고 개발할 수 있을 것 같은 자신감이 든다.
세 번째로는 React Query, Recoil 등 리액트를 사용하면 자주 마주하게 되는 라이브러리들을 한 번에 같이 사용해보며 어떻게 상호작용하는지 이해할 수 있었다는 점이다.
마지막으로 개발자가 되길 잘했다는 확신이 들었다. 이전에 사업할 때는 내가 생각한 제품을 다른 개발자들의 도움 없이는 만들 수 없었지만, 이번 프로젝트를 통해 내가 직접 생각한 내용을 애플리케이션으로 만들어낼 수 있다는 힘이 얼마나 큰가에 대해 체감하게 됐다. |
| 김민지 | 댓글 페이지네이션은 queryKey에 현재 페이지를 넣어 새로운 페이지 버튼을 클릭할 때마다 데이터를 fetching하는 방법으로 진행했다. 이때 댓글 UI가 심하게 깜빡거리는 문제가 있었다. 이는 로딩 상태인 경우 UI가 사라지고 상단에 위치한 UI가 내려온 것처럼 보이기 때문이었다. 이를 해결하기 위해 로딩 상태인 경우 동일한 UI skeleton을 렌더하도록 했지만 여전히 깜빡거리는 현상이 발생했다.
이후 react query 공식 문서의 pagination 관련 내용을 읽고, 다음과 같은 원인과 해결책을 찾을 수 있었다. 페이지 버튼을 클릭했을 시 새 페이지가 새로운 쿼리로 인식되기 때문에 UI의 상태가 성공↔로딩으로 반복적으로 변경된다. 이 때문에 UI가 깜빡거리는 현상이 발생했던 것이다. 이를 해결하기 위해 react query는 다음 데이터가 요청되어서 전달되는 동안 이전 데이터를 유지시켜주는 옵션인 keepPreviousData
옵션을 가지고 있다. 이를 true로 설정하여 쿼리 키가 변경되더라도 새로운 데이터를 가져오는 동안 가장 최근에 성공적으로 fetching했던 데이터를 사용하여 UI 변경 시 끊김 없이 동작하도록 했다. |
| --- | --- |
| 박경민 | 4개의 투표 관련 모달을 구현하는 과정에서 이들을 연결하는 것이 매우 어려웠다. 그래서 각 모달에서 필요한 데이터와 투표 알고리즘을 손으로 직접 작성해보고 구현을 시작했다. |
| 서예빈 | 이번 프로젝트는 백엔드, 프론트엔드 역할 분담을 한 것이 아니라 개발 하면서 필요한 부분은 각자 구현하였다. 백엔드 지식이 거의 없었던 나는 API를 직접 구현하는 과정이 어려웠다. 처음에는 프론트에서 필요한 여러 데이터들을 서버에 각각 요청하도록 구현하였다. 그러나 서버로 부터 응답 받은 데이터를 이용하여 또 다시 요청해야 하는 경우가 많았고, 그럴 수록 프론트엔드에서 구현해야할 로직은 복잡해졌다.
이러한 문제점을 해결하기 위해 상황에 따른 API를 사용해야 한다는 것을 깨달았다. 프론트엔드에서 필요한 데이터들이 서로 연관되고, 복잡한 경우에도 API를 잘 이용하면 한 번의 요청으로도 서버로부터 필요한 데이터를 응답받을 수 있었다.
결론적으로, 서버 요청과 관련된 많은 로직을 프론트에서 해결하는 것이 아니라 프론트에서는 데이터를 잘 표현하는데에 집중하고, 백엔드에서는 프론트에서 필요한 데이터만 찾아서 응답할 수 있도록 구현해야함을 느꼈다. |
| 손규성 | 메인페이지에서 관리하는 상태가 많기 때문에 초반에는 자주 막히는 느낌을 받았다. 지금에서야 보면 명확하지만, 당시에는 어떤 상태를 React Query로, 어떤 상태를 Recoil로, 어떤 상태를 지역 상태로 사용해야할지 헷갈렸다. 조금이나마 막힘 없이 코딩하고 싶다는 생각에 개발을 잠시 멈추고, 공책에 일종의 ‘상태 흐름표’를 그렸다*(아날로그한 솔루션이긴 하지만).* 흐름표를 참조하면서 개발하니까 개발 과정이 훨씬 수월하게 느껴졌다. |
| 김민지 | • 다크 모드 변수 정리
디자인적으로도 개선이 필요해 보인다. 아직은 토이 프로젝트 수준의 디자인에 머물러 있는 것 같아 조금 아쉽다. 기능을 잘 구현해낸 만큼 디자인 개선에 힘을 쓴다면 보는 사람들이 더 큰 흥미를 느낄 수 있을 것 같다. |