데이터를 불러오는 중입니다...
단일 호텔에 종속되어 있던 기존 예약 페이지를 다수의 파트너사(호텔)에 유연하게 적용할 수 있는 플랫폼 아키텍처로 전환했습니다. 프론트엔드 2인 팀 내에서 설계 주도 역할을 맡아, 코드 수정 없이 신규 프로젝트를 온보딩할 수 있는 Config-driven 구조 도입, 복잡한 예약 상태 관리 캡슐화, 크로스 플랫폼 결제 정합성 보장까지 핵심 아키텍처 결정을 담당했습니다.
결제 방식, 복합 예약(호텔+골프), 다국어, 회원 정책 등 파트너사마다 다른 요구사항을 공통 코드 내 if-else 분기로 처리했습니다. 신규 호텔 온보딩 시마다 공통 컴포넌트를 직접 수정해야 했고, A 호텔 수정이 B 호텔 UI에 사이드 이펙트를 유발하는 상황이 반복되었습니다. 공통 버그가 발생하면 복제된 N개 프로젝트를 모두 수정·배포해야 했습니다.
| 대안 | 접근 | 기각 사유 |
|---|---|---|
| 복제 코드 유지 + 분기 정리 | 기존 방식의 if-else를 상수로 추출해 정리 | 분기 자체가 남아 있어 확장할수록 복잡도 재증가 |
| 모노레포 + 패키지 분리 | 공통 로직을 npm 패키지로 분리 | 빌드 파이프라인 오버헤드가 현 팀 규모에 과도함 |
| Config-driven 아키텍처 | 관리자 페이지에서 Feature를 ON/OFF, 코드 수정 없이 동작 | — |
Config 데이터를 앱 최상단에서 한 번에 주입하면, 하위 컴포넌트는 설정값만 읽어 조건부 렌더링을 수행하므로 비즈니스 분기가 코드 밖으로 이동합니다.
useConfig() 훅으로 Feature Flag를 읽어 조건부 렌더링을 자체적으로 결정하도록 설계했습니다.이 챌린지의 기술적 상세는 인사이트 Config-driven 아키텍처로 React 프로젝트 파편화 막기 에서 다루고 있습니다.
객실 선택 → 정보 입력 → 결제로 이어지는 복잡한 상태를 App.tsx 최상단에서 전역으로 관리했습니다. 예약 상태 하나가 바뀌어도 무관한 결제 컴포넌트가 리렌더링되었고, 결제 도메인 밖의 컴포넌트에서도 useContext로 예약 상태에 접근해 예기치 않은 부수 효과를 유발할 수 있었습니다.
| 대안 | 접근 | 기각 사유 |
|---|---|---|
| Redux Toolkit | 전역 스토어 중앙화, 강타입 슬라이스 | 보일러플레이트 부담, 소규모 팀에 과도한 복잡도 |
| Zustand | 경량 전역 스토어, 간결한 API | 도메인 경계 강제 불가, 어느 컴포넌트에서나 접근 가능 |
| Context API + 라우터 레벨 격리 | 도메인별 Provider를 각 라우터에 배치, Custom Hook으로 접근 제한 | — |
라우터 레벨 격리는 해당 도메인 진입 시에만 Context가 마운트되므로, 별도 라이브러리 없이 스코프와 리렌더링 범위를 동시에 통제할 수 있다고 판단했습니다.
이 챌린지의 기술적 상세는 인사이트 Context API 지옥에서 벗어나 라우터 레벨 캡슐화 달성하기 에서 다루고 있습니다.
모바일 환경에서는 PC와 달리 PG 결제가 페이지 이동(Redirect) 방식으로 진행됩니다. 결제창으로 이동하는 사이 사용자의 입력 데이터가 유실되는 문제와, 복귀 시점에 재고·가격이 이미 변동되어 결제 금액과 실제 데이터가 불일치하는 두 가지 리스크가 동시에 존재했습니다.
| 대안 | 접근 | 기각 사유 |
|---|---|---|
| sessionStorage만 사용 | 탭 단위 임시 저장 | 브라우저 종료·새 탭 복귀 시 데이터 유실 |
| 서버 임시 저장 | 결제 전 상태를 서버 세션에 저장 | 별도 API·세션 관리 비용, 불필요한 서버 의존도 증가 |
| localStorage TTL + 서버 중심 재조회 | 입력값은 클라이언트 캐시, 가격·재고는 복귀 시 서버 재조회 | — |
클라이언트 상태(사용자 입력)와 서버 상태(재고·가격)를 명확히 분리하면, 각자의 신뢰 소스를 올바르게 사용할 수 있다고 판단했습니다.