문서보기
시작하기

시작하기

@use-funnel을 사용해 간단한 회원가입 흐름을 구현해 볼게요. 각 단계의 상태를 정의하고, 이를 통해 단계별로 상태를 안전하게 관리하는 방법을 배울 수 있어요.

stepcontext 정의하기

회원가입 과정은 여러 step으로 나눌 수 있어요. 여기서는 이메일 입력, 비밀번호 입력, 그 외 정보 입력의 세 step으로 나눠볼게요. 그리고 각 step마다 필요한 상태를 다음과 같이 타입으로 정의할게요.

context.ts
// 1. 아무것도 입력 안됨
type 이메일입력 = { email?: string; password?: string; other?: unknown }
// 2. 이메일은 입력됨
type 비밀번호입력 = { email: string; password?: string; other?: unknown }
// 3. 이메일과 비밀번호 입력됨
type 그외정보입력 = { email: string; password: string; other?: unknown }
  • 이메일입력: 회원가입의 첫 step이에요. 이메일과 비밀번호 입력 필드가 있지만, 아직 아무것도 입력되지 않은 상태에요. emailpassword는 둘 다 선택(optional) 필드로 정의되어 있어요.

  • 비밀번호입력: 회원가입의 두 번째 step이에요. 사용자가 이메일을 입력한 후 비밀번호를 입력하는 step이에요. 이 step에서는 email 필드가 필수적으로 입력되어 있어야 하고, password는 선택 필드에요.

  • 그외정보입력: 회원가입의 세 번째 step이에요. 사용자가 이메일과 비밀번호를 모두 입력한 후 추가 정보를 입력하는 step이에요. 이 step에서는 emailpassword가 모두 필수적으로 입력되어 있어야 해요.

이렇게 각 step의 상태를 타입으로 정의하면 코드에서 타입 안전성을 유지할 수 있고, step별로 필요한 정보를 쉽게 추적할 수 있어요.

초기 단계 설정하기

이제 useFunnel()을 사용해 초기 단계를 설정해 볼게요.

먼저 step을 key로 한 context 객체를 useFunnel()의 제네릭으로 지정해요. 앞 단계에서 각 단계의 상태를 정의한 타입을 useFunnel()에 전달해서 사용하는 거에요. 해당 컴포넌트에 진입했을 때 사용할 stepcontext 객체를 initial 프로퍼티에 지정해요.

여기서는 초기 단계인 "이메일입력"과 해당 단계에서 사용할 빈 context 객체를 설정했어요. id한 컴포넌트에 여러 퍼널이 있을 때 구분하기 위한 고유 식별자에요.

import { useFunnel } from "@use-funnel/next";
import type { 이메일입력, 비밀번호입력, 그외정보입력 } "./context";
 
function MyFunnelApp() {
  const funnel = useFunnel<{
    이메일입력: 이메일입력;
    비밀번호입력: 비밀번호입력;
    그외정보입력: 그외정보입력;
  }>({
    id: "my-funnel-app",
    initial: {
      step: "이메일입력",
      context: {}
    }
  });
  // ...
}

이 방법 이외의 step 별 상태 정의는 상태 정의 가이드를 참고하세요.

단계별 contexthistory 사용하기

useFunnel()에서 반환된 step에 따라 contexthistory를 사용해요. 각 단계별로 UI를 구성하고, 필요한 상태와 이벤트를 처리할 수 있어요.

declare function 이메일입력(props: { onNext: (email: string) => void }): JSX.Element;
declare function 비밀번호입력(props: { email: string; onNext: (password: string) => void }): JSX.Element;
declare function 그외정보입력(): JSX.Element;
 
switch (funnel.step) {
  case '이메일입력':
    return <이메일입력 onNext={(email) => funnel.history.push('비밀번호입력', { email })} />;
  case '비밀번호입력':
    return (
      <비밀번호입력
        email={funnel.context.email} // 이메일 입력에서 입력했기 때문에 undefined가 아니에요!
        onNext={(password) => funnel.history.push('그외정보입력', { password })}
      />
    );
  case '그외정보입력':
    return <그외정보입력 />;
}
  • funnel.context: 현재 stepcontext를 가져올 수 있어요. 예를 들어, "이메일입력" 단계에서 funnel.context.emailstring | undefined 타입이지만, "비밀번호입력" 단계에서는 string 타입으로 추론할 수 있어요.

  • funnel.history.push(): 다음 단계로 넘어갈 수 있어요. push()의 첫 번째 인자로 step을, 두 번째 인자로 해당 step 으로 진입하기 위해 필요한 context 를 받아요.

  • funnel.history.replace(): funnel.history.push()와 기본 동작은 같지만, 히스토리를 쌓지 않고 현재 step 을 덮어씌워요.

참고: <funnel.Render /> 컴포넌트를 사용해 쉽게 구현하기

혹은 useFunnel()이 반환하는 <funnel.Render /> 컴포넌트를 사용할 수 있어요. 이 컴포넌트를 사용하면 단계별 UI를 더 간편하게 관리할 수 있어요.

return (
  <funnel.Render
    이메일입력={({ history }) => (
      <이메일입력 onNext={(email) => history.push('비밀번호입력', { email })} />
    )}
    비밀번호입력={({ context, history }) => (
      <비밀번호입력
        email={context.email}
        onNext={(password) => history.push('그외정보입력', { password })}
      />
    )}
    그외정보입력={() => <그외정보입력 />}
  />
)

<funnel.Render /> 컴포넌트를 사용하는 자세한 방법은 레퍼런스를 참고해주세요.