Published on

부분 Suspense vs 전체 Suspense

Suspense란?

React 18부터 본격적으로 지원되는 Suspense는 컴포넌트가 아직 준비되지 않았을 때, 사용자에게 보여줄 대체 UI(fallback)를 정의할 수 있도록 돕는다. fallback UI로는 Skeleton, Spinner, Loader등 UI를 보여주어 사용자에게 로딩을 알리는 역할을 한다.

<Suspense fallback={<Skeleton />}>
  <MyComponent />
</Suspense>

여기서 MyComponent가 데이터를 가져오거나, 동적 임포트를 로딩하는 과정에서 ‘로딩 중’임을 알리려면 내부적으로 Promise를 throw해야 한다. 그래야만 Suspense가 이를 감지하고 fallback으로 정의된 Skeleton, Spinner, Loader 등을 보여줄 수 있다.

중요한 점은 단순히 비동기 요청을 한다고 해서 자동으로 Suspense가 fallback UI를 보여주는 것은 아니라는 점이다. React Query(TanStack Query) 같은 라이브러리에서 제공하는 useSuspenseQuery처럼, Promise throw를 내부적으로 해주는 방법을 사용해야 Suspense fallback이 활성화된다.

useSuspenseQuery({
  queryKey: ['todos'],
  queryFn: fetchTodos,
})

문제는 한 페이지 안에 여러 컴포넌트가 각각 다른 비동기 요청을 할 때, 어떻게 Suspense를 적용해줄지 결정해야 한다는 것이다. 이를 부분 Suspense와 전체 Suspense라는 두 가지 관점으로 나누어 살펴보자

부분 Suspense

부분 Suspense 방식은, 말 그대로 각 컴포넌트마다 Suspense를 따로 걸어주는 접근이다. 아래 예시처럼, 페이지를 구성하는 여러 컴포넌트를(Section1, Section2 등)을 각각 Suspense로 감싸주면 된다.

<Suspense fallback={<Skeleton />}>
  <Section1 />
</Suspense>

<Suspense fallback={<Skeleton />}>
  <Section2 />
</Suspense>

예시

  • Section1: 비동기 요청 시간이 3초 걸림
  • Section2: 비동기 요청 시간이 1초 걸림

이 경우 Section2는 1초 뒤에 빠르게 렌더링되어 사용자가 볼 수 있지만, Section1은 3초가 걸리므로 그 뒤에야 최종적으로 화면에 나타난다.

장점

  • 빠른 부분부터 먼저 표시: 사용자는 최대한 빠른 화면을 볼 수 있다.
  • 세밀한 로딩 제어: 화면 단위 혹은 섹션 단위로 Suspense를 구성할 수 있어, 특정 섹션만 늦게 로딩되어도 다른 섹션은 정상 동작하게 만들 수 있다.

단점

  • 팝콘 UI 문제: 데이터 로딩 완료 시점이 제각각이므로,마치 팝콘이 튀어나오듯이 컴포넌트가 하나씩 ‘뻥뻥’ 튀어나오는 느낌을 줄 수 있다. 사용자가 “화면이 덜그덕거린다”고 인식할 수도 있다.
  • UI 복잡도 증가: 여러 곳에서 Skeleton이나 Spinner가 동시에 나타날 수 있어, 로딩 화면도 여러 형태로 중첩될 위험이 있다.

전체 Suspense

“전체 Suspense”는, 페이지 수준에서 한 번에 Suspense를 적용하는 방식이다.

// Page Component
<Suspense fallback={<Skeleton />}>
  <Section1 />
  <Section2 />
</Suspense>

예시

  • Section1: 비동기 요청 3초
  • Section2: 비동기 요청 1초

이 경우 Section2가 이미 준비되었더라도, Section1이 3초 걸리기 때문에 결과적으로 3초 뒤에야 모든 컴포넌트가 한꺼번에 렌더링된다.

장점

  • 안정된 초기 화면: 사용자 입장에서는 Skeleton → 전체 화면 렌더링이라는 명확한 단계를 경험한다. “팝콘 UI” 문제가 발생하지 않으므로, 시각적인 안정감이 크다.
  • CLS(Cumulative Layout Shift) 방지: 로딩 도중에 화면 배치가 크게 바뀌지 않아, 스크롤 위치 이동 등의 문제를 줄일 수 있다.

단점

  • 첫 렌더링 지연: 느린 API 하나가 전체 페이지 로딩을 지연시킬 수 있다. 3초짜리 요청이 있다면, 3초 전에는 아무것도 볼 수 없게 된다.
  • 부분별 로딩 최적화 어려움: “Section2는 1초 만에 끝났으니 먼저 보여주자”는 식의 미세 조정이 불가능하다.

결론

개발하고 있는 서비스의 특성과 사용자 경험 일관성을 고려해서 적용해야한다. 다음은 내가 생각하는 기준이다.

  1. 어떤 사용자 경험을 제공하고 싶은가?
  • 서비스의 특성에 따라 제공하고자하는 사용자 경험이 다를 것이다.
  • 화면을 빨리 보여주고 싶은지 아니면 안정감있는 경험을 제공하고 싶은지를 고려해야한다.
  1. 어떤 플랫폼에서 서비스를 개발하고 있는지?
  • 모바일과 같은 작은 디스플레이에서 돌아가는 서비스를 개발한다면 전체 Suspense를 사용할 것 같다.
  • 한 화면에 보여지는 UI와 비동기 요청이 적을 것이다.
  • 데스크탑 PC를 타겟으로하는 수많은 UI와 비동기 요청이 존재하는 서비스라면 부분 Suspense를 사용할 것 같다.