*react conf 2021 동영상을 참고했습니다.
Suspense를 사용해야하는 이유
리액트는 데이터가 위에서 아래로 즉 단방향으로 흐르기때문에 서비스의 전반적인 흐름을 쉽게 이해 할 수 있습니다. 각 컴포넌트는 데이터를 가공하거나 사용하여 jsx를 반환합니다.
컴포넌트 내부에서도 코드의 흐름은 위에서 아래로 흐릅니다.
하지만 비동기적인 코드가 사용되면 그 흐름은 깨집니다. 코드를 건너 뛰어가며 보고 이해해야하기 때문입니다.
// 비동기 코드가 들어간다면
function List({pageId})) {
const [items, setItems] = useState();
// useEffect를 사용하여 fetch를 하게 되고
// fullfill 상태가 되면 state가 업데이트 되어 re-rendering을 하게 된다.
useEffect(() => {
fetItems(pageId).then(setItems);
}, [pageId]);
return items[pageId].map((item) =>
<li>{item}</li>
);
}
비동기 통신 중 pageId의 값이 변경되면 문제가 발생합니다.
통신이 끝나지도 않았는데 re-rendering이 일어나고 useEffect는 다시 실행되어 비동기 통신이 다시 일어납니다. race-condition 같은 버그가 발생할 수 있습니다.
이러한 문제점 때문에 많은 개발자는 apollo, swr, react-query 같은 data-fetching 라이브러리를 사용합니다.
// data-fetching library의 사용법은 보통 아래와 같습니다.
function List({pageId})) {
// 데이터와 로딩 상태를 return 하는 hook
const [items, isLoading] = useData(pageId);
// fetch의 성공 여부에 따라 다른 컴포넌트를 보여줄 수 있다.
if (isLoading) {
return <Spinner />;
}
return items[pageId].map((item) =>
<li>{item}</li>
);
}
이러한 라이브러리를 사용하면 비동기 코드가 사용 되어도 위에서 아래로 흐르는 흐름을 건너뛰지 않아 쉽게 이해할 수 있는 '리액트스러운' 코드가 됩니다.
이러한 방식은 주로 사용 되는 방식이지만 리액트팀은 아래 두가지 문제 때문에 더 좋은 방식을 생각해 냈습니다.
- 데이터와 로딩상태를 분리시킬수는 없을까?
- 로딩 상태가 컴포넌트 내부에 있을 필요가 있을까?
이밖에 다른 문제들도 있습니다.
- 각각의 컴포넌트가 loading 상태를 갖음으로써 각각의 컴포넌트에서 loading 중일땐 어떤 작업을 해야할 지 고민해야하는 번거로움이 있습니다.
- 상위 컴포넌트에서 로딩 상태를 갖도록 수정하기 위해 자식 컴포넌트들에서 로딩 상태 관련 코드를 지워야하는 번거로움이 있습니다.
Suspense를 사용하자
function List({pageId})) {
const items = useData(pageId);
return items[pageId].map((item) =>
<li>{item}</li>
);
}
data-fetching 라이브러리에서 isLoading 상태를 신경쓸 필요 없이 items 데이터만 불러와 사용하면 됩니다.
부모 컴포넌트 jsx에서 List 컴포넌트를 Suspense 컴포넌트로 감싸주고 fallback 값으로 로딩시 보여줄 컴포넌트를 넣어줍니다.
자식으로 등록된 UI가 준비상태가 아니면 fallback UI를 보여주는것이 Suspense의 컨셉입니다.
리액트팀은 로딩 상태는 jsx안에 위치하는것이 가장 좋은 방식이라고 생각하고 있습니다.
Header 컴포넌트와 List 컴포넌트에서 둘중 한곳에서 데이터를 fetching 하더라도 fallback UI를 보여주게 되며
두곳 모두에서 fetching 하여 모두 fetching이 종료되면 두 컴포넌트가 한번에 보여지게 됩니다.
만약 List 컴포넌트가 fetching 중일때 다른 fallback UI를 보여주고 싶다면 위와 같이 다른 fallback UI를 지닌 Suspense 컴포넌트로 감싸주면 된다.
요약
- 리액트의 단방향 데이터 흐름을 통해 컴포넌트 로직을 쉽게 파악할 수 있다.
- 비동기 코드로 컴포넌트의 상태를 바꾸는 로직은 리액트의 단방향 데이터 흐름 컨셉과 맞지 않기 때문에 data-fetching 라이브러리의 hook을 통해 비동기 코드를 리액트스럽게 사용했다.
- 하지만 data와 로딩 상태가 같이 있을 필요가 없고 컴포넌트 내부에서 로딩 상태 값을 다룰 필요가 없기때문에 Suspense가 나왔다.
- 로딩 상태는 jsx안에 위치하는것이 가장 좋은 방식이라고 리액트팀은 말한다.
- Suspense안에 자식이 fetching 중일땐 등록한 fallback UI를 보여준다.
'Web > React.js' 카테고리의 다른 글
[STUDY] 커스텀 Hooks (0) | 2021.03.27 |
---|---|
[STUDY] Hook의 규칙 (0) | 2021.03.23 |
[STUDY] Effect Hook 사용하기 (0) | 2021.03.22 |
[STUDY] State Hook 사용하기 (0) | 2021.03.21 |
[STUDY] Hook 개요 (0) | 2021.03.19 |
댓글