-
왕초보가 작성한 토스 퍼널의 아주 기초적인 사용법React 2024. 1. 19. 21:07
퍼널(Funnel)
이번 웹잼에서 온보딩 파트를 맡게 되었고, 어떤식으로 구현해야 할지 고민하였는데, 처음에 생각했던 방식은, ‘다음’버튼을 클릭했을 때 navigate로 이동시키는 방법이었다. 하지만 총 6단계로 이루어진 온보딩 파트였고, 공통으로 쓰이는 상태가 있을 것 같다고 판단했다. 이를 위해서 SOPT 웹파트 3주차 리액트 과제를 떠올렸다. 당시 나는 step에 따른 조건부 렌더링 방식을 선택했었고, 많은 props drilling이 발생하긴 했지만 조금 고도화 한다면 충분히 좋은 방법이라 생각했다. 그 즈음 예전에 우리 파트 OB분에게 지나가듯이 들었던 퍼널이 생각나서 검색 후 모든 자료를 뒤져보면서 공부하였다.
- 왜 이름이 퍼널일까?
- 퍼널의 뜻은 ‘깔때기’이다.
- 유저들의 잔존률이 깔때기 처럼, 스텝이 진행될 수록 빠져나가고 남은 유저의 비율이 마치 깔때기 같다고해서 붙여졌다고 한다.
- 퍼널이란 무엇이며 어떤 상황에 사용하는 것이 좋을까?
- 퍼널이란 state로 컴포넌트만 갈아끼우는 방식이다.
- 연속된 form이 많으며, 유저의 연속적인 입력이 많아, 뒤로가기를 누를 필요가 적은 곳에서 사용할 때 좋다고 한다.
- 전체적인 흐름을 일단 살펴보자
- 우선 우리 스윗의 퍼널 패턴을 보면,
우선 constants.ts에 퍼널 스텝의 이름을 상수화 한다. export const ONBOARDING_FORM_STEP = [ 'NAME', 'THUMBNAIL', 'PRESENT', 'TOURNAMENT_SCHEDULE_REGISTRATION', 'TOURNAMENT_PROCEEDING', 'GIFT_ROOM_FIX', ] as const;
벌써부터 머리가 지끈지끈 하다. 타입스크립트의 내장 유틸함수인 Omit은 또 뭐고, 제네릭이 너무 많고… 분명히 좋고 멋있는 코드인 것 같은데 너무 읽기 어렵다.useMemo로 해당 부분만 관리하고 있는 느낌이 들며, 변수명들이 steps, defaultStep인걸 보니 단계별로 스텝을 갈아끼우는 것 같은 느낌이 들었다.그 이후 최상위 컴포넌트에서 useFunnel 커스텀 훅을 받아준다. OnboardingPage.tsx const { Funnel, setStep } = useFunnel(ONBOARDING_FORM_STEP, ONBOARDING_FORM_STEP[0]); useFunnel.tsx import { useMemo, useState } from 'react'; import { NonEmptyArray } from '../types/utility'; import { Funnel, FunnelProps, Step } from '../components/common/Funnel/Funnel'; export const useFunnel = <Steps extends NonEmptyArray<string>>( steps: Steps, defaultStep: Steps[number], ) => { const [step, setStep] = useState<Steps[number]>(defaultStep); const FunnelComponent = useMemo( () => Object.assign( (props: Omit<FunnelProps<Steps>, 'step' | 'steps'>) => ( <Funnel<Steps> step={step} steps={steps} {...props} /> ), { Step }, ), [step], ); return { Funnel: FunnelComponent, step, setStep }; };
퍼널 스텝의 이름을, 좀 전에 constants에서 상수화 시켰던 것들로 1단계 스텝부터 6단계 스텝까지 작성한다.앞서 말한 useFunnel을 보며 사용법을 알아보자. 최상위 컴포넌트인 OnboardingPage.tsx <Funnel> <Funnel.Step name='NAME'> <OnBoardingHeader step={1} onClick={handleFirstHistoryClick} /> {/* step01 */} <NameInput onNext={() => setStep(() => 'THUMBNAIL')} /> </Funnel.Step> <Funnel.Step name='THUMBNAIL'> <OnBoardingHeader step={2} /> {/* step02 */} <ThumbnailInput onNext={() => setStep(() => 'PRESENT')} /> </Funnel.Step> <Funnel.Step name='PRESENT'> <OnBoardingHeader step={3} /> {/* step03 */} <GiftDelivery onNext={() => setStep(() => 'TOURNAMENT_SCHEDULE_REGISTRATION')} /> </Funnel.Step> <Funnel.Step name='TOURNAMENT_SCHEDULE_REGISTRATION'> <OnBoardingHeader step={4} /> {/* step04 */} <SetTournamentSchedule onNext={() => setStep(() => 'TOURNAMENT_PROCEEDING')} /> </Funnel.Step> <Funnel.Step name='TOURNAMENT_PROCEEDING'> <OnBoardingHeader step={5} /> {/* step05 */} <SetTournamentDuration onNext={() => setStep(() => 'GIFT_ROOM_FIX')} /> </Funnel.Step> <Funnel.Step name='GIFT_ROOM_FIX'> {/* step06 */} <OnboardingFinal /> </Funnel.Step> </Funnel>
- 마지막 퍼널인 GIFT_ROOM_FIX 에서는 마지막이기 때문에 당연히 다음 버튼이 없다.
- 퍼널을 사용해보니 장점은,사실상 최상위 컴포넌트에서만 상태를 관리해주면 되기 때문에토스의 라이브러리를 더 뜯어보면 좋았겠지만, 우선은 구현을 했다는 것에 만족하며…
- 데이터의 흐름을 파악하기가 굉장히 쉽고,
- 상태가 흩어져있지 않게 할 수 있었다.
- 전체 코드는 토스 홈페이지에 나와있으니 참고해봅시다!
- 근데 대충봐서도 느낌이, 컴포넌트를 갈아끼우는 코드라는게 느껴지지 않는가?
'React' 카테고리의 다른 글
모던 리액트 Deep dive - 클래스, 클로져 (3) 2024.02.18 모던 리액트 Deep Dive - 리액트 개발을 위해 꼭 알아야 할 JS (1) 2024.02.12 Funnel에서의 상태관리 고민 (0) 2024.01.30 리액트에서 .env 사용하기 (카카오 로그인) (0) 2023.11.06 [React] to do app 만들기 (0) 2023.07.11 - 왜 이름이 퍼널일까?