데동여지도(dedongyeo) 프로젝트에 대해 설명하는 글을 파트 별로 작성해 보고자 한다.
데동여지도는 2021년 넥스터즈 18기에 만들어진 팀(팀이름: Oh!When?, 우리 언제 만나? 라는 의미)에서 시작했으며, 커플들의 데이트 코스를 기록하고 저장하는 웹/앱 어플리케이션이다. 주요 기능으로는 지도에서 커플들이 방문한 스팟 조회 및 커스텀 스팟 생성, 스티커 찍기, 코스 만들기, 코스 저장하기 등이 있다.
첫 번째 포스팅에서는 웹 프론트엔드 파트의 기술 스택 및 아키텍처를 설명하려고 한다.
기술 스택
웹 프론트엔드에서는 다음과 같은 언어 및 라이브러리, 프레임워크를 사용하였다.
- TypeScript 4.1
- Next 10.x
- React 17.x
- Apollo Client
- GraphQL
- MapboxGL
- Styled Components
- Material Ui
언어는 타입스크립트를 사용했다. 타입스크립트를 사용한 이유는 팀원들이 대부분 실무에서 쓰고 있어서 익숙하기도 했고 (안 쓸 이유가 딱히 없었다) 정적타입 언어이기에 엄격한 타입 체킹으로 인한 컴파일 단계에서의 빠른 오류 파악 및 작성자의 의도를 쉽게 파악할 수 있다는 장점 등이 있어서 큰 고민 없이 선택했다.
프레임워크는 React 기반 SSR을 지원하는 Next 프레임워크를 사용했다. Next를 사용한 이유는 앞서 언급한 것처럼 SSR을 지원하는 것이 가장 컸다. 팀원들이 가장 익숙한 SPA 라이브러리가 React 여서 리액트를 사용했으며, 초기 로딩 속도가 빠른 SSR의 장점을 가져가면서, 때에 따라 CSR이 필요한 상황에서는(인터렉션이 많거나 등) 옵션으로 CSR을 사용할 수 있기에 역시 큰 고민 없이 Next 프레임워크를 선택했다.
상태관리는 Apollo Client를 선택했다. Apollo Client는 ContextAPI, Redux, MobX 등과 같이 React에서 전역으로 상태를 관리할 수 있는 도구이다. GraphQL(GQL)을 사용하여 서버와 통신하기 때문에, Apollo Client를 사용하면 반환 값을 캐시로 보관할 수 있고, 다른 라이브러리 없이 이것만 가지고 로컬 상태를 함께 관리할 수 있다. Apollo Client 에서 GQL 서버와 통신하는 로직은 아래 그림을 통해 이해할 수 있다.
서비스의 특성상 지도가 필요했기 때문에 Mapbox 라는 오픈소스 지도 라이브러리를 사용하게 되었다. 사실 국내에서만 서비스를 한다고 하면 네이버 지도 API나 카카오 지도 API도 훌륭한 선택지가 될 수 있고, 실제로 처음에는 카카오 지도 API를 가지고 개발했었다. 하지만, 구현해야 하는 기능 중에 코스 이미지를 렌더링 해 주어야 하는 기능이 있었는데, 카카오 지도 API를 가지고는 이 기능을 구현하는 과정에서 어려움이 있어서(저작권 이슈 포함) 이후 Mapbox 라는 오픈소스로 바꾸게 되었다. 한국어 지원이 완벽하지 않아서 지도에 한국어와 영어가 섞여있는 아쉬움은 있지만, 문서화도 잘 되어 있고 무엇보다.. 예쁘다..! ㅋㅋ (디자인을 여러가지로 지원한다)
프로젝트 디렉토리 구성
프로젝트 디렉토리 구성은 다음과 같다. 세부적인 사항은 github 레포지토리 참고
dedongyeo-map-frontend
│ README.md
│ package.json
│
└───components
│ └───_assets
│ └───_common
│ └───_layout
│ └───Course
│ └───Home
│ └───Popup
│
└───constants
│
└───lib
│ └───apollo
│ └───storage
│
└───nginx
│
└───pages
│ └───api
│ └───course
│ index.tsx
│ _app.tsx
│
└───public
│
└───styles
│
└───util
components 디렉토리에는 도메인 별로(Home, Course 등) 하위 디렉토리를 나누었고 공통으로 쓰이는 컴포넌트는 _(언더스코어)를 붙여서 하위 디렉토리로 구분해 주었다. 컴포넌트의 경우도 비즈니스 로직을 다루는 컴포넌트, 뷰(View) 만을 다루는 컴포넌트(styled-components), 상태를 다루는 컴포넌트로 필요에 따라 쪼개서 관리하였다.
lib의 apollo 디렉토리에는 전역에서 관리하는 캐시, 훅, 변수 등에 대해서 모아서 관리를 해 주었다.
util 디렉토리의 경우는 전체적으로 쓰일 수 있는 유틸성 함수들을 모아서 정리했다.
(디렉토리 구조에 대해서는 혹시 더 좋은 구조가 있으면 자유롭게 댓글로 의견을 남겨주기를 바랍니다)
아키텍처
프론트엔드 및 백엔드를 포함한 전체적인 아키텍처는 다음과 같다.
특이한 점은 프론트엔드 서버(Next)와 백엔드 서버(Nest)를 두 개 두었으며 브라우저에서도 백엔드 서버를 찌르고, 프론트엔드 서버에서도 백엔드 서버를 GQL로 찌른다는 점이다. Static 서버의 경우는 이미지 등의 저장소로 사용하였다.