TLDR : 렌더링의 관심사를 분리하여 stateless component를 만든다.
https://wit.nts-corp.com/2021/08/11/6461
이미 너무 좋은 글이 있지만, 데이터 페치와 렌더링 관심사 분리 관점에서 해당 패턴을 소개해본다.
해당 패턴은 사실 이전에 존재하던 컨테이너-프리젠터 패턴의 fancy-alias일 뿐이다.
최근에 스켈레톤 컴포넌트를 만들 일이 있었다.
즉 로딩 시에 대체 컴포넌트를 보여주는 것이다.
간단하게 생각하면 원래 위치의 빵꾸에 가짜 데이터를 넣으면 될 것이다.
하지만 보통 props drilling을 피하기 위해 아래와 같이 컴포넌트 안에 query 로직을 집어넣는다.
import "../App.css";
import axios from "axios";
import React from "react";
const WithoutSkeleton = () => {
const [data, setData] = React.useState([]);
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => {
setIsLoading(true);
// Intentionally delay the function execution
new Promise((res) => {
setTimeout(() => {
res();
}, 3000);
}).then(() => {
axios.get("https://reqres.in/api/users?page=2").then((res) => {
setData(res.data.data);
setTimeout(() => setIsLoading(false), 2000);
});
});
}, []);
return (
<div>
<h1>Without Skeleton</h1>
{isLoading &&
data.map((item) => {
return (
<li key={item.id} className="item">
<div>
<img className="img" src={item.avatar} alt="" />
</div>
<div className="info">
<p>
<strong>
{item.first_name} {item.last_name}
</strong>
</p>
<p>{item.email}</p>
</div>
</li>
);
})}
</div>
);
};
export default WithoutSkeleton;
여러분의 코드베이스에도 분명 데이터 fetch 로직과 렌더링 로직이 섞인 경우가 있을것이다.
해당 문제를 컨테이너 패턴과 VAC 패턴으로 해결해보자.
VAC 컴포넌트는 다음과 같은 조건을 만족해야 한다.
- 반복이나 조건부 노출, 스타일 제어와 같은 렌더링과 관련된 처리만을 수행합니다.
- 오직 props를 통해서만 제어되며 스스로의 상태를 관리하거나 변경하지 않는 stateless 컴포넌트입니다.
- 이벤트에 함수를 바인딩할 때 어떠한 추가 처리도 하지 않습니다.
VAC를 이용한 해법은 정말 간단하다. VAC 컴포넌트로 렌더링의 관심사를 분리하면 되는 것이다.
const WithoutSkeleton = ({ data }) => {
return (
<div>
{data?.map((item) => {
return (
<li key={item.id} className="item">
<div>
<img className="img" src={item.avatar} alt="" />
</div>
<div className="info">
<p>
<strong>
{item.first_name} {item.last_name}
</strong>
</p>
<p>{item.email}</p>
</div>
</li>
);
})}
</div>
);
};
그리고 fetch가 끝나면 데이터를 전달한다.
const WithFetch = ({ children }) => {
const { data } = useSWR(
"https://reqres.in/api/users?page=2",
(url) =>
new Promise((res) => setTimeout(() => res(), 5000)).then(() =>
axios.get(url).then((res) => res.data.data)
),
{ suspense: true }
);
return data ? children(data) : null;
};
그리고 스켈레톤은 Suspense의 fallback으로 사용한다.
또한 이 방법은 스토리북과 함께 사용할 때 좋다.
msw나 decorator를 이용한 모킹 없이 가짜 데이터로 컴포넌트를 시각화할 수 있다.
오히려 백엔드의 db, 서비스 모킹보다 훨씬 효과가 좋다 생각한다.
백엔드는 실제 db와 스키마가 다르거나, 연계 서비스가 틀어지면 무의미한 테스트이지만,
데이터를 잘 렌더링하는지 보는건 시각적 테스트이기 때문에 항상 의미가 있다.
'FrontEnd' 카테고리의 다른 글
리액트 디자인 패턴 : Container pattern (컨테이너 패턴) (0) | 2022.06.04 |
---|---|
리액트 디자인패턴 : Layout Component (레이아웃 컴포넌트 패턴) (0) | 2022.06.04 |
리액트 디자인패턴 : Render Props(렌더 프롭스 패턴) (0) | 2022.06.02 |
리액트 성능 최적화 : Code Splitting(코드 스플리팅) (0) | 2022.06.02 |
리액트 디자인패턴 : Control Props (컨트롤 프롭스 패턴) (0) | 2022.06.01 |