티스토리 뷰
API 호출에 필요한 상태, 한 눈에 관리하기
const [registerData, setRegisterData] = useState()
const [step, setStep] = useState<"가입방식"|"주민번호"|"집주소"|"가입성공">("가입방식")
return (
<main>
{step === "가입방식" && <가입방식 onNext={(data) => {
setRegisterData(prev => ({ ...prev, 가입방식: data }))
setStep("주민번호")
}} />}
{step === "주민번호" && <주민번호 onNext={() => setStep("집주소")} />}
{step === "집주소" && <집주소 onNext={async () => {
await fetch("/api/register", { data }) // API 호출 장소변경
setStep("가입성공")
}} />}
{step === "가입성공" && <가입성공 />}
</main>
)
위의 코드를 "Step"이 추가되더라도 유연하게 관리할 수 있도록 수정이 필요하다.
아래는 "회사주소"라는 Step이 추가된 예시이다.
const [registerData, setRegisterData] = useState()
const [step, setStep] = useState<"가입방식"|"주민번호"|"집주소"|"가입성공">("가입방식")
return (
<main>
{step === "가입방식" && <가입방식 onNext={(data) => {
setRegisterData(prev => ({ ...prev, 가입방식: data }))
setStep("주민번호")
}} />}
{step === "주민번호" && <주민번호 onNext={() => setStep("집주소")} />}
{step === "집주소" && <집주소 onNext={async () => setStep("회사주소")} />}
{step === "회사주소" && <회사주소 onNext={async () => {
await fetch("/api/register", { data }) // API 호출 장소변경
setStep("가입성공")
}} />}
{step === "가입성공" && <가입성공 />}
</main>
)
그렇다면 어떻게 해야할까?
바로 "Step"에 관련된 로직을 묶어내는 방식을 생각해 볼 수 있을 것이다.
const [registerData, setRegisterData] = useState()
const [step, setStep] = useState<"가입방식"|"주민번호"|"집주소"|"가입성공">("가입방식")
return (
<main>
{step === "가입방식" && <가입방식 onNext={(data) => {
setRegisterData(prev => ({ ...prev, 가입방식: data }))
setStep("주민번호")
}} />}
{step === "주민번호" && <주민번호 onNext={() => setStep("집주소")} />}
...
</main>
)
우선, 우리는 "조건부 렌더링"을 추상화할 필요가 있다.
<Step if={step === "가입방식"}>
<가입방식 onNext={() => setStep("주민번호")} />
</Step>
// 구현
function Show({ if, children }) {
if (if === true) {
return children
}
return null
}
그리고, 추상화한 컴포넌트로 묶어내야 한다.
const [registerData, setRegisterData] = useState()
const [step, setStep] = useState<"가입방식"|"주민번호"|"집주소"|"가입성공">("가입방식")
return (
<main>
<Step if={step ==="가입방식"}>
<가입방식 onNext={() => setStep("주민번호")} />
</Step>
<Step if={step ==="주민번호"}>
<주민번호 onNext={() => setStep("집주소")} />
</Step>
...
</main>
)
그러나, 코드를 잘 보게 되면 코드가 반복되는 것을 볼 수 있다.
반복되는 조건문을 추상화할 필요가 있다는 것이다.
const [registerData, setRegisterData] = useState()
const [step, setStep] = useState<"가입방식"|"주민번호"|"집주소"|"가입성공">("가입방식")
return (
<main>
<Step name="가입방식">
<가입방식 onNext={() => setStep("주민번호")} />
</Step>
<Step name="주민번호">
<주민번호 onNext={() => setStep("집주소")} />
</Step>
...
</main>
)
이를 해결하기 위해서,
"Step"들을 받고 반복문을 돌리고, 커스텀 훅을 생성해 처리할 수 있을 것이다.
커스텀 훅인 "useFunnel" Hook을 생성해볼 수 있겠다.
function getGraph(step: string, children: ReactElement[]) {
const result: string[] = ['graph TD']
children.map(stepElement => {
const stepName = stepElement.props.name
const children = stepElement.props.children
const childrenProps = children?.props as [string, unknown]
Object.entries(childrenProps).map(el => {
const [functionName, value] = el
if (typeof value === 'function') {
value.toString().match(/setStep\(.+\)/g)
?.map(matchedSetStep => {
const matchedStepName = matchedSetStep.match(/setStep\((?:'|")(.+)(?:'|")\)/)?.[1]
const graphNode = `${stepName}[${stepName}] -->|${functionName}| ${matchedStepName}[${matchedStepName}]`
result.push(graphNode)
})
}
})
})
return result.join('\n')
}
function useFunnel() {
const [step, setStep] = useState()
const Step = (props) => {
return <>{props.children}</>
}
const Funnel = ({children}) => {
// name이 현재 step 상태와 동일한 Step만 렌더링
const targetStep = children.find(childStep => childStep.props.name === step)
return Object.assign(targetStep, { Step })
}
return [Funnel, setStep]
}
생성한 "useFunnel" Hook을 기존 코드에 적용시키면,
const [registerData, setRegisterData] = useState()
const [step, setStep] = useFunnel<"가입방식"|"주민번호"|"집주소"|"가입성공">("가입방식")
return (
<Funnel>
<Funnel.step name="가입방식">
<가입방식 onNext={() => setStep("주민번호")} />
</Funnel.step>
<Funnel.step name="주민번호">
<주민번호 onNext={() => setStep("집주소")} />
</Funnel.step>
...
</Funnel>
)
여기에 히스토리 관리 기능을 추가해야 하지 않을까?
"name"에 맞는 데이터가 나타나야 할테니 말이다.
function useFunnel() {
const step = useQueryParam("funnel-step")
const setStep = (step: string) => {
const nextUrl = `${QS.create({...prevQuery, "funnel-step": step})}`
router.push(url, undefined, { shallow: true })
}
...
return [Funnel, setStep]
}
그러나, 위의 방법도 불편한 점이 있을 것이다.
첫 번째로, 1초 만에 파악하기 힘든 페이지 흐름이다. "step"이 많아질수록 코드가 길어질 것이고, 그래서 한 번에 읽기 어려움이 발생할 것이다.
<Funnel>
<Funnel.step name="가입방식">
<가입방식
on신규가입클릭={() => {
updateState({ 가입방식: "신규가입" })
setStep("주민번호")
}}
on번호이동클릭={() => {
updateState({ 가입방식: "번호이동" })
setStep("주민번호")
}}
/>
</Funnel.step>
<Funnel.step name="주민번호">
<주민번호
on다음클릭={() => {
if (emptyHomeAdress) {
setStep("집주소")
} else {
setStep("회사주소")
}
}}
/>
</Funnel.step>
...
</Funnel>
'프론트엔드 > React' 카테고리의 다른 글
모달창 만들기, createPortal() (0) | 2024.03.11 |
---|---|
트위터 기반으로 Pet 전문 플랫폼을 만들 계획입니다. (0) | 2024.01.13 |
프론트엔드 성능 최적화하기 (0) | 2023.08.15 |
useEffect, 필요한 부분만 요약해서 학습하기 (0) | 2023.08.09 |
Div 범벅에서 벗어나 제대로 시멘틱 태그 사용하기 (0) | 2023.07.08 |
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- 스프링
- React
- 신입개발자가 준비해야 할 것들
- Frontend
- 고민한 부분
- Default Branch
- Express
- 개발자이력서꿀팁
- 자바스크립트
- #포스텍애플디벨로퍼아카데미
- DB Error MongooseServerSelectionError
- 그룹인터뷰후기
- LottieFiles
- javascript
- 깃허브 Merge
- 설명회느낌점
- 원티드 프리온보딩
- 포스텍애플아카데미
- 싱글톤
- 코딩테스트 대비
- node
- 포스텍애플디벨로퍼아카데미
- 개발 이력서 지원 팁
- if(kakao)dev2022
- PostechAppleDeveloperAcademy
- 프론트엔드 챌린지
- 조코딩과함께
- Singleton
- 원티드 프리온보딩 챌린지
- 최종추가합격
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
글 보관함