React 리팩토링(w/Storybook): UI와 비즈니스 로직 분리
20246 단어 reactjavascripttypescriptwebdev
React Refactoring (w/Storybook): Separate UI and Business Logic
이전 회사에 입사했을 때 프로젝트 코드에 대해 아는 사람이 없었고 프로젝트를 만든 개발자가 떠났습니다.
구성 요소가 많았는데 어떤 구성 요소에 어떤 비즈니스 로직이 있는지 몰랐습니다. 재사용하고 싶었던 구성 요소가 있었지만 다른 구성 요소의 동작에 영향을 미칠 수 있는 복잡한 비즈니스 로직이 있었기 때문에 재사용할 수 없었습니다. 이로 인해 컴포넌트를 재활용하고 UI를 재사용하기가 어려우며 때로는 비슷한 모양으로 이미 존재하는 컴포넌트를 만들기도 했습니다.
어느 날 나는 그것들을 리팩토링하기로 결정했습니다. 이를 위해 UI와 비즈니스 로직을 분리하고 구성 요소 나열에
storybook
를 사용했습니다. 이 작업 후에는 구성 요소의 UI를 쉽게 재사용하고 어떻게 보이는지 확인할 수 있었습니다.이에 대한 예를 보여드리겠습니다.
방문자 수를 표시하는 구성 요소
TodayVisitorCard
가 있습니다.import { useEffect, useState } from "react";
import styled from "@emotion/styled";
const CardWrapper = styled.div`
border: 1px solid black;
border-radius: 8px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
padding: 24px;
text-align: center;
width: fit-content;
`;
interface VisitorResponse {
total: number;
today: number;
}
const TodayVisitorCard = () => {
const [data, setData] = useState<VisitorResponse | null>();
const [hasError, setHasError] = useState(false);
const fetchVisitor = async () => {
try {
const res = await fetch("http://localhost:7777/visitor");
if (res.status !== 200) {
throw new Error(`Response status is ${res.status}.`);
}
const data = await res.json();
setData(data);
} catch (e) {
console.error(e);
setHasError(true);
setData(null);
}
};
useEffect(() => {
fetchVisitor();
}, []);
if (data === undefined) return <div>loading...</div>;
return (
<CardWrapper>
{hasError && <div>Something went wrong!</div>}
{data && (
<>
<h2>Total: {data.total}</h2>
<span>Today: {data.today}</span>
</>
)}
</CardWrapper>
);
};
export default TodayVisitorCard;
논리를 분리합시다.
import { useEffect, useState } from "react";
import styled from "@emotion/styled";
const CardWrapper = styled.div`
border: 1px solid black;
border-radius: 8px;
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 12px;
padding: 24px;
text-align: center;
width: fit-content;
`;
interface VisitorResponse {
total: number;
today: number;
}
const TodayVisitorCard = () => {
const [data, setData] = useState<VisitorResponse | null>();
const [hasError, setHasError] = useState(false);
const fetchVisitor = async () => {
try {
const res = await fetch("http://localhost:7777/visitor");
if (res.status !== 200) {
throw new Error(`Response status is ${res.status}.`);
}
const data = await res.json();
setData(data);
} catch (e) {
console.error(e);
setHasError(true);
setData(null);
}
};
useEffect(() => {
fetchVisitor();
}, []);
if (data === undefined) return <div>loading...</div>;
return <TodayVisitorCardUI {...data} hasError={hasError} />;
};
const TodayVisitorCardUI = ({
hasError,
total,
today,
}: {
hasError?: boolean;
total?: number;
today?: number;
}) => {
return (
<CardWrapper>
{hasError && <div>Something went wrong!</div>}
{total && <h2>Total: {total}</h2>}
{today && <span>Today: {today}</span>}
</CardWrapper>
);
};
export { TodayVisitorCard };
export default TodayVisitorCardUI;
이제 UI로 다른 비즈니스 로직을 쉽게 사용할 수 있으며,
아래와 같이
storybook
로 컴포넌트 UI를 나열할 수 있습니다.import { ComponentStory, ComponentMeta } from "@storybook/react";
import TodayVisitorCard from ".";
export default {
title: "\"components/TodayVisitorCard\","
component: TodayVisitorCard,
args: {
hasError: false,
},
argTypes: {
today: {
type: "number",
},
total: {
type: "number",
},
},
} as ComponentMeta<typeof TodayVisitorCard>;
const Template: ComponentStory<typeof TodayVisitorCard> = (args) => (
<TodayVisitorCard {...args} />
);
export const Example = Template.bind({});
Example.args = {
today: 6522,
total: 139,
};
export const Error = Template.bind({});
Error.args = {
hasError: true,
};
결론
실제 세계에서는 더 복잡할 것입니다. 고려해야 할 사항이 많을 수 있습니다. 구조 및 명명 규칙이 그 중 하나일 수 있습니다. 프로젝트에 맞는 나만의 전략을 세워야 합니다.
누군가에게 도움이 되길 바랍니다.
행복한 코딩!
Reference
이 문제에 관하여(React 리팩토링(w/Storybook): UI와 비즈니스 로직 분리), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/lico/react-storybook-separate-ui-and-business-logic-44c0텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)