데이터를 불러오는 중입니다...
기존 PHP 중심으로 운영되던 리조트 웹사이트에 React를 도입하여, 예약/로그인/마이페이지 등 핵심 구간의 UX를 개선한 프로젝트입니다. 1인 백엔드 겸 프론트엔드 개발자로서 기존 기술 스택을 전면 부정하지 않고, PHP 레거시와 React/Node.js가 충돌 없이 공존하는 아키텍처를 설계했습니다. 파트너사 API 연동, 결제 시스템 통합, 앱 WebView 세션 문제 해결까지 프로젝트 전반을 주도했습니다.
세 가지 이질적인 문제가 동시에 존재했습니다.
| 대안 | 접근 | 기각 사유 |
|---|---|---|
| 서브도메인 분리 | app.example.com에 React 배포 | CORS 이슈, 쿠키 공유 복잡, 기존 SEO/링크 깨짐 |
| PHP 전면 교체 | React로 전체 재개발 | 운영 중 서비스라 전면 교체 리스크 과대, 일정 부족 |
| 세션/JWT 이중 운영 | PHP는 세션, React는 JWT로 각각 유지 | 로그인 상태 동기화 불가, 사용자가 페이지 이동 시 재로그인 필요 |
| Nginx 경로 분기 + JWT 통일 (채택) | /app/*만 React로 분기, 인증을 JWT로 통일 | — |
Same-Origin을 유지하면서 점진적으로 React를 확장할 수 있고, 인증 체계를 하나로 통일하여 PHP↔React 간 로그인 상태를 자연스럽게 공유할 수 있는 구조가 필요했습니다.
/app/* 요청만 React 빌드 파일로 분기, 직접 URL 접근 시 index.html로 Fallback. Same-Origin 내에서 PHP와 React가 충돌 없이 공존하는 점진적 아키텍처 구축window.postMessage 방식으로 토큰을 네이티브 앱에 전달기존 시스템은 "결제 승인 → DB 저장 → 외부 API 호출" 로직이 물리적으로 파편화되어 있었습니다. 중간 단계에서 실패하면 DB에 예약은 생성되었으나 실제 결제가 누락 되는 데이터 불일치가 간헐적으로 발생했습니다.
| 대안 | 접근 | 기각 사유 |
|---|---|---|
| 2PC (분산 트랜잭션) | 결제-DB-외부API를 하나의 분산 트랜잭션으로 | PHP 환경에서 구현 과도, 외부 API가 XA 미지원 |
| 이벤트 소싱 | 결제 이벤트를 기록하고 비동기 처리 | 인프라 복잡도 과대, 실시간 결제 확인 필요한 서비스 특성과 부적합 |
| 보상 트랜잭션 (채택) | 단일 흐름으로 묶고, 실패 시 역방향 취소 | — |
결제는 사용자가 즉시 결과를 확인해야 하므로 동기식 흐름이 적합했습니다.
tid 존재 여부를 확인하여 즉시 PG사 결제 취소 API 호출. "결제는 됐는데 예약은 안 됨" 상황을 원천 차단초기 React 도입 시 클라이언트에서 외부 파트너사 API를 직접 호출하도록 구현했으나, 브라우저 개발자 도구 네트워크 탭에 API Key가 그대로 노출 되는 치명적 보안 이슈를 인지했습니다.
| 대안 | 접근 | 기각 사유 |
|---|---|---|
| 환경변수 난독화 | 빌드 시 Key를 번들에 포함하되 난독화 | 번들 분석으로 추출 가능, 근본 해결 아님 |
| 백엔드 프록시 (채택) | 모든 외부 API 호출을 서버 경유로 전환 | — |
클라이언트에 시크릿을 두는 것 자체가 근본적으로 잘못된 구조입니다.
이 챌린지에서 얻은 교훈과 BFF 아키텍처로의 발전 과정은 인사이트 API Key 노출 이슈와 BFF 아키텍처로의 발전 에서 깊이 있게 다루고 있습니다.
INIT → APPROVED → CONFIRMED → FAILED 등 명확한 상태 머신을 기반으로 결제 파이프라인을 설계하게 되었습니다.