개발/React

[고급반] Step 3-2. Suspense와 Error Boundary: 선언적 비동기 처리 아키텍처

ophelisis 2025. 12. 23. 14:05
반응형

비동기 데이터를 불러올 때 if (isLoading) return <Spinner />와 같은 명령형 코드를 모든 컴포넌트에 작성하고 계신가요? 리액트 18은 SuspenseError Boundary를 통해 비동기 상태와 예외 처리를 UI 구조에서 분리하여 '선언적'으로 다룰 수 있는 환경을 제공합니다.

컴포넌트는 오직 '성공한 화면'에만 집중하고, 로딩과 에러 처리는 부모에게 맡기는 우아한 설계를 배워봅시다.


1. 🎭 Suspense: 로딩 상태를 선언적으로 분리하기

Suspense는 컴포넌트가 아직 렌더링될 준비가 되지 않았을 때(데이터 페칭 중일 때), 대신 보여줄 UI를 정의합니다.

🛠️ 구현 예시

JavaScript
 
import { Suspense } from 'react';

function ProfilePage() {
  return (
    <div className="layout">
      {/* 데이터가 올 때까지 fallback에 담긴 UI를 보여줍니다. */}
      <Suspense fallback={<SkeletonProfile />}>
        <UserProfile /> {/* 비동기 데이터를 사용하는 컴포넌트 */}
      </Suspense>

      <Suspense fallback={<SkeletonPosts />}>
        <UserPosts />
      </Suspense>
    </div>
  );
}
  • Waterfall 현상 방지: 여러 비동기 작업을 병렬로 처리하면서도 각각의 로딩 시점을 독립적으로 제어할 수 있습니다.
  • 사용자 경험(UX): 화면 전체가 멈추는 것이 아니라, 준비된 부분부터 순차적으로 보여주는 '점진적 렌더링'이 가능해집니다.

2. 🛡️ Error Boundary: 에러 발생 시의 '안전망' 구축

컴포넌트 내부에서 에러가 발생했을 때 전체 앱이 화이트아웃(White-out)되는 것을 막아주는 특수 컴포넌트입니다.

🛠️ 구현 예시

JavaScript
 
// ErrorBoundary는 현재 클래스 컴포넌트로 구현해야 합니다. (혹은 라이브러리 사용)
class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback; // 에러 발생 시 보여줄 UI
    }
    return this.props.children;
  }
}

// 사용 예시
<ErrorBoundary fallback={<ErrorDisplay />}>
  <Suspense fallback={<Loader />}>
    <MyCriticalComponent />
  </Suspense>
</ErrorBoundary>

3. 🧩 선언적 비동기 처리의 핵심: 관심사 분리

이 패턴의 핵심은 컴포넌트의 책임을 나누는 것입니다.

  • 비동기 컴포넌트: 데이터가 있다고 가정하고 '성공한 UI'만 작성합니다.
  • Suspense: 로딩 시점의 UI(Placeholder)를 결정합니다.
  • Error Boundary: 실패했을 때의 복구 UI를 결정합니다.

4. 🚀 실무 팁: react-query와의 시너지

최근 실무에서는 React Query (TanStack Query)와 함께 이 기능을 많이 사용합니다. suspense: true 옵션을 켜면 데이터 페칭 로직이 Suspense와 완벽하게 연동됩니다.

JavaScript
 
const { data } = useQuery({
  queryKey: ['user'],
  queryFn: fetchUser,
  suspense: true, // 이제 이 훅은 Suspense를 트리거합니다.
});

💡 시니어의 조언: 모든 비동기 컴포넌트를 각각 Suspense로 감쌀 필요는 없습니다. 의미 있는 UI 단위(예: 사이드바, 메인 콘텐츠 영역)로 묶어 최적의 '로딩 시퀀스'를 설계하는 것이 중요합니다.

반응형