일반적으로 컴포넌트에서 비동기 처리를 하면 로딩, 성공, 실패 처리를 동시에 수행하는 아래 같은 소스코드를 볼 수 있습니다.
function Profile() {
const foo = useAsyncValue(() => {
return fetchFoo()
})
const bar = useAsyncValue(() => {
if (foo.error || !foo.data) {
return undefined
}
return fetchBar(foo.data)
})
if (foo.error || bar.error) return <div>로딩에 실패했습니다.</div>
if (!foo.data || !bar.data) return <div>로딩중입니다.</div>
return (/* foo와 bar로 적합한 처리하기*/)
}
이게 좋은 소스코드일까요? 아닙니다. 비동기 함수가 늘어날수록 처리해야 하는 경우가 많이 증가합니다. 나중에는 가독성이 매우 떨어져 유지보수 하기가 힘들어집니다. 그러면 어떻게 해야 할까요?
성공하는 경우와 실패하는 경우, 로딩 중인 경우를 나누어 각각 그 경우에만 집중하는 소스코드를 구성해야 합니다. React v17 이전까지는 그런 컴포넌트를 구성하기가 어려웠는데 React v18 부터 Suspense로 비동기 처리 시 복잡도를 줄일 수 있습니다.
function FooBar() {
const foo = useAsyncValue(() => fetchFoo())
const bar = useAsyncValue(() => fetchBar(foo))
return <div>{foo}{bar}</div>
}
비동기 처리가 성공하는 경우만 집중한 컴포넌트입니다.
<ErrorBoundary fallback={<MyErrorPage />}>
<Suspense fallback={<Loader />}>
<FooBar />
</Suspense>
</ErrorBoundary>
로딩 상태는 Suspense에서 처리하고 에러 상태는 ErrorBoundary가 처리합니다. 이 형태를 이용하면 복잡도가 낮고 가독성이 좋고 비즈니스 로직이 쉽게 파악가능합니다.
Suspense는 아래 라이브러리를 이용해서 사용할 수 있습니다.
라이브러리를 사용하지 않고 비동기 함수를 처리하는 방법은 공식문서를 참고하시면 됩니다.
ErrorBoundary는 함수형 컴포넌트에서 구현되지 않는 기능들을 사용하기 때문에 클래스 컴포넌트로만 구현 가능합니다. react-error-boundary 라이브러리를 이용하거나 직접 구현해서 사용합니다.
Recoil을 이용해 비동기 함수를 다루었습니다.
선언적으로 컴포넌트를 구성해야 한다. 컴포넌트 바로 하단에 작성, 함수안에 비동기를 실행하면 안된다. suspense가 감지하지 못하니깐
https://toss.im/slash-21/sessions/3-1
https://www.daleseo.com/react-suspense/
https://maxkim-j.github.io/posts/suspense-argibraic-effect
https://jbee.io/react/error-declarative-handling-1/
https://varletc0nst.tistory.com/39
https://velog.io/@rkd028/React-ErrorBoundary-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EC%97%90%EB%9F%AC-%ED%95%B8%EB%93%A4%EB%A7%81-%ED%95%98%EA%B8%B0
https://sangmin802.github.io/Study/Think/error%20boundary/
https://kentcdodds.com/blog/use-react-error-boundary-to-handle-errors-in-react