데브코스 5기 파이널 프로젝트 약 두 달 간의 여정이 마무리 되었다. 기획부터 프로젝트가 완성되기까지의 과정을 회고해보려 한다.
소개 | 실시간 산책 기록 및 산책로 공유 서비스 |
기간 | 2024.02.05 ~ 2023.03.25 (2개월) |
인원 | 총 8명 (FE 5명, BE 3명) |
스택 | (FE) Next.js, Typescript, TanStack-Query, Zustand, Kakaomap API, TailwindCSS (BE) Java, Spring Boot, MySQL, redis, OAuth2, Spring Security, JJWT, SpringDoc, Sentry |
주소 | https://www.masilgasil.app/ |
개요
👟 서비스 소개
마실가실은 실시간 산책 기록 및 산책로 공유 서비스이다. 잠깐의 여유와 운동이 필요한 현대인에게, 다양한 산책로를 이용하고 공유하면서 색다른 경험을 느끼게 하고자 하였다. 서비스의 특징 중 하나가 산책로 곳곳의 핀포인트 저장 기능인데, 산책로를 기획하는 사람의 시점에 따라 다양한 테마로 보여질 수도 있겠다고 생각해 하늘 아래 같은 산책로는 없다 등의 캐치프레이즈도 구상했었다. 서비스의 전체적인 기능들은 추후 기능소개 섹션에서 자세하게 다룰 예정이다.
기획
💭 테오의 스프린트
파이널 프로젝트를 앞두고, 테오님의 스프린트 특강을 들을 수 있는 기회가 있었다. 이전 팀프로젝트를 진행하며 목적의 동기화 측면에서 다소 아쉬움을 갖고 있었었는데, 특강 내용을 적용해 팀 캔버스 맵을 작성한 덕분에 새로 만난 팀원들과 더 빨리 서로를 알 수 있었고, 각자 생각하는 프로젝트의 목적과 가치를 공유할 수 있었다. 같은 곳을 바라보게 한다는 것이 정말 중요한 것임을 알 수 있는 시간이었다. 시간이 다소 소요되더라도 꼭 필요한 과정이 아니었나 싶다.
💾 Scheme Driven Development (SDD)
백엔드 팀과 합류하기 전 구상한 유저 스토리에 기반해 프론트엔드 팀원들끼리 스키마도 작성해보았다. 미리 데이터의 구조와 형태를 그려보며 씽크를 맞추고, 백엔드 분들에게 제안할 부분을 생각보는 시간을 가졌다. 객체의 인터페이스 타입을 미리 정해본다는 느낌도 들었다. 예를 들면 데이터가 갖고 있는 시간 형태와 유저에게 입력 받는 시간의 형태를 맞춘다거나, 위치 정보를 [위도, 경도]로 담을지 GeoJSON 형태로 담을지 등의..
이 과정을 통해 용어를 통일하고, 프로덕트의 흐름에 대한 동기화가 이루어질 수 있었다. 덕분에 이후 진행한 회의나 담당한 부분의 커뮤니케이션이 많이 매끄러워질 수 있지 않았나 싶다. 하나의 TMI를 덧붙히자면, 일주일 동안 논의했던 기존의 기획안이 3분의 스몰토크에서 나온 아이디어로 인해 변경되었다. 그만큼 다 마음에 드셨단 뜻이겠지~
🏃🏻 협업
2주 단위의 스프린트
본격적인 협업이 시작되고, 플래닝포커를 통해 기능들에 대한 예상되는 일정을 산정해보았다. 이후 산정한 일정을 기반으로 2주 단위의 기획 - 개발 - MVP 완성 - 유저 테스트 및 QA 단계로 구성된 스프린트를 계획하게 되었다.
그렇게 생겨버린 저녁 코어타임
프로젝트 초반에는 기존 데브코스 코어타임 (09 ~ 14) 동안 팀 디스코드에 접속해 매일의 작업 내용을 공유하고 결정할 것들을 논의하였는데, 도중에 게더타운으로 이주하며 팀별 회의 장소가 나뉘어지는 바람에 점차 소통이 뜸해지게 되었다. 결국 한 차례 문제가 있었고, 그 날을 계기로 자체적으로 저녁 코어타임을 진행하며 프로세스를 한 번 다잡았던 기억이 난다. 이후 아침저녁으로 스크럼을 진행하며 작업 상황을 동기화하였고, 큰 문제 없이 마무리할 수 있었다.
🚀 주요 기능
- 메인 페이지 : 유저의 산책 통계와 인기 산책로를 확인할 수 있다.
- 실시간 산책 기록 : 실시간으로 산책을 기록하고, 원하는 지점에 핀포인트를 남길 수 있다.
- 산책로 공유 : 다녀온 산책로를 공유하고, 다른 유저의 산책로를 선택하여 산책을 시작할 수 있다.
- 산책 메이트 : 유저들이 공유한 산책로를 기반으로 메이트를 모집하고, 함께 산책할 수 있다.
- 나의 산책일지 : 내 산책 이력을 월별로, 일별로 통계와 함께 확인할 수 있다.
- 산책로 탐색 : 전국의 산책로와 메이트 모집 현황을 최신순, 인기순으로 확인할 수 있다.
기여
🧑🏻💻 담당 업무
1. 실시간 산책 기록
유저의 산책을 실시간으로 기록하고, 서버에 전송하는 기능을 담당하였다. 팀원 한 분과 함께 진행하였다.
산책 중 GPS 호출 최적화하기
민감한 GPS를 보정하기 위하여 실시간으로 유저 위치를 저장하는 GeoLocation API에 쓰로틀링을 적용하였고, 한 번의 산책에서 이루어지는 API 호출을 90% 감소시킬 수 있었다.
'산책' 데이터 관리하기
경로, 핀포인트, 메모로 구성된 산책기록 데이터를 Context API로 관리하여 Prop Drilling을 개선하였고, 역할에 따라 로직을 분류 및 문서화하여 협업 간 코드의 쉬운 이해를 돕고자 하였다.
또한 상황에 맞는 단계별 애니메이션, 바텀시트를 통해 모바일 환경에서의 사용자 경험을 고려하였다.
2. 나의 산책일지
유저의 산책 기록을 월별로 확인할 수 있는 산책 일지 페이지를 구현하였다. 단독으로 진행하였다.
무분별한 Data fetching 제어하기
산책일지 페이지는 쿼리스트링을 통해 월별 산책기록을 서버로부터 받아온다. 사용자의 무분별한 API 요청을 방지하기 위하여, 쿼리 라우팅 간 디바운스를 적용하여 일정 시간 내에 발생한 요청을 1회로 묶어 처리하였다.
목적에 맞는 캘린더 컴포넌트 커스텀하기
React-Day-Picker 라이브러리를 기반으로 캘린더를 컴포넌트화하여 유저의 월별, 일별 산책 기록에 대한 상호작용이 이루어질 수 있도록 로직을 구현하였다.
3. 유저 프로필
유저의 산책 통계와 관련 이력을 확인할 수있는 프로필 페이지를 구현하였다. 단독으로 진행하였다.
조건부 렌더링을 통한 컴포넌트 재사용
세션 스토리지에 저장된 서비스 토큰과 접속한 페이지의 userId를 대조하여, 내 프로필 페이지와 다른 유저의 프로필 페이지 렌더링을 분기처리함으로써 페이지 컴포넌트의 재사용성을 높였다.
확장성을 고려한 산책 리스트
추가적인 데이터의 확장을 고려하여 객체 형태로 리스트 데이터를 구조화하였다. 리스트 내부 컨텐츠 유무를 판단하는 isEmpty, 공개 여부를 판단하는 visible 프로퍼티를 통해 Data mapping 과정에서 조건을 만족하는 데이터만 필터링될 수 있도록 구현하였다.
🤔 문제 해결
별도의 포스트로 작성한 바 있어서, 텍스트로 요약해두려 한다. 자세한 내용은 아래 제목의 링크를 통해 확인할 수 있다!
🔗MVC에서 FLUX 패턴 구조로 마이그레이션하기
문제 인식
MVC 디자인 패턴 구조를 적용한 컴포넌트 작업을 진행하던 중, 일련의 문제들(Prop Drilling, Controller에 집중된 로직들)로 인해 서로의 코드를 이해하고 수정하는 데에 불편함을 느껴 이를 주도적으로 개선하였다.
적용 및 결과
분류할 로직들을 1차적으로 정리하고, ContextAPI와 useReducer를 활용하여 데이터의 특성에 따른 분류 작업을 진행하였다. 추가적으로 JSX를 반환하는 기존의 Controller 로직을 커스텀 훅으로 변경함으로써 전달되는 프롭과 파라미터의 타입을 제거, Prop Driling을 줄임과 동시에 컴포넌트 간 결합도를 낮추었다. 이를 통해 서로의 코드를 이해하고 수정하는 데에서 발생하는 불편함을 완화시킬 수 있었으며, FLUX 등의 외부 라이브러리에 의존하지 않은 채 사용성을 개선할 수 있었다.
돌아보기
초기에 불편함을 느꼈던 원인은 적용한 패턴에 있어 프로젝트의 특성과 개발환경을 고려하지 않은 채로 작업을 진행하였기 때문이라고 생각된다. 따라서 문제점을 발견하고 개선하는 것도 좋지만, 애초에 사이드 이펙트를 고려하여 문제가 발생할 수 있는 경우의 수 자체를 줄여 나가는 것도 옳은 방법이라는 것을 배울 수 있었다.
🔗유저 선책경로 썸네일 로직 제작하기
문제 인식
유저가 산책을 끝낸 후, 산책 경로를 시각적으로 확인 가능한 썸네일을 제작하는 로직이 필요했다. 해당 로직을 구현하기 위해 산책 경로가 그려진 카카오 맵 컴포넌트 DOM을 캡쳐하여 사용하고자 하였다. 그러나 카카오맵 API는 맵 폴리곤 이미지를 실시간으로 생성하여 조립하는 방식으로 지도를 렌더링하기에, DOM 캡쳐를 통해서는 원하는 형태의 소스를 얻을 수 없었다.
해결방안 모색
- 폴리곤 이미지를 직접 조립한 후 그 위에 경로를 그려주는 방법
- 로딩된 폴리곤 이미지는 캐싱이 이루어지기 때문에, 산책이 종료된 시점의 폴리곤 이미지를 재추적하여 조립하는 과정은 꽤나 까다로운 작업이라 판단하였다.
- 카카오맵 API의 정적 맵 기능을 활용하는 방법
- 해당 요소가 필요할 때마다 맵 컴포넌트를 렌더링해야 하므로, 많은 리소스가 소요된다.
- 경로 마킹 기능을 지원하지 않아 산책 경로를 그려줄 수 있는 방법이 부재하다.
- 가공된 이미지를 별도로 저장하기 때문에, 저작권 문제에서도 자유롭지 않다.
- CanvasAPI를 활용해 사용자의 산책경로를 직접 그려 이미지로 추출하는 방법
- 로직도 무겁지 않고, 저작권 문제에서도 자유로울 수 있다.
적용
진행 상황과 사이드이펙트를 고려한 결과, CanvasAPI를 활용하기로 결정하였다. 이를 위해 산책 경로 데이터를 입력받고 썸네일 이미지를 반환하는 drawPath 함수를 구현하였다. 🔗drawPath 함수는 스케일링을 통한 축소 비율 추출 → 비율에 맞게 경로 그리기 → 시작점과 종료점에 대한 처리의 단계로 설계하였다.
결과
가벼운 리소스만으로 저작권 문제에서도 자유롭게 문제를 해결할 수 있었다. 기술적으로 문제를 바라보는 것도 좋지만, 한 걸음 물러나 다양한 관점에서 해결책을 찾아보는 것도 중요하다는 것을 배울 수 있었다.
마치며
📝 후기
평범하지 않은 도메인이라 좋았다. 관련해 GPS나 카카오 지도 API 등 기술적인 다양한 챌린지를 경험해볼 수 있었고, 프로젝트 기간 내내 너무 재밌게 개발을 진행했던 것 같다. 다만 최대한 모바일 친화적인 환경을 구성하려 했으나, 브라우저의 한계에서 오는 백그라운드, 잠금 상태에서의 구동 불가 같은 문제를 해결할 수 없다는 점이 다소 아쉽다. 네이티브 배우면 GPS 로직만 마이그레이션 해보는걸로..
또 Next.js의 학습 기간이 짧았어서, 해당 프레임워크를 잘 활용하지 못했단 점도 다소 아쉬웠다. 프로젝트가 끝나고 후속 활동으로 Next.js 스터디를 진행하고 있는데, 이때 이걸 왜 안썼지..? 같은 상황이 자주 연출된다. 리팩토링과 관련회 주기적으로 논의하고 있으니, 계속해서 점진적으로 개선해나가고자 한다.
마지막으로 팀원들! 개인적으로 이번 프로젝트의, 데브코스의 가장 큰 성과가 아닌가 생각이 든다. 합도 잘 맞고, 같이 성장하며 많은 자극이 되었던 감사한 인연들이다.. 계속해서 스터디나 프로젝트를 진행하며 고된 취준 시즌을 함께 이겨내고자 한다. 데브코스 때보다 더 자주 보는 것 같기도..
🛠️ 리팩토링
데브코스가 마무리되며 공식적인 프로젝트의 진행 기간은 끝이 났지만, 추가로 구현할 기능들과 함께 개선이 필요한 부분들을 지속적으로 고쳐 나가고자 한다. 글 쓸 내용이 어느 정도 쌓이면 한 번 더 다루는 걸로.. 대강 리스트업해보면 아래와 같다.
개선
- 합성 컴포넌트 및 아토믹 패턴을 적용한 메인 페이지 재구성
- 페이지별 성능 최적화 (LightHouse, Metadata, Next 컴포넌트 등)
- Next.js의 페러렐 / 인터셉트 라우팅을 통한 모달 관리
- 렌더링 최적화를 위한 디테일한 RSC / RCC 분리
추가 기능
- 메이트 채팅 기능 (w. socket.io)
- 알림 기능
- 업적 기능