Front-end/React.js

30. componentDidCatch 메서드 - 리액트 입문 | 벨로퍼트

AGAL 2020. 7. 8. 23:22
반응형

 

이번에는, componentDidCatch 라는 생명주기 메서드를 사용하여 리액트 애플리케이션에서 발생하는 에러를 처리하는 방법을 알아보도록 하겠습니다.

 

먼저, 이번 튜토리얼을 진행 하기 위하여 새로운 프로젝트를 만들어주세요.

$ npx create-react-app error-catch

 

그 다음에, 해당 디렉터리를 에디터로 열고, 개발 서버를 시작해주세요.

$ cd error-catch

$ yarn start

 

리액트 앱에서 에러가 발생하는 상황

리액트 앱이 어떤 상황에서 에러가 발생하게 되는지 알아봅시다.

우선, User.js 라는 파일을 src 디렉터리에 생성하여 다음과 같이 컴포넌트 코드를 작성해보세요.

/src/User.js :

import React from 'react';

function User({ user }) {
  return (
    <div>
      <div>
        <b>ID</b>: {user.id}
      </div>
      <div>
        <b>Username:</b> {user.username}
      </div>
    </div>
  );
}

export default User;

이 컴포넌트는 user 라는 props 를 받아와서 해당 데이터의 id 와 username 값을 보여줍니다.

 

이 컴포넌트를 한번 App 컴포넌트에서 사용해보세요.

/src/App.js :

import React from 'react';
import User from './User';

function App() {
  const user = {
    id: 1,
    username: 'AGAL'
  };
  return <User user={user} />;
}

export default App;

만약에 user props 를 제대로 설정하지 않았다면 어떨까요?

function App() {
  // ...
  return <User />;
}

에러가 발생하게 됩니다. 이 화면은 개발환경에서만 보여지는 에러화면인데요, 우측 상단의 X 버튼을 눌러보시면 실제 환경에서 보여지는 화면이 나타나게 됩니다.

 

실제 환경에서는 아무것도 렌더링되지 않고 흰 페이지만 나타나게 됩니다. 만약에 여러분이 만든 서비스에서 사용자가 이러한 상황을 겪게 된다면 상당히 당황스럽겠지요?

 

이번 튜토리얼에서는 이런 상황에 이렇게 흰 화면을 보여주는 대신에, 에러가 발생했다는 것을 알려주는 방법에 대하여 알아볼것입니다.

이에 대하여 진행하기 전에 어떤 상황에 또 이런 에러가 발생하는지 알아보고, 에러를 방지 할 수 있는 방법에 대해서도 알아보겠습니다.

 

일단, 방금과 같은 에러를 방지하려면 User 컴포넌트에서 다음과 같은 작업을 하시면 됩니다.

/src/User.js :

function User({ user }) {
  if (!user) {
    return null;
  }

  // ...
}

이렇게 하시면 user 값이 존재하지 않는다면 null 을 렌더링하게 됩니다. 리액트 컴포넌트에서 null 을 렌더링하게되면 아무것도 나타나지 않게 됩니다. 이를 "null checking" 이라고 부릅니다.

 

코드를 이렇게 작성해주시면, 화면에 아무것도 보여지지 않는것은 마찬가지이지만, 적어도 에러는 발생하지 않습니다. 보통 데이터를 네트워크 요청을 통하여 나중에 데이터를 받아오게 되는 상황이 발생하는 경우 이렇게 데이터가 없으면 null 을 보여주거나, 아니면 <div>로딩중</div>과 같은 결과물을 렌더링하시면 됩니다.

 

에러가 발생 할 수 있는 또 다른 상황에 대해서 알아봅시다.

function Users({ users }) {
  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.username}</li>
      ))}
    </ul>
  );
}

만약에 위와 같은 컴포넌트에 users 값을 설정해주지 않았을 때에도 렌더링 과정에서 오류가 발생하게 됩니다. users 가 undefined 이면 당연히 배열의 내장함수 map 또한 존재하지 않기 때문이죠.

 

때문에 다음과 같이 users 가 없으면 다른 결과물을 반환하는 작업을 해주셔야 합니다.

function Users({ users }) {
  if (!users) return null;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.username}</li>
      ))}
    </ul>
  );
}

또 다른 상황으로는 다음과 같은 상황이 있습니다.

function Users({ users, onToggle }) {
  if (!users) return null;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id} onClick={() => onToggle(user.id)}>
          {user.username}
        </li>
      ))}
    </ul>
  );
}

만약에 위 컴포넌트에 onToggle props 를 전달하지 않으면, 에러가 발생하게 될 것입니다. 에러를 방지하기 위해선 onToggle 을 props 로 넣어주는 것을 까먹지 않기 위해서 다음과 같이 defaultProps 설정을 해주는 방법이 있습니다.

function Users({ users, onToggle }) {
  if (!users) return null;

  return (
    <ul>
      {users.map(user => (
        <li key={user.id} onClick={() => onToggle(user.id)}>
          {user.username}
        </li>
      ))}
    </ul>
  );
}

Users.defaultProps = {
  onToggle: () => {
    console.warn('onToggle is missing!');
  }
};

다른 솔루션으로는 PropTypes 라는 것을 사용하는 방법도 있습니다. PropTypes 를 사용하면 필요한 데이터를 넣지 않았을 때 개발 단계에서 경고를 볼 수 있기 때문에 실수로 props 설정을 깜박하는 일을 방지 할 수 있습니다. 단, 사용법이 좀 불편하고 귀찮기 때문에 이를 사용하는 것 대신에 나중에 TypeScript 또는 Flow 를 사용해서 관리 하는 것을 권장 드립니다. (이 강좌에서는 추후 TypeScript 를 사용하게 됩니다.)

 

componentDidCatch 로 에러 잡아내기

자, 이번에는 componentDidCatch 생명주기 메서드를 사용하여 우리가 사전에 예외처리를 하지 않은 에러가 발생 했을 때 사용자에게 에러가 발생했다고 알려주는 화면을 보여줘봅시다.

 

우선, src 디렉터리에 ErrorBoundary 라는 컴포넌트를 만들어보세요.

/src/ErrorBoundary.js :

import React, { Component } from 'react';

class ErrorBoundary extends Component {
  state = {
    error: false
  };

  componentDidCatch(error, info) {
    console.log('에러가 발생했습니다.');
    console.log({
      error,
      info
    });
    this.setState({
      error: true
    });
  }

  render() {
    if (this.state.error) {
      return <h1>에러 발생!</h1>;
    }
    return this.props.children;
  }
}

export default ErrorBoundary;

여기서 componentDidCatch 메서드에는 두개의 파라미터를 사용하게 되는데 첫번째 파라미터는 에러의 내용, 두번째 파라미터에서는 에러가 발생한 위치를 알려줍니다.

 

이 메서드에서 현재 컴포넌트 상태 error 를 true 로 설정을 해주고, render() 메서드에서는 만약 this.state.error 값이 true 라면 에러가 발생했다는 문구를 렌더링하도록 하고 그렇지 않다면 this.props.children 을 렌더링하도록 처리를 해주세요.

 

그 다음에 App 컴포넌트에서 <User /> 컴포넌트를 감싸주세요.

/src/App.js :

import React from 'react';
import User from './User';
import ErrorBoundary from './ErrorBoundary';

function App() {
  const user = {
    id: 1,
    username: 'velopert'
  };
  return (
    <ErrorBoundary>
      <User />
    </ErrorBoundary>
  );
}

export default App;

그리고 이전에 User 컴포넌트에서 null checking 을 하는 코드를 주석처리해보세요.

/src/User.js :

import React from 'react';

function User({ user }) {
  // if (!user) {
  //   return null;
  // }

  return (
    <div>
      <div>
        <b>ID</b>: {user.id}
      </div>
      <div>
        <b>Username:</b> {user.username}
      </div>
    </div>
  );
}

export default User;

그리고 나서 브라우저를 열어보면 다음과 같이 아까전에 봤었던 에러 화면이 나타나겠지만,

우측 상단의 X 버튼을 누르고 나면 흰 화면이 아닌 "에러 발생!" 이라는 문구가 보여지게 될 것입니다.

반응형