npm: @hongpung/react-step-flow
↑ 본문에서 설명하는 배포중인 라이브러리↑
홍풍의 디렉토리 아키텍쳐를 FSD(Featured-Sliced Design)로 리팩토링 하는 과정에서, 하나의 페이지로 묶어야 하는 위젯들의 집합을 어떻게 다룰지를 많이 고민 하게 됐다.
Navigator는 Page의 상위 레이어로 처리했기에, 이곳으로 다시 page 레이어에서 navigation을 선언할 수도 없는 노릇이었다. 또한 그렇게 복잡한 navigation이 필요하지 않고 화면이 별도의 인터랙션 없이 전환만 필요한 상황이었다.
그래서 우선적으로 표현한 방법이 하나의 Page에서 상태에 따라서 분기해서 들어가는 것이었다.
const getContent = () => {
if (isCheckin) {
if (checkinStatus === "지각" && sessionData.status === "JOINABLE") {
return (
<LateSessionCompleteWidget startTime={sessionData.currentSession.startTime} />
);
}
if (checkinStatus === "참가" || checkinStatus === "출석") {
return <AttendSessionCompleteWidget attendanceStatus={checkinStatus} />;
}
return <StartSessionCompleteWidget />;
}
if (sessionData.status === "JOINABLE" && sessionData.currentSession) {
return <AttendSessionConfirmWidget session={sessionData.currentSession} />;
}
if (sessionData.status === "STARTABLE" && sessionData.nextReservationSession) {
return <StartSessionConfirmWidget session={sessionData.nextReservationSession} />;
}
if (sessionData.status === "CREATABLE") {
return (
<CreateSessionConfirmWidget
nextSession={sessionData.nextReservationSession}
participationAvailable={participationAvailable}
onParticipationAvailableChange={setParticipationAvailable}
/>
);
}
return null;
};
이렇게 처리할때 문제는, 넘치는 분기에 대해서 관리하기 힘들어지고, switch로 관리하는 패턴이 썩 좋지 않았다.
더군다나, 이 페이지의 기능에는 step에 관여하는 상태가 2개 이상이었다. 그러니까 switch를 이중문으로 작성하거나, 해야되는 전여 선언적이지 않은 가독성이 구린 컴포넌트가 탄생하는 것이다.
상태를 step으로 전환하고, 두 상태에 대해서 step을 전환하는 패턴으로 전환해본다.
const getStep = (): string => {
if (isCheckin) {
if (checkinStatus === "지각" && sessionData.status === "JOINABLE") {
return "LATE_SESSION_COMPLETE";
}
if (checkinStatus === "참가" || checkinStatus === "출석") {
return "ATTEND_SESSION_COMPLETE";
}
return "START_SESSION_COMPLETE";
}
if (sessionData.status === "JOINABLE" && sessionData.currentSession) {
return "ATTEND_SESSION_CONFIRM";
}
//중략...
};
const getContent = () => {
const step = getStep();
switch (step) {
case "LATE_SESSION_COMPLETE":
return (
<LateSessionCompleteWidget
startTime={sessionData.currentSession.startTime}
/>
);
case "ATTEND_SESSION_COMPLETE":
return (
<AttendSessionCompleteWidget
attendanceStatus={checkinStatus}
/>
);
case "START_SESSION_COMPLETE":
return <StartSessionCompleteWidget />;
//중략...
}
};
위와 같은 방식으로 재정의하여 조금 더 상태에 따른 step 분기 로직과 렌더링 로직을 분리할 수 있었다. 여기에서 구현을 마무리하고 다시 리팩토링을 진행할까 했지만 조금 더 선언적인 방법으로, 실제 JSX 문법으로 표현을 하는 것이 조금 더 깔끔하고 가독성이 좋지 않을까 하는 생각을 했다.
그래서 자주 사용하는 라이브러리의 패턴을 빌려오는 것이 낫지 않을까 라는 생각을 했고, react-navigation의 표현과 타입 선언을 분석해봤다.