[React] Error Boundary(에러 바운더리)

apro_xo·2022년 12월 6일
10
post-thumbnail

Error Boundary?

에러 바운더리(에러 경계)는 리액트를 사용하여 구현할 시 사용되는 컴포넌트 에러를 핸들링하는 방법이다.

리액트 컴포넌트 함수에서 반환되는 jsx, 또는 render() 함수에서 반환되는 jsx를 렌더링 하는 도중 에러를 만나면 컴포넌트 렌더링을 멈춰버리며, 사용자는 빈 화면을 보게 된다.

이를 리액트 공식문서에서는 컴포넌트가 깨진다고 표현을 한다.

에러로 인해 컴포넌트가 깨지는 경우 대체 컴포넌트(fallback component)를 보여주도록 하는 것이 Error Boundary이다.

Error Boundary 사용법

Error Boundary는 클래스형 컴포넌트에서만 사용 가능하다.
함수형 컴포넌트에서 사용하기 위해서는 react-error-boundary 패키지를 설치하여 사용한다.

1. 클래스형 컴포넌트

기본적으로 error boundary는 생명주기 함수인 getDerivedStateFromError()와 componentDidCatch()를 정의한 클래스형 컴포넌트를 만들어야 한다.
둘 중 하나를 정의해서 만든 클래스형 컴포넌트가 에러 경계가 되어 동작한다.

getDerivedStateFromError()를 사용해서 fallback component를 보여주고, componentDidCatch()를 사용해서 에러 내용을 기록하는 형태로 사용하면 된다.

2. 함수형 컴포넌트🔥

2-1. 패키지 설치

npm install react-error-boundary

2-2. react-error-boundary 사용하기

// Person.jsx
const Person = () => {
  const person_name = "철수";

  if (person_name !== "영희") {
    const new_error = new Error("영희가 아니에요!");
    new_error.name = "이름이 달라요";
    new_error.person_name = person_name;

    throw new_error;
  }

  return <div>{person_name}</div>;
};

export default Person;
// app.jsx
import Person from './Person';
import {ErrorBoundary} from 'react-error-boundary';

const ErrorFallback = () => {
  return(<div>에러났어요@@</div>)
}

function App() {
  return (
    <div className="App">
      <ErrorBoundary FallbackComponent={ErrorFallback}>
      <Person />
      </ErrorBoundary>
    </div>
  );
}

export default App;

person.jsx라는 임의의 컴포넌트를 만들었다.
커스텀 에러를 사용하여 고의적으로 에러를 발생시켜 보았다.

app.jsx에서는 컴포넌트가 깨졌을 때 대신 보여줄 fallback component인 ErrorFallback 컴포넌트를 만들고 App 컴포넌트에서 사용하였다.

FallbackComponent 속성에 만들었던 fallback component를 넣어주어 사용하며 정확한 코드 사용법은 위 코드를 참고하자.

코드를 실행하면 정상적으로 fallback component가 보여지는 것을 알 수 있다.

2-3. 에러 로깅하기

클래스형 컴포넌트에서 에러를 로깅하기 위해서는 생명 주기 함수인 componentDidCatch()를 사용해야 하는데,
react-error-boundary 패키지를 사용할 때는 어떻게 로깅해야할까?

만들었던 fallback component에서 에러를 로깅할 수 있다. 아래와 같이 사용한다.

import Person from './Person';
import {ErrorBoundary} from 'react-error-boundary';


// 에러 로깅 가능
const ErrorFallback = (err) => {

  console.log(err.error.name);
  console.log(err.error.person_name);
  return(<div>에러났어요@@</div>)
}

function App() {

  return (
    <div className="App">
      <ErrorBoundary FallbackComponent={ErrorFallback}>
      <Person />
      </ErrorBoundary>
    </div>
  );
}

export default App;

fallback component에서 error 객체를 받고 내부의 error 속성으로 에러 데이터에 접근할 수 있다.

person.jsx에서 커스텀 에러를 만들었는데, name과 person_name 속성을 직접 커스텀하였었다.

// person.jsx
if (person_name !== "영희") {
    const new_error = new Error("영희가 아니에요!");
    new_error.name = "이름이 달라요";
    new_error.person_name = person_name;

    throw new_error;
  }

위 코드를 실행한 후 개발자 도구를 열어보면 정상적으로 에러에 포함된 데이터가 출력됨을 알 수 있다.

Error Boundary가 잡지 못하는 에러

공식 문서에서는 아래와 같이 명시했다.
https://reactjs.org/docs/error-boundaries.htmlhttps://reactjs.org/docs/error-boundaries.html

이벤트 핸들러에서 에러 처리를 할 때는 try-catch문을 사용하여 에러를 처리하는 것이 좋다고 문서에 명시 되어있다.

그렇다.

useErrorHandler()

위에 명시된 경우와 같이 에러 바운더리가 감지하지 못하는 에러는 try/catch 문 등을 사용하여 직접 핸들링해야 하는데,
react-error-boundary에서는 이를 쉽게 해주는 useErrorHandler 기능을 제공한다.

나는 fetch()를 이용한 비동기 코드로 테스트해봤다.

우선, data 디렉토리를 만들고 그 안에 data.json을 만들어 임의의 데이터를 생성하였다.

{
    "person":[
    {
        "name":"철수",
        "age":56,
        "gender":"male"
    },
    {
        "name":"영희",
        "age":56,
        "gender":"male"
    }
  ]
}

그리고 이 데이터를 fetch()를 통해 받아오는 코드를 Person.jsx의 useEffect에 작성하였는데,
에러를 고의로 발생시키기 위해 경로에 오타를 내보았다.

// App.js
import Person from './Person';
import {ErrorBoundary} from 'react-error-boundary';

const ErrorFallback = (err) => {

  console.log(err.error.name);
  console.log(err.error.person_name);
  return(<div>에러났어요@@</div>)
}

function App() {

  return (
    <div className="App">
      <ErrorBoundary FallbackComponent={ErrorFallback}>
      <Person />
      </ErrorBoundary>
    </div>
  );
}

export default App;
// Person.jsx
import React, { useEffect, useState } from "react";
import { useErrorHandler } from 'react-error-boundary';

const Person = () => {
  const person_name = "영희";
  const handleError = useErrorHandler();

  useEffect(() => {
    const getPersonInfo = async () => {
      try {
        const res = await fetch("pata.json", {
          headers: {
            Accept: "application/json",
          },
          method: "GET",
        });
        const data = await res.json()
        console.log(data);
      }
      catch(err) {
        handleError(err);
      }
    };

    getPersonInfo();
  }, []);

  return <div>{person_name}</div>;
};

export default Person;

위 코드를 실행해보면 에러 바운더리가 비동기 코드임에도 불구하고 에러를 잘 잡아내는 것을 확인 할 수 있다. fallback UI도 잘 뜬다.

profile
유능한 프론트엔드 개발자가 되고픈 사람😀

0개의 댓글