Error Boundary는 자식 컴포넌트 트리에서 JavaScript 오류를 포착하여 충돌 페이지 대신 폴백 인터페이스를 표시하는 React 컴포넌트입니다. React Native의 맥락에서는 치명적이지 않은 렌더링 오류 발생 시 애플리케이션의 완전한 충돌을 방지합니다. React Documentation, 2024에 따르면, error boundary는 자식 컴포넌트의 render 메서드, 생명주기 훅 및 생성자에서 오류를 포착하여 애플리케이션이 계속 작동할 수 있게 합니다. React Native에서는 모바일 앱을 F5로 다시 로드할 수 없기 때문에 이것이 특히 중요합니다 — 사용자는 전체 세션을 잃게 됩니다.
핵심 내용
Error Boundary는 렌더링 오류 발생 시 원활한 성능 저하를 위한 React 메커니즘입니다. React 16(2017)에서 두 가지 생명주기 메서드(static getDerivedStateFromError 또는 componentDidCatch) 중 하나를 구현하는 래퍼 컴포넌트로 도입되었습니다. Error Boundary를 사용하면 빈 흰색 화면이나 완전한 앱 충돌 대신 사용자에게 의미 있는 메시지를 표시할 수 있습니다.
React 16 이전에는 render에서 처리되지 않은 오류가 발생하면 전체 DOM 트리가 마운트 해제되면서 완전한 앱 충돌이 발생했습니다. 웹 애플리케이션에서는 빈 흰색 화면을 의미했고, React Native에서는 홈 화면으로 돌아가는 완전한 충돌을 의미했습니다. React 팀은 선언적 UI를 위한 catch 블록의 아날로그로 Error Boundary를 도입했으며, Erlang 언어의 “let it crash” 접근 방식에서 개념을 차용했습니다.
React Native에서 Error Boundary가 없으면 렌더링 오류 발생 시 완전한 앱 충돌이 발생합니다. 사용자는 복구 옵션 없이 현재 세션 전체를 잃게 됩니다. React Native에서 Error Boundary는 매우 중요합니다. 모바일 앱은 웹 페이지처럼 다시 로드할 수 없기 때문에 사용자 세션이 돌이킬 수 없게 손실되고 사용자는 처음부터 다시 시작해야 합니다.
Error Boundary는 React 트리 수준에서 작동합니다. 자식 컴포넌트가 render 또는 생명주기에서 오류를 throw하면 React는 전체 트리를 마운트 해제하지 않고 계층 구조에서 가장 가까운 Error Boundary로 제어를 전달합니다. Boundary는 getDerivedStateFromError를 호출하고, state.hasError = true로 설정하며, 손상된 컴포넌트 브랜치 대신 폴백 UI를 렌더링합니다.
import React, { Component, ErrorInfo, ReactNode } from "react"
interface Props {
children: ReactNode
fallback?: ReactNode
}
interface State {
hasError: boolean
error?: Error
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false }
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error("Caught by boundary:", error)
Crashlytics.recordException(error)
}
render() {
if (this.state.hasError) {
return this.props.fallback || <FallbackUI />
}
return this.props.children
}
}
getDerivedStateFromError는 폴백 UI 렌더링을 위한 상태를 설정합니다 — 변경 사항을 커밋하기 전에 렌더링 단계에서 호출되는 정적 메서드입니다. componentDidCatch는 커밋 단계에서 실행되며 부작용(로깅, Crashlytics에 충돌 보고서 전송, 분석)을 위해 사용됩니다. 두 메서드는 UI 상태 관리와 부작용 간의 책임을 분리합니다.
Error Boundary를 생성하려면 getDerivedStateFromError 및/또는 componentDidCatch 메서드가 있는 클래스 컴포넌트를 구현해야 합니다. 함수형 컴포넌트는 Error Boundary가 될 수 없습니다 — React는 생명주기 메서드에 대한 액세스가 필요하기 때문에 이 기능을 클래스 컴포넌트에서만 지원합니다. react-error-boundary 라이브러리는 편의를 위해 훅 기반 API가 있는 기성 구현을 제공합니다.
// React Native에서 Error Boundary 사용 예
import { ErrorBoundary } from "react-error-boundary"
const FallbackComponent = ({ error, resetError }: FallbackProps) => (
<View style={styles.container}>
<Text>문제가 발생했습니다</Text>
<Text>{error.message}</Text>
<Button title="재시도" onPress={resetError} />
</View>
)
const App = () => (
<SafeAreaView>
<ErrorBoundary FallbackComponent={<FallbackComponent />}>
<UserProfile userId={"123"} />
</ErrorBoundary>
<BottomNavigation />
</SafeAreaView>
)
래퍼 수준 — Error Boundary는 계층 구조의 다양한 수준에 배치할 수 있습니다. 앱 루트에 하나의 전역 Boundary를 두면 모든 오류에서 폴백 UI가 표시되지만 탐색은 유지됩니다. 화면 수준에서 여러 Boundary를 사용하면 오류를 격리할 수 있습니다. 한 화면이 손상되어도 나머지는 독립적으로 계속 작동합니다. react-error-boundary는 useErrorBoundary 훅을 통해 재설정 메커니즘을 단순화하여 다시 로드 없이 상태를 재설정할 수 있습니다. 일반적인 프로젝트의 경우 React Native 애플리케이션에 3단계 Boundary 체계가 최적으로 간주됩니다.
오류 발생 후 사용자는 hasError를 false로 재설정하고 자식 트리를 다시 렌더링하는 “재시도” 버튼을 클릭할 수 있습니다. 재설정 메커니즘은 다시 로드 없이 애플리케이션 기능을 복원하는 데 중요합니다. react-error-boundary에서는 onReset 콜백을 사용하며, 캐시를 지우거나 데이터를 다시 가져오거나 트리 상위에서 상태를 업데이트할 수 있습니다.
Error Boundary는 비동기 오류를 포착하지 않습니다 — setTimeout, setInterval, Promise, async/await 내의 오류입니다. React는 렌더링 주기와 생명주기 훅 외부의 오류를 가로챌 수 없습니다. 이러한 오류는 다른 실행 컨텍스트에서 실행되기 때문입니다. 비동기 오류의 경우 핸들러 내에 별도의 try-catch를 두거나 전역 unhandledrejection 이벤트 핸들러가 필요합니다.
onClick, onChange 및 기타 이벤트 핸들러의 오류는 React 렌더링 외부에서 실행되므로 Error Boundary가 포착하지 않습니다. 이벤트 핸들러의 오류 처리는 try-catch를 사용하여 핸들러 자체 내에서 수행해야 합니다. react-error-boundary 라이브러리는 이벤트 핸들러에서 가장 가까운 Boundary로 오류를 throw하는 useErrorHandler 훅을 제공합니다.
Error Boundary는 Next.js 또는 Gatsby의 서버 사이드에서 작동하지 않습니다. getDerivedStateFromError 및 componentDidCatch 메서드는 SSR 중에 호출되지 않습니다. 생명주기 메서드는 브라우저에서만 사용할 수 있기 때문입니다. 서버 오류의 경우 별도 전략이 필요합니다: getServerSideProps의 try-catch, error.js 폴백 페이지(Next.js 13+), 또는 전역 미들웨어.
React Native에서 Error Boundary는 네이티브 모듈 수준의 충돌을 방지하지 않습니다. 네이티브 충돌(segfault, 메모리 부족, 네이티브 예외)은 Objective-C 또는 Java 수준에서 발생하며 JavaScript 레이어에 도달하지 않습니다. 네이티브 충돌의 경우 Crashlytics NDK(Android) 또는 KSCrash(iOS)가 필요합니다. Error Boundary는 React Native 애플리케이션의 JavaScript 레이어만 보호합니다.
Error Boundary를 논리적 모듈의 경계에 배치하세요: 화면당 하나의 Boundary, 타사 위젯당 하나, 복잡한 양식당 하나입니다. 이렇게 하면 오류가 격리되고 사용자가 애플리케이션의 다른 부분에서 계속 작업할 수 있습니다. 루트 Boundary는 항상 있어야 합니다 — 공통 탐색 컴포넌트 또는 공급자에서 발생하는 심각한 오류에 대비합니다. 각 Boundary는 자체 인터페이스 조각을 담당하며 오류 발생 시 인접한 컴포넌트에 영향을 주지 않습니다.
항상 componentDidCatch를 통해 Crashlytics 또는 Sentry로 오류를 전달하세요. 컨텍스트(화면 이름, userId, 앱 버전, 탐색 매개변수)를 추가하세요. Sentry에서는 오류로 이어지는 사용자 작업 시퀀스인 breadcrumbs를 사용할 수 있습니다. 비치명적 오류의 빈도를 분석하려면 이슈별로 그룹화된 Crashlytics 대시보드를 사용하세요.
빈 폴백을 사용하지 말고 의미 있는 인터페이스를 만드세요. React Native의 권장 세트: 오류 메시지(사용자 친화적, 기술적이지 않음), “재시도” 버튼, 지원 또는 채팅 링크. 빈 View는 피하세요 — 사용자가 앱이 완전히 충돌했다고 생각하고 닫을 것입니다. 폴백은 앱의 전체 디자인에 통합되어야 합니다.
React Testing Library 또는 React Native Testing Library를 사용하여 각 Error Boundary를 테스트하세요. 렌더링 시 오류를 throw하는 트리거 컴포넌트를 만들고 폴백 UI가 표시되는지 확인하세요. 통합 테스트의 경우 다양한 Error Boundary 상태(정상, 오류, 재설정 후 상태)로 storybook을 사용하세요. Boundary의 자동화된 테스트는 컴포넌트가 변경되어도 폴백 UI가 프로덕션에서 올바르게 계속 작동하도록 보장합니다. 각 Boundary의 테스트 커버리지는 React Native 프로젝트의 코드 리뷰 필수 요구 사항이어야 합니다.
자주 묻는 질문
React는 Error Boundary를 클래스 컴포넌트에서만 구현합니다. componentDidCatch 및 getDerivedStateFromError 생명주기 메서드에 대한 액세스가 필요하기 때문입니다. 함수형 컴포넌트에는 이러한 메서드가 없습니다. react-error-boundary 라이브러리는 사용 편의성을 위해 훅 기반 API가 있는 기성 클래스 래퍼를 제공합니다.
영향은 미미합니다 — Error Boundary는 자식 트리가 렌더링될 때마다 상태 검사를 추가합니다. state.hasError 비교는 상수 복잡도의 O(1) 연산입니다. 오류가 없으면 오버헤드가 없습니다. 오류가 발생한 경우에만 Boundary가 폴백 UI를 추가로 렌더링합니다.
아니요, 2–3 수준이면 충분합니다: 전체 애플리케이션을 위한 루트 Boundary, 각 탐색 브랜치를 위한 화면 Boundary, 중요한 위젯(결제 양식, 지도, 채팅)을 위한 로컬 Boundary. 과도한 Boundary 사용은 큰 이점 없이 아키텍처를 복잡하게 만듭니다.
Error Boundary와 Suspense는 독립적입니다: Suspense는 로딩(React 18+의 보류 중인 Promise)을 포착하고, Error Boundary는 렌더링 오류를 포착합니다. 다음과 같이 결합할 수 있습니다: <ErrorBoundary><Suspense><Component /></Suspense></ErrorBoundary>. 로딩 중에는 Suspense가 먼저 트리거되고, 로드된 컴포넌트의 오류 시 Error Boundary가 트리거됩니다.
try-catch는 동기 명령형 코드의 오류를 포착하지만 JSX 렌더링 오류는 가로챌 수 없습니다. Error Boundary는 선언적 UI를 위해 특별히 설계되었습니다: render, 생명주기 훅 및 자식 컴포넌트의 생성자에서 오류를 가로챕니다. 이는 React 렌더링의 특성상 try-catch가 할 수 없는 일입니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.