C
React/고급/Lesson 20

Error Boundary 에러 처리

30분·theory
이 챕터
1/2
TypeScript

Error Boundary 에러 처리

💡 왜 배워야 할까요?

🎯 한 컴포넌트의 버그가 전체 앱을 먹통으로 만드는 것을 방지합니다.
💼 사용자는 에러 화면 대신 '문제가 있습니다' 메시지만 봅니다.
프로덕션 환경에서 필수. 안정적인 서비스 = 취업 경쟁력입니다.
🏢 실무에서는
은행 앱에서 송금 기능에 버그가 발생했을 때, Error Boundary가 없으면 전체 앱이 흰 화면으로 변합니다. Error Boundary가 있으면 송금 부분만 '일시적 오류입니다' 메시지를 보여주고 다른 기능(잔액 조회 등)은 계속 사용 가능합니다.

개념

Error Boundary는 React 컴포넌트 트리에서 에러가 발생했을 때 전체 앱이 크래시되는 것을 방지하고, 대신 fallback UI를 보여주는 에러 격리 메커니즘입니다. 실무에서는 사용자 경험을 보호하고 안정적인 서비스 운영을 위해 필수적입니다.

왜 중요한가?

실제 운영 환경에서 서드파티 라이브러리 에러나 예상치 못한 데이터로 인한 런타임 에러가 발생하면 전체 화면이 하얀 페이지로 변하는데, Error Boundary가 있으면 특정 섹션만 에러 UI로 대체되어 사용자가 계속 서비스를 이용할 수 있습니다. 또한 에러 리포팅과 연동하여 장애 감지와 디버깅에도 활용됩니다.

핵심 개념

Error Boundary는 마치 건물의 방화벽과 같습니다. 한 방에서 불이 나더라도 방화벽이 있으면 다른 방으로 번지지 않죠. React에서 Error Boundary는 컴포넌트 트리의 특정 부분에서 에러가 발생해도 전체 앱이 멈추지 않도록 에러를 '격리'하고, 사용자에게는 적절한 fallback UI를 보여줍니다.

핵심 포인트

  • 클래스 컴포넌트에서만 구현 가능 (React 19 기준)
  • 하위 컴포넌트의 렌더링 에러와 라이프사이클 메서드 에러만 잡음
  • 이벤트 핸들러, 비동기 코드, 서버사이드 렌더링 에러는 잡지 못함
  • 에러 로깅과 연동하여 운영 모니터링에 활용
💻 나쁜 예시 - Error Boundary 없는 컴포넌트
// 에러 격리 없는 위험한 컴포넌트
const UserProfile = () => {
  const [user, setUser] = useState<User | null>(null);
  
  return (
    <div>
      <h1>{user.name}</h1> {/* user가 null이면 전체 앱 크래시 */}
      <img src={user.avatar} alt="프로필" />
      <ThirdPartyWidget userId={user.id} /> {/* 서드파티 에러 시 크래시 */}
    </div>
  );
};

// App.tsx
const App = () => {
  return (
    <div>
      <Header />
      <UserProfile /> {/* 여기서 에러 나면 전체 화면 하얀 페이지 */}
      <Footer />
    </div>
  );
};
💻 좋은 예시 - 2025년 권장 Error Boundary 패턴
// 현대적인 Error Boundary 클래스 (타입스크립트)
interface ErrorBoundaryState {
  hasError: boolean;
  error?: Error;
}

class ErrorBoundary extends Component<
  PropsWithChildren<{ fallback?: ReactNode; onError?: (error: Error) => void }>,
  ErrorBoundaryState
> {
  constructor(props: any) {
    super(props);
    this.state = { hasError: false };
  }

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

  componentDidCatch(error: Error, errorInfo: ErrorInfo) {
    // 에러 로깅 (Sentry, LogRocket 등)
    console.error('Error Boundary caught an error:', error, errorInfo);
    this.props.onError?.(error);
    
    // 운영환경에서는 에러 리포팅 서비스로 전송
    if (process.env.NODE_ENV === 'production') {
      // Sentry.captureException(error, { contexts: { errorInfo } });
    }
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="error-fallback">
          <h2>문제가 발생했습니다</h2>
          <p>잠시 후 다시 시도해주세요.</p>
          <button onClick={() => this.setState({ hasError: false })}>다시 시도</button>
        </div>
      );
    }

    return this.props.children;
  }
}

// 사용법 - 전략적 배치
const App = () => {
  return (
    <div>
      <Header />
      <ErrorBoundary 
        fallback={<UserProfileError />}
        onError={(error) => analytics.track('user_profile_error', { error: error.message })}
      >
        <UserProfile />
      </ErrorBoundary>
      
      <ErrorBoundary fallback={<div>추천 상품을 불러올 수 없습니다</div>}>
        <RecommendationSection />
      </ErrorBoundary>
      
      <Footer /> {/* 에러가 나더라도 Footer는 계속 보임 */}
    </div>
  );
};
💻 고급 예시 - React-Error-Boundary 라이브러리 활용
// 현업에서 많이 사용하는 react-error-boundary 라이브러리
import { ErrorBoundary, withErrorBoundary } from 'react-error-boundary';

// 커스텀 fallback 컴포넌트
const ErrorFallback = ({ error, resetErrorBoundary }: any) => {
  return (
    <div className="error-boundary-fallback" role="alert">
      <h2>오류가 발생했습니다</h2>
      <pre style={{ whiteSpace: 'normal' }}>{error.message}</pre>
      <button onClick={resetErrorBoundary}>다시 시도</button>
    </div>
  );
};

// HOC 패턴으로 컴포넌트 감싸기
const SafeUserDashboard = withErrorBoundary(UserDashboard, {
  FallbackComponent: ErrorFallback,
  onError: (error, errorInfo) => {
    // 에러 리포팅
    logErrorToService(error, errorInfo);
  },
});

// React Query와 함께 사용하는 현대적 패턴
const App = () => {
  return (
    <QueryClientProvider client={queryClient}>
      <ErrorBoundary
        FallbackComponent={ErrorFallback}
        onReset={() => {
          // 에러 리셋 시 캐시 무효화
          queryClient.invalidateQueries();
        }}
        resetKeys={['user']} // 의존성이 변경되면 자동 리셋
      >
        <UserDashboard />
      </ErrorBoundary>
    </QueryClientProvider>
  );
};

💡 ⚠️ 흔한 실수

  • Error Boundary를 너무 상위에만 두어서 에러 발생 시 전체 페이지가 fallback UI로 변하는 경우
  • 이벤트 핸들러나 비동기 코드의 에러도 Error Boundary가 잡을 것이라고 착각하는 경우 (try-catch 별도 필요)
  • 에러 로깅 없이 단순히 fallback UI만 보여주어 운영 중 문제 파악이 어려워지는 경우

💡 🎯 면접 준비

Q: Error Boundary가 잡을 수 있는 에러와 잡을 수 없는 에러의 차이점을 설명해주세요
Q: Error Boundary를 어떻게 전략적으로 배치하시겠습니까?
Q: Error Boundary와 try-catch의 차이점은 무엇인가요?

힌트: Error Boundary는 렌더링 중 에러만 잡고, 이벤트/비동기는 try-catch 필요. 전략적 배치로 에러 격리하여 UX 보호. getDerivedStateFromError와 componentDidCatch의 역할 구분. 에러 리포팅과 모니터링 연동의 중요성 강조.

⚛️ React 패턴 — Error Boundary 에러 처리

Error Boundary 에러 처리을 React에서 어떻게 쓰는지 코드와 함께 단계별로 익혀보세요.
1 🧩 1. Error Boundary 에러 처리 사용 시나리오
이 기능이 필요한 상황.
2 💻 2. 코드 작성
Error Boundary 에러 처리의 기본 사용법.
3 🎨 3. 렌더링 결과
사용자가 보는 화면.
4 💡 4. 실무 팁
흔한 함정과 베스트 프랙티스.

🎮 Error Boundary 에러 처리 — 단계별 이해

각 단계를 클릭해 내용을 읽고 ✓ 이해했어요 버튼으로 진행률을 확인하세요.
🖥️ 실행 결과 — 렌더링된 React 컴포넌트
✏️ React 코드 수정하기 (클릭해서 열기)
⚛️ React 18 + Babel Standalone — 결과 먼저 확인 후 코드 편집기에서 직접 수정해볼 수 있습니다.

확인 퀴즈

Error Boundary가 잡을 수 없는 에러는?
💡 Error Boundary는 렌더링, 생명주기 메서드, 생성자 에러를 잡아요. 이벤트 핸들러, 비동기 코드(setTimeout, Promise), SSR 에러는 Error Boundary로 잡히지 않아요.
먼저 읽으면 좋은 개념: React.memo / useMemo / useCallback
Error Boundary + Suspense - React