React - Error Boundaries

이소라·2022년 8월 9일
0

React

목록 보기
7/23

Error Boundaries

  • Error Boundaries가 나오게 된 배경
    • 과거에는 컴포넌트 내부의 JavaScript 에러가 React의 내부 상태를 훼손하고 다음 렌더링에서 암호화된 에러 방출을 유발했음
    • 이러한 에러는 항상 애플리케이션 코드의 이전 단계의 에러로 인해 발생했지만, React는 컴포넌트 내에서 에러를 정상적으로 처리할 수 있는 방법을 제공하지 않아서 이를 복구할 수 없었음
    • UI의 일부분에 존재하는 JavaScript 에러가 전체 애플리케이션을 중단시키는 문제를 해결하기 위해서 React 16에서는 에러 경계(Error Boundaries)라는 개념을 도입함

Error Boundaries 소개

  • Error Boundaries

    • 하위 컴포넌트 트리의 어디서든 JavaScript 에러를 잡아서 그 에러들을 기록하고, 깨진 컴포넌트 트리 대신 fallback UI를 보여주는 React 컴포넌트
    • Error boundaries는 렌더링 도중에, 생명주기 메서드 중에, Error boundaries 아래의 전체 트리의 constructor에서 에러를 잡음
  • Error boundaries가 잡지 않는 에러들

    • 이벤트 핸들러
    • 비동기적 코드 (예: setTimeOut 혹은 requestAnimationFrame 콜백)
    • 서버 사이드 렌더링
    • 자식 컴포넌트가 아니라 Error boundary 자체에서 발생하는 에러
  • 클래스 컴포넌트에서 생명주기 메서드 static getDerivedStateFromError()componentDidCatch()를 정의하면 그 클래스 컴포넌트 자체가 error boundary가 됨

    • static getDerivedStateFromError()
      • 하위의 자손 컴포넌트에서 에러가 발생했을 때 호출되는 메서드
      • 매개변수로 에러를 받고, 갱신된 state값을 반드시 반환해야 함
      • 이 메서드는 render 단계에서 호출되므로, 부수효과를 발생시키면 안 됨
      • 부수효과를 발생시키는 경우, componentDidCatch()를 대신 사용하기
    • componentDidCatch()
      • 자손 컴포넌트에서 에러가 발생했을 때 호출되는 메서드
      • 발생한 에러인 error와 어떤 컴포넌트가 오류를 발생시켰는지에 대한 정보를 componentStack 키의 값으로 갖고 있는 객체 info를 매개변수로 받음
      • 이 메서드는 커밋 단계(React가 변경 사항을 반영하는 단계)에서 호출되므로, 부수 효과를 발생시켜도 됨
      • 이 메서드의 오류를 처리하는 방식이 프로덕션 빌드와 개발 빌드가 다름
      • 개발 빌드에서는 오류가 window까지 전파되므로, window.onerrorwindow.addEventLister('error', callback)가 이 메서드에서 잡은 오류를 인터셉트함
      • 프로덕션 빌드에서는 오류가 전파되지 않으므로, 상위 오류 핸들러에서는 이 메서드에 의해 명시적으로 잡히지 않은 오류만 받음
      • React15의 unstable_handleError 메서드는 더 이상 동작하지 않으며 componentDidCatch로 변경해야함
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }
  
  static getDerivedStateFromError(error) {
    // state를 갱신하여 다음 렌더링에서 대체 UI를 표시함
    return { hasError: true };
  }
  
  componentDidCatch(error, info) {
    // Example "componentStack":
    //	in ComponentThatThrows (created by App)
    // 	in ErrorBoundary (created by App)
    //	in div (created by App)
    //	in App
    logComponentStackToMyService(info.componentStack);
  }
  
  render() {
    if (this.state.hasError) {
      // 별도로 작성한 대체 UI를 렌더링할 수 있음
      return <h1>Something went wrong.</h1>
    }
    
    return this.props.children;
  }
}

class App extends React.Component {
  return (
  <ErrorBoundary>
    <MyWidget />
  </ErrorBoundary>
  );
}
  • Error boundaries는 JavaScript의 catch {] 구문과 유사하게 동작하지만 컴포넌트에 적용됨
  • 클래스 컴포넌트만 Error boundaries가 될 수 있음
  • Error bondary는 트리 내에서 하위에 존재하는 컴포넌트의 에러만을 포착함
    • Error boundary 자체적으로는 에러를 포착할 수 없음
    • Error boundary가 에러 메세지를 렌더링하는 데 실패한 경우, 에러는 그 위에서 가장 가까운 Error boundary로 전달됨

Error Boundaries를 배치할 위치

  • Error boundraies의 배치 위치는 개발자의 의도에 달려 있음
    • 최상위 경로의 컴포넌트를 감싸서 유저에게 "문제가 발생했습니다"라는 메세지를 보여줄 수 있음
    • 각 위젯을 Error boundary로 감싸서 애플리케이션의 나머지 부분이 충돌하지 않도록 보호할 수 있음

포착되지 않는 에러에 대한 새로운 동작

  • React16부터는 Error boundaries에서 포착되지 않은 에러로 인해 전체 React 컴포넌트 트리의 마운트가 해제됨

    • 이유 : 손상된 UI를 완전히 제거하는 것보다 그대로 남겨두는 것이 더 좋지 않으므로
    • 예) 메신저와 같은 제품에서 손상된 UI를 그대로 남겨두면 누군가 잘못된 사람에게 메세지를 보내게 될 가능성이 있음
  • 프로덕션 환경에서 발생한 처리되지 않은 예외 상황에 대해 학습하고 수정할 수 있도록 JavaScript 에러 리포팅 서비스를 활용하거나 직접 작성하는 것을 권장함


컴포넌트 스택 추적

  • React 16은 애플리케이션이 실수로 에러를 집어삼킨 경우에도 개발 과정에서 렌더링하는 동안 발생한 모든 에러를 콘솔에 출력함
  • React 16은 컴포넌트 스택 추적을 제공함
    • 정확히 컴포넌트 트리의 어느 부분에서 에러가 발생했는지 확인할 수 있게 됨
    • 스택 추적 내에서 파일 이름과 줄 번호도 확인할 수 있게 됨
    • Create React App 프로젝트 내에서 기본적을 동작함
    • Create React App을 사용하지 않는 경우, 수동으로 @babel/plugin-transform-react-jsx-source 플러그인을 Babel 설정에 추가하여 사용할 수 있음
    • 이 기능은 개발 단계를 위해서만 제작됨 (프로덕션 환경에서는 비활성화해야함)
    • 스택 추적에 표시되는 컴포넌트 이름은 Function.name 프로퍼티에 따라 다름


Error Boundaries와 try/catch

  • try/catch는 명령형 코드에서만 동작함
try {
  showButton();
} catch (error) {
  // ...
}
  • React 컴포넌트는 선언적이며 무엇을 렌더링할지 구체화함
<Button />
  • Error Boundaries는 React의 선언적인 특성을 보존하고 예상한 대로 동작함
    • 예) 트리 깊숙한 어딘가에 있는 setState에 의해 유발된 componentDidUpdate 메서드에서 에러가 발생하더라도 가장 가까운 error boundary에 올바르게 전달됨

Error Boundaries와 Error in Event Hanlder

  • Error Boundaries는 이벤트 핸들러 내부의 에러를 포착하지 `않음
    • 이벤트 핸들러는 렌더링 중에 발생하지 않음
    • 이벤트 핸들러가 에러를 던져도 React는 여전히 화면에 무엇을 표시해야 할지 알고 있음
  • 이벤트 핸들러 내에서 에러를 잡아야하는 경우, 일반 JavaScript의 try/ catch 구문을 이용하기
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { error: null };
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    try {
      // 에러를 던질 수 있는 무언가를 해야합니다.
    } catch (error) {
      this.setState({ error });
    }
  }

  render() {
    if (this.state.error) {
      return <h1>Caught an error.</h1>
    }
    return <button onClick={this.handleClick}>Click Me</button>
  }
}

0개의 댓글