상태 기반 흐름 전환

» FE

TeachTouch : 디지털 약자를 위한 키오스크 학습 가이드 서비스📚

Github: 2025_SEASONTHON_TEAM_77_FE

2025.09


상태 기반 흐름 전환

가이드를 구현하는 중 각 카테고리별로 화면이 너무 다르게 생겨서 공용 컴포넌트를 만들어 여러 카테고리에서 사용하긴 어려워보였다.

-> 카테고리별로 컴포넌트를 만들고 그 안에서 제어하기로 결정

그렇다면 학습 시나리오를 어떻게 구성해야할까. 고려사항은 다음과 같았다.

  • 각 카테고리는 intro, content, complete라는 흐름으로 진행
  • intro, content, complete 화면이 각각 전환되지만 화면 구조는 크게 다르지 않고, 각 단계는 동일한 컴포넌트 위에 모달, 툴팁, 오버레이, 하드웨어 위치 강조 등 시각적 요소들이 쌓이며 변화
  • 특정 조건이 충족될 때 다음 단계로 넘어갈 수 있도록
  • 카테고리 내부 상태는 저장될 필요 없이 시작과 끝만 저장되면 됨
  • 사용자의 경로는 시작과 끝을 통해서만 이동(중간 콘텐츠로 url 변경해서 들어갈 수 없도록)

따라서 하나의 컴포넌트 내부에서 상태를 기반으로 전체 흐름을 관리하기로 결정했다.

이유는?

  1. 라우팅 기반 전환은 url 변경을 수반하므로 사용자가 url을 직접 변경해 중간 과정으로 들어갈 수도 있음
  2. 특정 조건이 충족될 때만 다음 단계로 넘어갈 수 있도록 해야 하는데 사용자가 url 직접 입력, 뒤로가기, F5 등으로 의도되지 않은 이동을 할 수 있어 학습 시나리오가 깨질 수 있음
  3. 화면 간 전환보다는 같은 뷰 안에서 맥락의 변화를 표현해야 하는 경우에 가까움

구현 방식

const [page, setPage] = useState<'intro' | 'content' | 'complete'>('intro');

현재 보여줄 화면을 결정하는 라우터 역할

intro와 complete 단계에선 IntroScreen, CompleteScreen컴포넌트를 만들어서 여러 카테고리에서 재활용했다.

안에 content 부분은

if (step !== null && step < structureSteps.length - 1) {
  setStep(step + 1);
} else {
  setStep(null);
  setPage('complete');
}

step을 사용해 화면 내 세부 단계를 제어하게 했다.


확실히 useState를 쓰다보니 코드가 길어진 감이 있긴 하다. 단순한 페이지 흐름 제어에서 useState를 쓴 것은 나름 합리적인 선택이긴 했지만 다른 대안들도 있었을 것 같다.

  1. useReducer
     const reducer = (state, action) => {
     switch(action.type) {
         case 'TO_CONTENT': return { page: 'content', step: 0 };
         case 'NEXT_STEP': return { ...state, step: state.step + 1 };
         ...
     }
     }
    

    상태 간 의존성이 있거나 전이가 복잡할 경우에 유용하다는데 우린 단순하므로 패스

  2. useSearchParams, useLocation + useNavigate
     const [searchParams, setSearchParams] = useSearchParams();
     useEffect(() => {
     const step = Number(searchParams.get('step')) || 0;
     setStep(step);
     }, []);
    

    학습을 도중에 나갔다가 다시 이어서 해야 하는 경우가 있다면 유용할 것 같다.

  3. Redux, Zustand 등 전역 상태

    학습 완료율 등을 추적하는 대시보드 등과 연동하거나 전역으로 학습 흐름 상태를 관리해야 한다면 유용할 것 같다. 하지만 해커톤이기도 하고 전역 상태는 비용이 과도할 것 같다는 생각에 패스

지금 단계에서는 useState로도 충분하지만 확장을 고려한다면 다른 훅을 고려해봐도 좋을 것 같다.