내일배움캠프
[241202 TIL] 본캠프 44일차 (Funnel 패턴)
gnchoco97
2024. 12. 2. 20:27
Funnel 패턴
Funnel은 깔대기를 의미한다. 즉, Funnel 패턴은 마치 깔대기 모양처럼 사용자가 단계를 거치면서 정보를 입력하는 UI 설계 방식을 말한다.
사용자가 여러 정보를 입력할 때, 모든 것을 한 번에 입력하는 것이 아니라 단계를 나눠서 하나씩 처리하기 때문에 깔대기처럼 점점 좁아지는 형식처럼 보인다.
기존 방식
각 단계별로 별도의 컴포넌트를 만들고 전역 상태를 사용해서 데이터를 관리한다. 페이지 간의 이동은 라우터로 처리하고, 최종 단계에서 모든 데이터를 수집해서 API를 호출한다.
- 각 단계가 별도의 파일이나 컴포넌트로 분산되어 있어 흐름 파악이 어려움
- 상태 변경이 여러 컴포넌트에서 이루어지기 때문에 데이터 흐름을 추적하기 어려움
- 코드 수정 시 여러 파일을 수정해야하기 때문에 유지보수가 어려움
- 라우터에 강하게 의존
import { useState } from 'react';
import { BrowserRouter as Router, Route, Switch, useHistory } from 'react-router-dom';
function Registration() {
const [userData, setUserData] = useState({});
const history = useHistory();
return (
<Router>
<Switch>
<Route path="/step1">
<Step1
onNext={(data) => {
setUserData({ ...userData, ...data });
history.push('/step2');
}}
/>
</Route>
<Route path="/step2">
<Step2
onNext={(data) => {
setUserData({ ...userData, ...data });
history.push('/step3');
}}
/>
</Route>
<Route path="/final">
<FinalStep onSubmit={() => apiCall(userData)} />
</Route>
</Switch>
</Router>
);
}
Funnel 패턴 방식
Funnel 패턴을 적용하여 상태 관리와 페이지 흐름을 하나의 컴포넌트에서 관리함으로써 기존 방식의 문제점을 해결할 수 있다.
- 상태 관리와 페이지 흐름을 하나의 컴포넌트에서 처리해서 코드가 간결해짐
- 모든 상태 변경이 한 곳에서 이루어지므로 데이터 흐름 추적이 쉬움
- 코드 수정이 쉬움
- 라우팅 없이도 단계 전환 가능
import { useState } from 'react';
function Registration() {
const [userData, setUserData] = useState({});
const [step, setStep] = useState('가입방식');
const handleNext = (data, nextStep) => {
setUserData((prev) => ({ ...prev, ...data }));
setStep(nextStep);
};
return (
<div>
{step === '가입방식' && (
<SignUpMethod onNext={(data) => handleNext(data, '주민번호')} />
)}
{step === '주민번호' && (
<ResidentNumber onNext={(data) => handleNext(data, '주소입력')} />
)}
{step === '주소입력' && (
<AddressInput onNext={(data) => handleNext(data, '가입성공')} />
)}
{step === '가입성공' && <SuccessPage userData={userData} />}
</div>
);
}
useFunnel 생성
상태 관리와 페이지 전환 로직을 캡슐화하기 위해서 Custom Hook을 생성해서 사용한다.
import { useState } from 'react';
function useFunnel(initialStep) {
const [currentStep, setCurrentStep] = useState(initialStep);
const Step = ({ name, children }) => {
return <>{children}</>;
};
const Funnel = ({ children }) => {
const steps = React.Children.toArray(children).filter(
(child) => child.type === Step
);
const activeStep = steps.find(
(child) => child.props.name === currentStep
);
return activeStep || null;
};
const next = (nextStep) => {
setCurrentStep(nextStep);
};
const prev = (prevStep) => {
setCurrentStep(prevStep);
};
return { Funnel, Step, next, prev, currentStep };
}