pile·
프론트엔드·카카오 엔터테인먼트 FE카카오 엔터테인먼트 FE·

React의 Error Boundary를 이용하여 효과적으로 에러 처리하기

카카오페이지 웹이 React 16 의 Error Boundary 로 비동기 호출 에러와 렌더링 중 런타임 에러를 함께 처리하는 방식을 공유한 글이다. API 호출 상태를 맡는 Fetcher 컴포넌트가 에러를 그냥 throw 하고, 두 층의 Error Boundary 가 영역별로 잡아 처리한다.

핵심 포인트
  • 기존 방식은 에러 상태를 redux 로 관리해 에러 메시지 컴포넌트를 렌더링하는 것이었고, 세 가지 아쉬움이 남았다.
  • 선언적으로 에러를 처리할 방법, 서버 점검이나 네트워크 에러처럼 공통 처리할 에러를 한곳에서 다룰 방법, 사용자가 재시도해 에러를 리셋할 트리거가 필요했다.
  • 여기에 더해 렌더링 중 TypeError 같은 예상치 못한 런타임 에러가 나면 React 트리 전체가 마운트 해제돼 사용자가 빈 화면을 보게 된다.
  • Error Boundary 는 하위 컴포넌트 트리에서 렌더링 중 throw 된 에러를 잡아 선언적으로 처리한다. try-catch 처럼 동작하고, 처리하지 못해 re-throw 하면 가장 가까운 상위 Error Boundary 가 잡는다.
  • 구조는 두 층이다. Fetcher 바로 위의 ApiErrorBoundary 가 제한된 영역을, 루트의 GlobalErrorBoundary 가 페이지 전체를 담당한다.
상세 정리
  • Fetcher 는 React 18 의 Suspense 에서 힌트를 얻어 만든 컴포넌트로, API 호출과 상태를 관리하면서 선언형으로 쓸 수 있다.
  • 카카오페이지 웹은 next 12 를 쓰고 있어 React 18 로 올리지 못했고, 그래서 Suspense 대신 같은 컨셉의 Fetcher 를 쓴다.
  • Fetcher 는 에러가 나면 자체적으로 에러 컴포넌트를 보여주지 않고 그냥 throw 한다. 에러에 대한 책임을 Error Boundary 로 넘기는 설계다.
  • 던져지는 에러 객체는 카카오페이지 웹에서 쓰는 에러 코드 정보를 담고 있다.
  • ApiErrorBoundary 는 각 Fetcher 위에 선언돼 그 영역에만 에러 컴포넌트를 보여준다. 자신이 처리할 수 없는 에러 코드는 render 단계에서 rethrow 한다.
  • GlobalErrorBoundary 는 루트에 위치해 서버 점검처럼 페이지 전체에 에러를 보여줘야 하는 경우와, ApiErrorBoundary 로 감싸이지 않은 컴포넌트의 에러를 잡는다.
  • 두 층을 합치면 렌더링 중 발생하는 모든 에러를 처리할 수 있는 구조가 된다.
  • Error Boundary 는 에러 컴포넌트만 보여주는 데 그치지 않는다. 리셋 처리로 하위 Fetcher 를 re-mount 시켜 API 를 다시 호출하게 한다.
  • 그래서 사용자는 다시 시도 버튼으로 해당 영역의 콘텐츠만 재요청할 수 있고, 에러가 난 영역 밖의 콘텐츠는 그대로 볼 수 있다.
  • 예시는 카카오페이지 콘텐츠 홈의 추천 작품 영역이다. 그 영역의 API 가 실패해도 페이지 전체가 아니라 해당 블록만 에러 화면이 된다.
  • 저자는 Fetcher 와 Error Boundary 조합이 개발 경험과 사용자 경험 양쪽에서 괜찮았다고 정리한다.
왜 읽나컴포넌트마다 에러 상태를 들고 분기하느라 코드가 지저분해진 React 팀에게, 에러 책임을 경계 컴포넌트로 옮기는 구조와 재시도 처리를 함께 보여주는 사례.
카카오 엔터테인먼트 FE
카카오 엔터테인먼트 FE 블로그
원문은 여기서 이어서 읽을 수 있어요
원문 읽기
읽음 (0)

이 글과 비슷한

  1. 프론트엔드·여기어때 (GC컴퍼니)여기어때 (GC컴퍼니)·

    항공 프론트엔드 구축기 (7/10): 창구를 하나만 두었습니다

    여기어때 항공 서비스 프론트엔드가 웹과 앱 웹뷰 두 환경에서 동일한 함수 호출로 동작하는 앱 브릿지 추상화 레이어를 설계한 과정을 다룬다. iOS·안드로이드 규약 차이와 "웹에 존재하지 않는 브릿지를 어떻게 호출하나"라는 문제를 단일 추상화 층으로 해결한 구현 사례다.

    요약 이어보기
    #react#typescript#webview+2