뱅크몰 — 대출 비교 플랫폼
800만 유저 금융 플랫폼 뱅크몰에서 대출 신청 플로우·금융사 정책 로직·상담사 매칭까지 프론트엔드 구조를 재설계
역할 핵심 신청 플로우·정책 구조 설계 · 로직/UI 개발
Key Result
복잡한 대출 신청 플로우의 사용자 이탈과 운영 오류를 함께 크게 감소 · 금융사 정책 변경 영향 격리 · 상담사 업무 흐름 자동화
핵심 요약
800만 유저 금융 플랫폼 뱅크몰에서 담보·전세·신용·개인회생자 대출 신청 플로우, 금융사별 상담 신청 정책, 대출상담사 매칭까지 여러 도메인의 프론트엔드 구조를 재설계했습니다. 화면 단위 구현을 넘어 입력·진행·정책·외부 데이터의 책임을 분리해, 조건 하나를 바꿔도 다른 플로우가 흔들리지 않는 구조로 전환하고 사용자 이탈과 운영 오류를 함께 줄였습니다.
프로젝트 배경
뱅크몰은 금융사·담보 유형·사용자 조건에 따라 다음에 물어야 할 항목과 검증 기준이 매번 달라지는 대출 비교·신청 서비스입니다. 같은 "대출 신청"이라도 주택담보·전세·신용·개인회생자에 따라 필요한 서류, 외부 조회(KB시세·공시지가·주소), 금융사별 정책이 모두 다릅니다.
여기에 사용자 웹·운영자 CMS·대출상담사 웹·React Native 앱 2종까지 채널이 나뉘어, 같은 도메인 로직이 여러 화면에 흩어지기 쉬운 구조였습니다. 저는 이 중 핵심 신청 플로우와 정책·매칭 영역의 프론트엔드를 맡아 구조를 다시 설계했습니다.
담당 범위
- 주택담보·전세대출 신청 플로우 재설계 — 입력 상태와 Flow 상태 분리
- 신용·개인회생자대출 신청 라우팅 — URL as State + Step Guard
- 금융사별 상담 신청 로직·상품 목록 — Strategy Pattern으로 정책 격리
- 대출상담사 온·오프라인 매칭 — 입력 계약 스키마화·운영 변경 전달 자동화
- 레거시 코드의 점진적 타입·스타일 마이그레이션 (JS→TS, styled-components→Tailwind)
문제 정의
재설계 전 코드는 하나의 Context와 분기 로직이 입력값·진행 단계·외부 데이터·다음 단계 결정을 한꺼번에 떠안는 구조였습니다. 그 결과 다음 문제가 반복됐습니다.
- 상태 결합 — 입력 상태와 플로우 상태가 한 곳에 묶여, 조건 하나를 고치면 관련 없는 단계까지 영향이 번지고 회귀 범위를 예측하기 어려웠습니다.
- 정책 결합 — 금융사별 정책이 하나의 함수에 조건문으로 뭉쳐 있어, 특정 금융사 로직 수정이 다른 금융사까지 흔들었습니다.
- UI 중복 — 같은 UI가 여러 페이지에 복제돼, 한 번의 변경이 여러 파일 수정으로 이어졌습니다.
- 원인 추적 난이도 — 운영 오류가 접수돼도 상태·정책·외부 데이터 중 어디서 깨졌는지 판별할 계층이 없었습니다.
설계 원칙 — 상태를 종류별로 분리
가장 먼저 세운 기준은 "모든 상태를 같은 방식으로 다루지 않는다"였습니다. 성격이 다른 상태를 각자에 맞는 도구로 관리해 결합을 끊었습니다.
- 서버 상태 — 시세·정책·목록처럼 서버가 소유하는 데이터는 TanStack Query로 캐싱·무효화해, 컴포넌트가 원본을 복사·보관하지 않도록 함
- 클라이언트 전역 상태 — 세션·플로우 진행처럼 화면 간 공유되는 값은 Zustand로 분리
- 폼 상태 — 입력값·검증은 React Hook Form이 소유해 불필요한 리렌더링을 억제
- URL 상태 — "지금 몇 단계인지"처럼 새로고침·공유에도 보존돼야 하는 값은 URL로 표현
기술적 의사결정
1. 입력 상태와 Flow 상태를 분리한 Multi-step 구조
- 배경 — 다단계 신청은 "사용자가 입력한 값"과 "지금 어느 단계이고 다음이 무엇인지"가 서로 다른 관심사인데, 초기 구조는 이 둘이 한 상태에 묶여 있었습니다.
- 선택 — 각 단계를 관심사 단위로 나누고, 입력 데이터와 플로우 상태를 별개 책임으로 관리했습니다. 외부 데이터(KB시세·공시지가·주소) 조회·변환은 화면 밖 모듈로 빼, UI가 데이터 가공까지 떠안지 않게 했습니다.
- 트레이드오프 — 모든 단계를 아우르는 범용 Form 하나로 추상화하는 방법도 있었지만, 담보 유형마다 필드·검증이 달라 과추상화하면 분기 지옥이 됩니다. 공통은 얇게, 차이는 단계별로 두는 쪽을 택했습니다.
2. URL을 상태 모델로 + Step Guard
- 배경 — 신용·개인회생 플로우는 새로고침·뒤로가기·링크 공유에도 단계가 유지돼야 하는데, 상태를 메모리에만 두면 이탈·유실이 발생합니다.
- 선택 — pathname + query로 신청 단계를 표현해 브라우저 히스토리와 자연스럽게 맞물리게 했습니다.
- 보완 — URL은 사용자가 임의로 조작할 수 있으므로, 라우트 진입 전 Step Guard로 이전 단계 데이터·유효성을 검증해 비정상 진입을 차단했습니다. URL을 상태로 신뢰하되 검증 계층으로 방어하는 구조입니다.
3. 금융사 정책을 Strategy Pattern으로 격리
- 배경 — 금융사가 늘어날수록 조건문 하나에 정책이 쌓이면, 신규 추가가 기존 로직을 건드리는 구조가 됩니다.
- 선택 — 금융사별 로직이 같은 인터페이스를 따르되 독립적으로 유지되도록 Strategy로 분리해, 신규 금융사를 기존 조건문 수정 없이 Strategy 추가만으로 확장(OCP)했습니다.
- 의도적 중복 허용 — 정책 일부는 공통화하지 않고 의도적으로 중복을 남겼습니다. 지금은 같아 보여도 각 금융사가 독립적으로 바뀔 값이라, 섣부른 공통화가 오히려 변경을 전파시키기 때문입니다. "같은 이유로 바뀌는 것만 함께 둔다"를 기준으로 삼았습니다.
4. 입력 계약의 스키마화 + 운영 변경 자동 전달
- 배경 — 상담사 매칭은 입력 항목이 많고 정책도 자주 바뀌어, 검증 규칙이 화면 곳곳에 흩어지면 유지보수가 어렵습니다.
- 선택 — Zod로 입력 계약을 스키마화하고 React Hook Form과 연결해, 클라이언트 검증과 에러 메시지를 단일 소스에서 관리했습니다. 다만 최종 판단은 서버에 남겨 클라이언트 검증은 UX 보조로만 두었습니다.
- 운영 연계 — 정책·공통 코드 변경은 Slack Webhook으로 운영팀에 자동 전달해, 코드 변경과 운영 인지 사이의 공백을 줄였습니다.
5. 점진적 타입·스타일 마이그레이션
운영 중인 서비스라 한 번에 갈아엎을 수 없어, JavaScript→TypeScript, styled-components→Tailwind CSS를 점진적으로 전환했습니다. 새로 만지는 파일부터 타입을 입히고 스타일을 옮겨, 기능 회귀 없이 코드베이스를 개선했습니다.
결과
- 담보대출 신청 이탈과 운영 환경 오류 접수를 함께 크게 감소
- 새로고침·뒤로가기·직접 URL 접근에도 신청 단계 유지, 비정상 진입 사전 차단
- 동일 UI 수정 파일 5개 → 1개로 축소, 금융사별 정책 변경 영향 격리
- 상담사 입력 오류 감소 · 운영 변경 인지 시간 단축 · 해당 영역 Frontend 약 70% 기여