Privia Front · 호텔 POI 검색
Architecture
Stack
- Next.js 14
- React 18
- TypeScript
- Tailwind CSS
- Zustand
- pnpm workspace
External integrations
- Hotel POI Backend
- Google Maps
- GTM
Highlights
- POI 검색 (Quick / Result / Recent)
- 공휴일 인식 date picker
- 노출 임프레션 → GTM 이벤트
문제정의
기존 프리비아 호텔 검색은 카테고리 트리 중심이었다. 사용자는 도시 → 지역 → 등급 → 가격대를 차례로 좁히며 호텔을 찾아야 했고, "강남역 근처 5분 거리 4성급 호텔"처럼 공간 + 조건이 섞인 의도를 표현할 수 없었다. PC와 모바일 UX가 분리돼 일관성도 부족했다.
사업팀의 요청은 명확했다 — 키워드와 지도 기반 검색으로 전면 개편. 사용자가 자연어 키워드를 던지거나 지도에서 직접 영역을 탐색하면서 호텔을 발견할 수 있도록.
목표는 세 가지로 좁혔다.
- PC/모바일을 단일 코드베이스로 통합 — 같은 비즈니스 로직, 반응형 레이아웃.
- 지도와 리스트의 깊은 통합 — 클러스터링·반경 표시·오버레이뷰·양방향 선택까지.
- 분석으로 닫는 루프 — 노출 임프레션 → GTM 이벤트로 사업팀이 사용 패턴을 지속 측정.
구현
4-store Zustand로 도메인 분리
검색 페이지의 상태는 도메인이 명확하게 갈렸다. 각자 다른 라이프사이클이라 하나의 거대 store가 아니라 4개 도메인 store로 분리했다.
| Store | 책임 |
|---|---|
HotelPoiQSStore | QuickSearch — 키워드·날짜·인원 입력 상태 |
SearchResultStore | 결과 페이지 — 필터·정렬·페이지·선택 마커 |
RecentSearchStore | 사용자 최근 검색어 영속화 |
| (Fallback) | 입력 없을 때 추천 호텔 캐러셀 |
이 분리 덕에 검색바를 PC 레이어 모달에 띄우는 케이스와 모바일 바텀시트에 띄우는 케이스가 같은 store, 다른 컴포넌트로 자연스럽게 분기됐다.
PC/모바일을 단일 코드 + 반응형 BLC로
비즈니스 로직 컴포넌트(BLC)를 한 벌만 유지하고 PC/모바일 레이아웃을 위에 얹는 방식. 같은 HotelPoiSearchSection이 PC에서는 레이어 팝업, 모바일에서는 바텀시트로 렌더된다. 마커 클릭 시 동작도 통일 — PC는 맵 중앙으로 panBy, 모바일은 줌 레벨 조정으로 같은 "선택 시 강조" 의미를 살렸다.
Google Maps 깊은 통합 (잔버그의 산)
Google Maps SDK를 클러스터링 · 반경 Circle · InfoWindow 오버레이 · 마커-카드 양방향 선택 · 클러스터 제외 로직 수준까지 다뤘다. 운영하며 누적된 잔버그가 결국 컴포넌트 관심사 분리 리팩토링으로 이어졌다.
| 사고/요청 | 대응 |
|---|---|
| Circle이 깜박이는 문제, 마커가 겹쳐 보이는 문제 | 마커 렌더링과 Circle 렌더링 관심사 분리 |
| 모바일에서 panBy가 부자연스러움 | 모바일에서는 panBy 비활성, 줌만 사용 |
| 선택된 마커가 클러스터에 흡수돼 안 보임 | 클러스터링 조건에 줌 레벨 + 선택 상태 반영 |
view_poi 이벤트의 필드 누락 | GTM 페이로드 일원화 후 travelInfo 필드 추가 |
| 가격이 0원인 호텔 처리 | 필터링하지 않고 리스트 맨 하단으로 정렬 (사업팀 요구) |
지도와 리스트 사이의 양방향 인터랙션이 이 화면의 핵심 사용성이다.
사용성을 단단히 잡은 작은 훅들
useHolidays— 공휴일 API로 캘린더의 휴일을 시각 표시. 검색 의도가 자주 휴가/연휴 중심이라 의사결정에 직결.useItemVisibility— IntersectionObserver로 카드/캐러셀 임프레션 추적. 노출 자체가 사업팀 KPI.usePoiGa— GTM 페이로드 일원화.view_item_list,select_item,search이벤트를 한 표면에서 발사.
URL Search Params로 공유 가능한 결과 페이지
검색 상태(키워드·날짜·필터·정렬)를 URL에 그대로 반영해 링크 공유로 같은 결과를 재현할 수 있게 했다. 마케팅 캠페인이 검색 결과 페이지로 직접 랜딩하는 시나리오가 가능해졌다.
출시
4월 18일 첫 커밋, 6월 중순에는 검색 핵심 UX·지도·필터·캘린더·인피니트·GTM이 모두 안정화. 사실상 핵심 2개월 안에 출시 가능한 형태로 끝낸 프로젝트다.
배포 후 평가 두 가지:
- 전사 상반기 배포 서비스 중 가장 신속·정확한 일정 달성 사례로 평가받음.
- 서비스개발실 호텔매핑 과제의 주요 시작점으로 채택 — 이후 호텔 도메인 통합 작업이 이 코드와 도메인 이해 위에 쌓였다.
배포 이후에도 GTM 이벤트 파라미터 보강·UX 개선(반응형 디테일, 키워드 검색 팝오버 구조, 별 컬러, 캘린더 N박 디자인 등)이 234 커밋 누적까지 이어졌다.
결과학습
| 항목 | 결과 |
|---|---|
| 개발 일정 | 핵심 2개월에 출시 가능 상태 도달 |
| 전사 평가 | 상반기 배포 서비스 중 신속·정확 #1 |
| 후속 확장 | 서비스개발실 호텔매핑 과제의 주요 시작점 |
| 코드베이스 | PC + 모바일 단일 (BLC + 반응형) |
| 누적 변경 | 234 커밋 (배포 후 GTM·UX 보강 포함) |
프론트엔드 리드로서 가져간 학습은 네 가지다.
- 상태 도메인을 분리하면 PC/모바일 레이아웃 분기가 깔끔해진다. 4-store Zustand가 가장 큰 무기였다 — 같은 store를 PC 레이어 팝업·모바일 바텀시트가 공유하면서 화면만 갈렸다.
- Google Maps는 깊이 다룰수록 잔버그가 다발한다. Circle 깜박임·마커 겹침·panBy 부자연 같은 사고는 모두 관심사 분리 리팩토링으로 수렴. SDK 통합은 한번에 안 끝나고 반복 리팩토링이 필요한 영역이다.
- 노출 추적은 사업팀과 협업의 통로다.
view_item_list/select_item/search이벤트 페이로드를 사업팀과 정렬하는 과정에서 KPI 자체가 명료해졌다. 분석 이벤트는 기능이 아니라 협업 도구. - 공유 가능한 URL은 마케팅 자산이다. Search Params 전면화는 단순한 라우팅이 아니라 사업팀이 캠페인 링크를 만들 수 있는 인프라가 된다.
Next
- 호텔매핑 과제로의 도메인 확장 — 이미 시작점으로 평가받았으니, POI ↔ 호텔 메타데이터 매칭의 후속 작업이 자연스러운 다음 단계.
- AI 큐레이션과의 연결 —
privia-trip의 자연어 의도 분석 검색이 호텔 POI 검색의 진화 노선. 두 검색 표면을 단일 진입으로 묶을 여지. - 임프레션 데이터 기반 랭킹 —
useItemVisibility로 쌓인 노출·클릭 로그를 결과 정렬 가중치로 재투입.