[React] 리액트 React.lazy와 Suspense란?
글 작성자: 망고좋아
반응형
🎯 리액트 React.lazy와 Suspense란?
📝 React.lazy
const SomeComponent = React.lazy(() => import('./SomeComponent'));
const OtherComponent = React.lazy(() => import('./OtherComponent'));
function MyComponent() {
return (
<Suspense fallback={<Spinner />}>
<div>
<OtherComponent />
</div>
</Suspense>
);
}
React.lazy()
를 사용하면 동적으로 불러오는 컴포넌트를 정의할 수 있다.- 번들의 크기를 줄이고, 초기 렌더링에서 사용되지 않는 컴포넌트를 불러오는 작업을 지연시킬 수 있다.
React.lazy
로 import 해준 컴포넌트는 반드시Suspense
의 자식으로 들어가야 한다.
📝 Suspense
특정 컴포넌트에서 사용되고 있는 데이터의 준비가 아직 끝나지 않았음을 react에 알릴 수 있으며 data fetching 라이브러리와 함께 사용할 수 있는 구조
Suspense를 사용하면, 렌더링을 시작하기 전에 응답이 오기를 기다리지 않아도 된다.
- Suspense를 사용하면 해당 데이터를 사용하는 컴포넌트를 렌더링 하지 않고 다른 로딩 화면을 보여줄 수 있다.
- fetching 라이브러리에서의 워터폴 현상은 이전 fetch 요청에 대한 응답이 도착해야 다음 fetch 요청을 보낼 수 있는 구조이다.
- 컴포넌트 렌더링 -> data fetching 요청 -> data 응답 구조로 동작
- 이러한 문제를 Suspense를 이용해
data fetching 요청 -> data 응답 -> 컴포넌트 렌더링
의 구조로 바뀌는 것이다. - 이렇게 구조가 바뀌면 data fetching 요청이 컴포넌트 렌더링에 의존되지 않고 모두 한 번에 실행되므로 워터폴 현상을 막을 수 있다.
// fetching 라이브러리만을 사용했을 때 구조
data fetching 요청 -> 로딩중 UI 렌더링 -> data 응답 -> 컴포넌트에 응답 반영
// suspense 사용
data fetching 요청 -> suspense 하위의 컴포넌트에 요청 리소스를 반영 -> suspense에 의해 로딩 UI 렌더 -> 요청 리소스로 data 응답이 들어옴 -> 컴포넌트에 응답 반영
- 이러한 구조에서 suspense는 요청 직후
요청 리소스
를 바로 컴포넌트로 주입하는 방식으로 바꿔준다. - 여기서 말하는 요청 리소스는
Promise의 형태가 아니다.
data fetching 라이브러리 내부적으로 구현되어있는 일반 객체이다.
const ProfilePage = React.lazy(() => import('./ProfilePage')); // 지연 로딩
// 프로필을 불러오는 동안 스피너를 표시
<Suspense fallback={<Spinner />}>
<ProfilePage />
</Suspense>
- Suspense를 사용하면 선언적으로 데이터 등을 기다려준다.
- 이미지, 스크립트, 그 밖의 비동기 작업을 기다리는 데에도 사용할 수 있다.
const resource = fetchProfileData();
function ProfilePage() {
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function ProfileDetails() {
// 비록 아직 불러오기가 완료되지 않았겠지만, 사용자 정보 읽기를 시도합니다
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
// 비록 아직 불러오기가 완료되지 않았겠지만, 게시글 읽기를 시도합니다
const posts = resource.posts.read();
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
- Suspense를 사용하면 컴포넌트가 렌더링 되기 전까지 기다릴 수 있다.
- Suspense는 데이터 불러오기 라이브러리가 아니다.
- Suspense는 컴포넌트가 읽어 들이고 있는 데이터가 아직 준비되지 않았다고 React에 알려줄 수 있는, 데이터 불러오기 라이브러리에서 사용할 수 있는 메커니즘이다. 이후 리액트는 데이터가 준비되기를 기다렸다가 UI를 갱신할 수 있다.
📝 Suspense가 아닌 것
- Suspense는 데이터 불러오기에 대한 구현이 아니다.
- GraphQL, REST 또는 특정한 데이터 형식, 라이브러리, 전송 또는 프로토콜을 사용한다고 가정하지 않는다.
- Suspense는 바로 사용할 수 있는 클라이언트가 아니다.
fetch
또는 Relay를 Suspense로 “대체”할 수 없다. - Suspense는 데이터 불러오기 작업과 뷰 레이어를 결합해주지 않는다. UI 상에 로딩 상태를 표시할 수 있도록 조정하는 것을 돕지만, 네트워크 로직을 React 컴포넌트에 종속시키는 것은 아니다.
📝 Suspense가 가능한 것
- 데이터 불러오기 라이브러리들이 React와 깊게 결합할 수 있도록 해 준다.
- 의도적으로 설계된 로딩 상태를 조정할 수 있도록 해 준다.
- Suspense는 데이터가 어떻게 불러져야 하는지를 정하지 않고, 앱의 시각적인 로딩 단계를 밀접하게 통제할 수 있도록 해준다,
- 경쟁 상태(Race Condition)를 피할 수 있도록 돕는다.
await
를 사용하더라도 비동기 코드는 종종 오류가 발생하기 쉽다. Suspense를 사용하면 데이터를 동기적으로 읽어오는 것처럼 느껴지게 해 준다. 마치 이미 불러오기가 완료된 것처럼!
📝 동작 순서
const resource = fetchProfileData();
function ProfilePage() {
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails />
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline />
</Suspense>
</Suspense>
);
}
function ProfileDetails() {
// 아직 로딩이 완료되지 않았더라도, 사용자 정보 읽기를 시도합니다
const user = resource.user.read();
return <h1>{user.name}</h1>;
}
function ProfileTimeline() {
// 아직 로딩이 완료되지 않았더라도, 게시글 읽기를 시도합니다
const posts = resource.posts.read();
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.text}</li>
))}
</ul>
);
}
- 이미
fetchProfileData()
에게 요청을 보냈다. <ProfilePage>
렌더링을 시도- 자식 컴포넌트로
<ProfileDetails>
와<ProfileTimeline>
을 반환
- 자식 컴포넌트로
<ProfileDetails>
렌더링을 시도resource.user.read()
호출- 아직 불러온 데이터가 아무것도 없으므로, 이 컴포넌트는 “정지”
- 이 컴포넌트를 넘기고, 트리 상의 다른 컴포넌트의 렌더링을 시도
<ProfileTimeline>
렌더링 시도resource.posts.read()
호출- 또 한 번, 아직 데이터가 없으므로, 이 컴포넌트 또한 “정지”
- React는 이 컴포넌트도 넘기고, 트리 상의 다른 컴포넌트의 렌더링을 시도
- 렌더링을 시도할 컴포넌트가 없다.
<ProfileDetails>
가 정지된 상태이므로, React는 트리 상에서<ProfielDetails>
위에 존재하는 것 중 가장 가까운<Suspense>
Fallback을 찾는다.<h1>Loading profile...</h1>
- 데이터가 계속 흘러들어옴에 따라, React는 렌더링을 다시 시도하며, 그때마다 React가 “더 깊은 곳까지” 처리할 수 있게 된다.
resource.user
를 불러오고 나면,<ProfileDetails>
컴포넌트는 성공적으로 렌더링 ⇒ Fallback 사라짐
🏷 요약
- suspense란 비동기 작업이 자식 컴포넌트에서 처리되기 전에 suspense의 fallback props가 렌더링 된다. 그리고 자식 컴포넌트에서 비동기 작업이 끝나면 리 렌더링이 일어나면서 fallback이 종료되고 컴포넌트가 렌더링 된다.
- suspense는
렌더링 시점
에서만 감지해준다. - 따라서 기존에 있는 isLoding을 완전히 대체할 수 없다. 첫 페이지 로딩은 suspense를 사용하고, 페이지 내에서 비동기 처리를 하는 부분은 isLoading을 사용해서 로딩 창을 보여줘야 한다.
- 추후 지원된다고 한다.
- 따라서 첫 화면 렌더링 된 이후에 데이터를 요청해서 받아오는 일이 없다면 suspense만으로 로딩 창을 손쉽게 구현할 수 있다.
- 하지만 렌더링 후 페이지 내에서 데이터를 요청해서 받고 보여줘야 한다면 useState로 isLoding를 설정해서 로딩 창을 보여줘야 한다.
📌 참고
반응형
'프로그래밍 > React' 카테고리의 다른 글
[React] Do not access Object.prototype method 'hasOwnProperty' from target object. 에러 해결 방법 (0) | 2022.02.17 |
---|---|
[React] 리액트 PropTypes 사용하기 (0) | 2022.02.15 |
[React] 리액트 차트 라이브러리 :: Recharts 사용법 (0) | 2022.02.10 |
[React] 리액트 조건부 렌더링 방법 (0) | 2022.02.10 |
[styled-components] 초기 CSS 세팅하기(GlobalStyles, styled-reset, ThemeProvider) (0) | 2022.02.08 |
댓글
이 글 공유하기
다른 글
-
[React] Do not access Object.prototype method 'hasOwnProperty' from target object. 에러 해결 방법
[React] Do not access Object.prototype method 'hasOwnProperty' from target object. 에러 해결 방법
2022.02.17 -
[React] 리액트 PropTypes 사용하기
[React] 리액트 PropTypes 사용하기
2022.02.15 -
[React] 리액트 차트 라이브러리 :: Recharts 사용법
[React] 리액트 차트 라이브러리 :: Recharts 사용법
2022.02.10 -
[React] 리액트 조건부 렌더링 방법
[React] 리액트 조건부 렌더링 방법
2022.02.10