type Job = {
name: string;
status: "WAITING" | "COMPLETED" | "CANCELED" | "RUNNING";
};
type RunningJob = {
name: string;
status: "RUNNING";
};
// 기존
type GpuServer = {
// ...중략,
jobs: Job[];
};
const Component = () => {
const gpuServer = useAsyncHook<GpuServer>();
const runningJobName = gpuServer.jobs.find((job) => job.status === "RUNNING")?.name ?? "N/A";
const waitingJobCount = gpuServer.jobs.filter((job) => job.status === "WAITING").length;
}, 0);
return (
<div>
<span>현재 실행중인 Job: {runningJobName} </span>
<span>대기중인 Job의 개수: {waitingJobCount}개 </span>
</div>
);
};
// 변경후
type GpuServer = {
// ...중략,
runningJob: RunningJob | null;
waitingJobCount: number;
};
const Component = () => {
const gpuServer = useAsyncHook<GpuServer>();
return (
<div>
<span>현재 실행중인 Job: {gpuServer.runningJob?.name ?? "N/A"} </span>
<span>대기중인 Job의 개수: {waitingJobCount}개 </span>
</div>
);
};
runningJob
)과 대기중인 Job의 개수(waitingJobCount
)를 계산하였으나, 서버측에서 runningJob
과 waitingJobCount
를 계산하여 프론트로 보내주는 방식으로 변경되었습니다.runningJob
과 waitingJobCount
계산하는 부분을 삭제하면 되니 간단한 대응이라고 생각하였으나, 실제로 변경에 대응하다보니 11개의 파일이 변경되었습니다.runningJob
과 waitingJobCount
계산하는 비즈니스 로직 모두 가지고 있습니다.runningJob
과 waitingJobCount
를 필요로 하는 모든 컴포넌트에서 각각 runningJob
과 waitingJobCount
를 계산하고 있었기에 이를 모두 수정하여야 합니다.⇒ 역할과 책임을 구분하는 레이어가 명확하지 않아, 변경에 취약한 구조라고 결론내렸습니다.
개선방안 1. 서비스 계층을 분리하고 커스텀 훅에서 비즈니스 로직을 담당합니다.
개선방안 2. 상태관리 툴을 도입합니다.
⇒ 기존에 waitingJobCount
, runningJob
을 Reducer에서 계산했다면, 사용하는 Component는 API Response가 변경되더라도 Store에서 상태를 가져오는 것은 동일하므로 아무런 변경 사항이 없었을 것입니다. 즉, 레이어를 구분한다면, 역할과 책임을 명확히 하여 변경사항이 전파되는 경계점을 쉽게 파악할 수 있을 것으로 예상됩니다.
현재 프로젝트에는 gpServer
, job
, member
의 3가지 도메인이 존재합니다.
최소 필수 기능은 모두 구현된 상태에서 리덕스를 전면적으로 도입하는 것은 많은 코드 수정을 불러일으키므로, 여파를 최소화 하기 위해 가장 로직이 간단한 member
부터 도입하기로 결정하였습니다.
memberSlice
의 구조도는 아래와 같습니다.
member
기능을 리덕스로 마이그레이션 중입니다