MateTrip은 실시간 협업을 통해 여행 계획을 손쉽게 세울 수 있는 웹 애플리케이션입니다. 친구들과 함께 지도를 보며 일정을 조율하고, 화상 채팅으로 소통하며, AI 추천을 통해 최적의 여행 경로를 찾을 수 있습니다.
Design Reference: Travel Planning Tool Design (Figma)
- 동시 편집: 여러 사용자가 동시에 지도 위에서 장소를 추가, 삭제, 이동할 수 있으며, 모든 변경 사항이 실시간으로 반영됩니다.
- 커서 공유: 다른 사용자의 마우스 커서 위치와 현재 보고 있는 화면 영역을 실시간으로 공유하여 원활한 소통을 돕습니다.
- 카테고리 필터링: 관광지, 숙소, 맛집 등 카테고리별로 장소를 필터링하여 지도에 표시할 수 있습니다.
- 직관적인 UI:
@dnd-kit을 활용하여 장소 카드를 드래그하여 날짜별 일정에 쉽게 추가하거나 순서를 변경할 수 있습니다. - 경로 시각화: 일정에 추가된 장소들 간의 이동 경로와 소요 시간, 거리를 지도와 리스트에서 즉시 확인할 수 있습니다.
- 장소 보관함: 관심 있는 장소를 임시 보관함에 담아두고 언제든지 일정으로 옮길 수 있습니다.
- AI 여행 가이드: 챗봇에게 여행 스타일이나 선호도를 말하면 적절한 장소를 추천해줍니다.
- 경로 최적화: 선택한 장소들을 가장 효율적으로 방문할 수 있는 최적의 경로를 AI가 계산하여 제안합니다.
- 자동 일정 생성: 여행 기간과 목적지만 입력하면 AI가 전체 여행 일정을 자동으로 생성해줍니다.
- Amazon Chime SDK 연동: 별도의 프로그램 설치 없이 웹 브라우저 내에서 고화질 화상 통화와 음성 채팅이 가능합니다.
- 화면 공유: 여행 계획을 세우면서 서로의 얼굴을 보거나 필요한 정보를 화면으로 공유할 수 있습니다.
- 상세 정보 제공: Kakao Maps API와 자체 데이터를 결합하여 장소의 사진, 주소, 개요, 카테고리 등 상세한 정보를 제공합니다.
- 주변 장소 추천: 선택한 장소 주변의 인기 있는 관광지나 맛집을 자동으로 추천해줍니다.
- Core: React 19, TypeScript, Vite
- State Management: Zustand
- Styling: Tailwind CSS, Radix UI, Framer Motion
- Maps: React Kakao Maps SDK, Kakao Mobility API
- Real-Time: Socket.io-client
- Drag & Drop: @dnd-kit
- Communication: Amazon Chime SDK
- Forms: React Hook Form
- Package Manager: npm
- Linting & Formatting: ESLint, Prettier
- Deployment: AWS (Frontend & Backend)
- 문제: 여러 사용자가 동시에 같은 일정을 수정할 때 데이터 충돌이 발생할 수 있음.
- 해결: Socket.io를 활용하여 모든 이벤트를 중앙 서버를 통해 브로드캐스팅하고, 낙관적 UI 업데이트(Optimistic UI Update)를 적용하여 사용자 경험을 저해하지 않으면서 데이터 일관성을 유지했습니다.
- 문제: 지도에 수많은 마커와 경로를 표시할 때 성능 저하 발생.
- 해결: 현재 뷰포트(Viewport) 내에 있는 장소만 렌더링하는 가시성 필터링(Visibility Filtering)과 마커 클러스터링 기술을 도입하여 렌더링 성능을 최적화했습니다. 또한,
React.memo와useMemo를 적극 활용하여 불필요한 리렌더링을 방지했습니다.
- 문제: 지도, 사이드바, 보관함 등 서로 다른 컨텍스트 간의 드래그 앤 드롭을 매끄럽게 연결해야 하는 복잡성 발생.
- 해결:
@dnd-kit의SortableContext를 활용하되, 각 영역(보관함, 일자별 일정)을 독립적인 Droppable 영역으로 관리하고,onDragEnd이벤트 핸들러에서 드래그 소스와 타겟의 ID를 분석하여 상태 업데이트 로직을 분기 처리함으로써 유연한 이동을 구현했습니다.
포트폴리오 데모 배포판은 실제 NestJS 백엔드·FastAPI AI 서버 없이, 프론트엔드 혼자서 완전히 동작합니다.
- REST API: Mock Service Worker(MSW)가 브라우저 안에서 네트워크 요청을 가로채 고정된 데모 데이터로 응답합니다(
src/mocks/handlers/rest.ts). - Socket.io 실시간 기능:
socket.io-client패키지 자체를 직접 만든 가짜 구현(src/mocks/fake-socket-io.ts)으로 바꿔치기합니다. 실제 Socket.IO 프로토콜을 흉내 내는 대신, 이 앱이 실제로 쓰는 소켓 인터페이스(.on/.off/.emit/.disconnect/.connected/.id) 여섯 가지만 구현합니다.- 지도 협업(POI) 목서버:
src/mocks/fake-socket-server/poi.ts - 채팅 목서버:
src/mocks/fake-socket-server/chat.ts
- 지도 협업(POI) 목서버:
- 가상 협업 연출: 실제로 접속한 사용자가 한 명뿐이어도, 워크스페이스 입장 8초 뒤 가상 동행자("김동행")가 장소 하나를 자동으로 추가한 것처럼 이벤트를 흉내 내서 실시간 협업 기능을 보여줍니다.
- AI 채팅 응답:
@AI채팅 응답은 실제 LLM이 아니라, 미리 정해둔 고정 데이터(장소 추천 2곳)로 응답합니다.
원래 백엔드는 NestJS(REST + Socket.io 게이트웨이 2개) + FastAPI(AI 에이전트) + PostgreSQL(PostGIS·pgvector) + Redis 조합으로 되어 있어, 이 스택 전체를 상시 배포하려면 무료 호스팅 티어의 콜드 스타트·데이터베이스 자동 삭제 같은 제약을 감수해야 했습니다. 대신 프론트엔드만 독립적으로 배포해 핵심 기능(실시간 협업, AI 채팅)을 그대로 시연할 수 있는 방향을 선택했습니다.
.env에 아래 값을 추가하고 npm run dev를 실행하면, 실제 백엔드 없이도 전체 기능을 확인할 수 있습니다.
VITE_USE_MOCKS=true이 값을 빼거나 false로 두면, 기존처럼 VITE_REACT_APP_SERVER_URL 등에 설정된 실제 백엔드에 연결합니다.
- Node.js (Latest LTS recommended)
- npm
-
Clone the repository
git clone https://github.com/yourusername/matetrip-frontend.git cd matetrip-frontend -
Install dependencies
npm install
-
Environment Setup Create a
.envfile in the root directory and add necessary API keys:VITE_KAKAO_MAP_API_KEY=your_kakao_map_api_key VITE_API_URL=your_backend_api_url
-
Run the development server
npm run dev
npm run dev: 개발 서버 실행npm run build: 프로덕션 빌드npm run preview: 빌드된 결과물 미리보기npm run lint: 코드 린팅npm run format: 코드 포맷팅
- 실시간 협업 환경 구축:
Socket.io를 활용하여 여러 사용자가 동시에 접속하여 여행 계획을 세울 수 있는 워크스페이스를 구현했습니다. - 지도 및 일정 동기화:
usePoiSocket훅을 개발하여 마커 추가/삭제, 일정 변경, 커서 이동 등의 이벤트를 실시간으로 동기화했습니다. - 드래그 앤 드롭 일정 관리:
@dnd-kit을 사용하여 장소 보관함과 일자별 일정 간의 직관적인 드래그 앤 드롭 인터페이스를 구현했습니다. - PDF 내보내기 기능:
html2canvas와jspdf를 활용하여 완성된 여행 계획을 PDF 파일로 다운로드하는 기능을 구현했습니다.
- 카카오 지도 연동:
react-kakao-maps-sdk를 사용하여 지도를 띄우고, 마커, 커스텀 오버레이, 폴리라인 등을 렌더링했습니다. - 경로 시각화: 카카오 모빌리티 API를 연동하여 일정에 등록된 장소들 간의 이동 경로와 소요 시간을 지도 위에 시각화했습니다.
- 장소 상세 정보 패널: 장소 클릭 시 상세 정보를 보여주는 패널을 구현하고, 주변 장소 추천 기능을 연동했습니다.
- 반응형 레이아웃:
Tailwind CSS를 활용하여 다양한 화면 크기에 대응하는 반응형 레이아웃을 구성했습니다. - 모달 및 컴포넌트:
Radix UI기반의 재사용 가능한 UI 컴포넌트(모달, 버튼, 입력 폼 등)를 개발하고 스타일링했습니다. - 사용자 경험 개선: 로딩 인디케이터, 스켈레톤 UI, 토스트 알림 등을 적절히 사용하여 사용자 경험을 향상시켰습니다.
- 렌더링 최적화:
React.memo,useMemo,useCallback등을 활용하여 불필요한 리렌더링을 방지하고 성능을 최적화했습니다. - 빌드 오류 수정: 배포 과정에서 발생하는 빌드 오류를 해결하고, 환경 변수 설정 등을 관리했습니다.
- PDF 생성 최적화: 많은 양의 이미지와 지도가 포함된 PDF 생성 시 발생하는 성능 문제를 해결하고, 페이지 분할 로직을 개선했습니다.
- Custom Hook 설계 (
usePoiSocket): 소켓 연결의 생명주기를 관리하고,join,sync,mark,move등 다양한 이벤트를 캡슐화하여 컴포넌트 로직을 분리했습니다. - 낙관적 업데이트 (Optimistic UI): 네트워크 지연을 최소화하기 위해
markPoi등의 함수에서 서버 응답을 기다리지 않고 로컬 상태(pois)를 즉시 갱신하도록 구현했습니다. 이를 통해 사용자는 끊김 없는 경험을 할 수 있으며, 소켓 전송 실패 시 롤백 로직을 통해 데이터 정합성을 유지했습니다. - 커서 동기화 최적화: 마우스 이동 이벤트(
mousemove)가 과도하게 발생하는 것을 방지하기 위해 스로틀링(Throttling)을 적용하여CURSOR_MOVE이벤트를 효율적으로 전송하고, 다른 사용자의 위치를 실시간으로 부드럽게 시각화했습니다.
- 뷰포트 기반 데이터 페칭 (Viewport-based Fetching):
MapPanel컴포넌트에서 지도의idle이벤트를 감지하여 현재 사용자가 보고 있는 영역(getBounds)에 해당하는 장소 데이터만 웹소켓으로 요청하도록 구현했습니다. 이는 초기 로딩 속도를 획기적으로 개선하고 불필요한 데이터 전송을 줄였습니다. - 렌더링 성능 최적화:
useMemo를 활용하여 수천 개의 장소 데이터 중 현재 활성화된 카테고리와 일정에 포함된 장소만 필터링하여 렌더링하도록 최적화했습니다. 또한,React.memo로 마커 컴포넌트를 감싸 불필요한 리렌더링을 방지했습니다. - 경로 시각화 파이프라인: 카카오 모빌리티 API의 응답(
RouteSegment)을 파싱하여 지도 위에Polyline으로 경로를 그리고, 경로의 중간 지점을 계산하여 소요 시간과 거리를 표시하는 커스텀 오버레이를 동적으로 생성했습니다.
- 다중 컨텍스트 지원:
@dnd-kit을 사용하여 '장소 검색 결과', '장소 보관함', '일자별 일정'이라는 서로 다른 성격의 컨테이너 간 드래그 앤 드롭을 구현했습니다. - 상태 관리 분기 처리:
onDragEnd핸들러에서 드래그 소스(Source)와 타겟(Target) 컨테이너의 ID를 분석하여, 단순 순서 변경(reorderPois), 일정 간 이동(moveSchedule), 보관함 추가(markPoi) 등 다양한 시나리오에 맞는 소켓 이벤트를 발송하도록 정교하게 분기 처리했습니다.
- 동적 페이지네이션 알고리즘: 여행 일수가 늘어남에 따라 PDF 페이지를 동적으로 계산하는 로직을 구현했습니다. 첫 페이지에는 지도와 핵심 요약을 배치하고, 이후 페이지부터는 상세 일정을 3개씩 나누어 배치하여 가독성을 높였습니다.
- CORS 이미지 프록시 처리:
html2canvas로 화면을 캡처할 때 외부 이미지(카카오, 관광공사 등)로 인해 발생하는 CORS 오류(Tainted Canvas)를 해결하기 위해, 백엔드에 이미지 프록시 API(/proxy/image)를 연동하여 이미지를 우회 로딩하는 방식으로 PDF 생성 기능을 완성했습니다.
This project is part of a portfolio demonstrating advanced frontend development skills.