✔ 도입한 기술(라이브러리)과 이유 (없다면 생략 가능)
useSearchParams
(react-router-dom)→ URL 쿼리스트링으로 퍼널 단계를 관리하기 위해 도입했다. 새로고침이나 공유 시에도 현재 단계를 유지할 수 있다.
커스텀 훅
useFunnel
→ 퍼널 단계 관리, 이전/다음 단계 이동, 유효성 검사 등을 재사용 가능한 형태로 분리해 구조화했다.
✔ 문제 요약
퍼널 구조의 온보딩을 구현하면서 다음과 같은 문제가 발생했다:
- 온보딩 도중 사용자가 '그룹 매칭'을 선택했을 때, 퍼널 흐름이 갈라지는 구조를 어떻게 설계할지.
- 퍼널 단계를 기준으로 프로그레스 바를 표시하려 했으나, 전체 스텝 수가 동적으로 바뀌는 상황에서 진행률을 어떻게 계산할지.
- 새로고침 시 현재 단계와 선택값이 유지되지 않아 진행 중이던 온보딩이 초기화되는 UX 문제.
- 이미 온보딩을 완료한 사용자가 URL로 다시 접속했을 때 어떻게 처리할지 (재접근 허용? 홈 리디렉션? 에러 페이지?).
✔ 원인 분석
1. 퍼널의 동적 분기 구조
처음에는 하나의 useFunnel
훅 안에서 MATCHING_TYPE
에 따라 분기 처리를 하려 했으나,
- 분기 전에는 전체 스텝의 개수를 알 수 없어
ProgressBar
계산에 문제 발생 GROUP_FUNNEL_STEPS
는FIRST_FUNNEL_STEPS
와 구조도 다르고, 쿼리스트링도 별도로 관리되어야 하므로 결국 두 퍼널을 분리함
2. 새로고침 시 상태 초기화
- 상태(state)는
useState
로만 관리되고 있었고, URL은 쿼리스트링으로 현재step
만 유지됨 - 새로고침 시
selections
객체가 초기화되므로, 이전에 선택했던 값들이 모두 사라짐 → 퍼널 흐름상 뒤로 돌아가도 UX가 불안정해짐
3. 완료된 사용자의 재접근
- API를 통해 서버에
POST
요청을 보낸 후, 사용자가/onboarding
주소로 다시 접속할 수 있는 구조였음 - 다시 접속 시 완료된 사용자도 온보딩 흐름으로 진입 가능해 의도치 않은 재요청 또는 중복 입력이 발생할 수 있음
✔ 해결 방법
1. 퍼널 구조 분리 및 navigate
활용
MATCHING_TYPE
단계에서 "그룹"을 선택하면navigate('/onboarding/group?step=GROUP_ROLE')
로 이동- 일반 온보딩과 그룹 온보딩을 완전히 다른 퍼널 흐름으로 분리하여
useFunnel(GROUP_FUNNEL_STEPS)
을 각각 적용 goTo
를 통해 쿼리스트링만 변경하며 단계 이동 → 새로고침 후에도 해당 단계 유지 가능
2. ProgressBar
와 동적 스텝 수 처리
- 일반 퍼널에서는
ProgressBar currentStep={progressOverride ?? currentIndex}
로 계산 - 그룹 퍼널로 분기된 이후에는
GROUP_FUNNEL_STEPS.length
를 기준으로 별도로ProgressBar
렌더링 progressOverride
state를 통해 조건부로 오버라이딩 가능하게 구현
3. 새로고침 시 단계 유지 및 에러 방지
useFunnel
내부에서 쿼리스트링(?step=X
)이 없거나 유효하지 않을 경우,→
setSearchParams({ step: steps[0] })
으로 강제 초기화→ 잘못된 접근은
navigate(ROUTES.ERROR)
로 에러 페이지 유도
useEffect(() => {
if (!stepFromUrl) {
setSearchParams({ step: steps[0] }, { replace: true });
}
if (!isValidStep) {
navigate(ROUTES.ERROR, { replace: true });
}
}, [...]);
4. 완료된 사용자의 재접근 처리
- 서버에서 이미 온보딩을 완료한 사용자의 경우
/onboarding
접속 시 홈으로 redirect 되도록 가드 필요 - 클라이언트에서
GET /my/onboarding-status
와 같은 API를 통해 진행 여부 판단 → 진입 제한하거나 중간단계에서 redirect 처리
✔ 회고 및 개선 방향
퍼널 UI는 정적인 흐름을 가정하면 안 될 것 같다.
특히 UX 흐름 상 사용자의 선택에 따라 다음 단계가 달라지는 경우가 많기 때문에,
퍼널 전체 단계를 하나의 고정된 리스트로 처리하기보다 분기별 퍼널을 나누고 구조적으로 분리하는 게 안정적이었다.
ProgressBar 계산은 퍼널 흐름에 강하게 의존한다.
‘전체 단계 수를 어디까지 계산할 것인가’라는 문제는 분기를 포함하면 더 복잡해지므로,
분기된 퍼널 흐름마다 고유한
useFunnel
을 관리하고progressOverride
등을 활용하는 전략이 효과적이었다.URL 기반 상태 관리의 장점
useSearchParams
를 통해 퍼널 단계를 URL에 명시한 덕분에, 새로고침/뒤로가기/직접 접근 등의 시나리오에서도 상태를 안정적으로 유지할 수 있었다.상태 유지와 초기화의 균형 필요
selections를 로컬스토리지에 저장할지 여부는 아직 보류했지만, 초기에는 완전히 사라지는 UX가 너무 불친절해서 추후에는
step
만 유지하고 값은 임시적으로sessionStorage
에 저장하는 방안을 고려할 수 있을 듯하다.중복 접근 방지 필요
이미 완료된 사용자의
/onboarding
재접근 이슈는 아직 명확한 UX 기준이 없다면 서버에서 진입 제한 flag를 내려주는 구조로 처리하는 것이 안전할 것 같다.