티스토리 뷰

리액트에서 임의로 js파일에 에러를 냈을 때, 브라우저에서는 아무런 요소도 볼 수 없고 흰 화면만 나타난다. 

이유는 바로 "에러가 나면 스크립트가 실행을 멈추기 때문이다."

try - catch (+ finally)

 

예시 코드

  • 에러를 임의적으로 만들어놓은 상황(상수에 다른 값을 재할당했습니다.)
 const a = 1;
  a = 2;
  //위와 같이 임의로 에러를 냈을 때 에러난 부분부터 스크립트가 실행을 멈추고 
  // 아래의 코드는 실행이 안된다. 
  return (
    <Container>
      <div className="App" style={style.App}>
        <MyHeader />
        <h2 style={style.h2}>안녕하세용 😎</h2>
        <b id="bold_text" style={style.bold_text}>
          React.js
        </b>
        <Counter {...counterProps} />
        <MyFooter />
      </div>
    </Container>
  );
}

 

상수 a를 2로 변경하는 코드(에러코드) 이후의 멀쩡한 코드들은 그대로 브라우저에 렌더링시켜야 합니다. 

이럴 때 에러 핸들링을 해줍니다.

 

에러가난 부분은 보여주지 않더라도 정상적으로 구동하는 애들은 보여주기 위해서 사용하는 것이 try- catch 입니다. 

 try {
    const a = 1;
    a = 2;
  } catch (err) {
    console.log(err);
  }

이렇게 에러가 발생할 수도 있는 코드를 try내부의 코드로 넣어주고 catch의 블록 부분에 에러가 났을 때는 이걸 실행해줘 라는 에러 핸들링 코드를 적어줍니다.

-> 크롬 개발자 도구를 통해 콘솔을 확인해보면, TypeError(Assigment to constant variable) 가 붉은 글씨가 아니라 검정 글씨로 잘 찍힌 것을 확인할 수 있습니다.

⭐try catch 동작원리

try {
    const a = 1;
    a = 2;
  } catch (err) {
    console.log(err);
  } finally {
    console.log("끝났다.");
  }
  • try catch 구문은 에러가 나지 않았을 때는, catch 블록을 지나쳐 다음 코드를 진행합니다.
  • 만약에 에러가 나든 안나든 마지막으로 항상 실행시켜주고 싶은 코드가 있다면 finally를 마지막에 붙여주어 원하는 동작을 finally의 블록 내부에 작성해주시면 됩니다.  

⭐try catch는 모든 에러를 잡아주는 구문은 아니다.

try-catch 문은 오직 런타임 에러에서만 동작합니다.(읽을 수 있는 코드에서만 동작)
풀어서 설명하자면, 오직 읽을 수 있는 코드에 한하여 에러를 잡을 수 있다는 말입니다. 
자바스크립트는 코드를 실행하기 전에 미리 코드를 읽어보고 해당 코드를 실행할 수 있는지 확인을 거칩니다. 
이 과정에서 에러가 나면 parse-time에러가 납니다. 이런 parse-time에러는 try-catch로 잡을 수 없습니다. 
*parse-time error를 간단히 말하자면 중괄호가 덜 닫혔거나 하는 것을 예시로 들 수 있습니다. 

 

Error 객체 다루기(커스텀 에러)

try -catch의 catch 구문에서 받는 err를 typeof 로 찍어보면 객체임을 알 수 있다. 

Error, SyntaxError, ReferenceError, TypeError 등 이런 에러들을 표준 에러 객체라고 합니다. 
표준 에러 객체들은 전부 생성자를 사용해 직접 만들고 커스텀할 수 있습니다. 
*생성자를 사용해 - > new 키워드 사용
 try {
    const a = 1;
    a = 2;
  } catch (err) {
    console.log(typeof err); //object
    // JS에서는 에러도 객체이다. 규격화돼있는 표준 에러 객체

예시 

 let my_new_error = new Error("에러랍니다!!!");
let my_custom_syntaxError = new SyntaxError("끾끼");
console.log(my_new_error, my_custom_syntaxError);

const new_error = new TypeError("message");
    new_error.name = "타입에러";
    console.log(new_error);// 타입에러: message

이렇게 생성자 함수의 인자값으로 message를 전달할 수도 있는데 에러객체의 이름을 따로 지정해주지않으면 생성자 객체의 명과 동일하게 기본으로 들어가게 됩니다. 저는 new_error.name을 "타입에러"로 커스텀하였습니다. 

 

예시2 :

  • 값을 받아오는 것에는 에러가 없으나 받아온 값이 20이 아닐 경우에도 자체적으로 에러라고 처리하고 싶다.
  const data = { my_cat: "동글이", age: 20 };
  try {
    let cat_age = 29;
    console.log(cat_age);
    const age_error = new Error("동글이의 나이가 틀렸어요");
    if (cat_age !== 20) {
      console.log(age_error);
    }
  } catch (err) {
  } finally {
    console.log("끝났다.");
  }
  • 위의 코드는 try문에서 커스텀 에러를 발생시키고 있으므로 우리가 원하는 동작이 아닙니다.
  • catch 문으로 넘기고 싶을 때는 try블록에서 throw 에러객체 를 통해 가능합니다.
 const data = { my_cat: "동글이", age: 20 };

try {
    let cat_age = 29;
    console.log(cat_age);
    const age_error = new Error("동글이의 나이가 틀렸어요");
    if (cat_age !== 20) {
      throw age_error; // 커스텀 에러 객체를 catch 구문으로 넘겨줬다. 
    }
  } catch (err) {
    console.log(err); //여기서 출력되는 것은 age_error 객체
  } finally {
    console.log("끝났다.");
  }
🔥throw는 catch 블록에서도 사용이 가능합니다.
위의 경우에서 설명을 덧 붙이자면 만일 age_error가 아니라 값을 못 읽어와서 발생하는 에러가 있는 경우, 이 에러에 대해서도 처리를 해줘야 하는데 이럴 때는 try catch 구문 바깥에 try catch를 하나 더 씌워 내부 catch에서 에러를 throw 하면 외부 catch 구문에서 에러를 잡아낼 수 있습니다.
const data = { my_cat: "동글이", age: 20 };
  try {
    try {
      let cat_age = data.age.age.age; //없는 값에 접근
      console.log(cat_age);
      const age_error = new Error("동글이의 나이가 틀렸어요");
      age_error.name = "inner_err";
      if (cat_age !== 20) {
        throw age_error;
      }
    } catch (err) {
      if (err.name !== "inner_err") {
        throw err;
      }
    } finally {
      console.log("끝났다.");
    }
  } catch (err) {
    console.log("외부 catch:: ", err);
  }

👾ErrorBoundary 

자바스크립트에서는 에러가 나면 코드 실행을 멈춥니다. 
리액트에서 자바스크립트 코드 실행이 멈추게 되면? 새하얀 화면만 보여지게 됩니다. 
  • 자바스크립트 에러가 전체 어플리케이션을 중단시키게 됩니다.
  • 리액트는 이런 문제를 해결하기 위해 16버전부터 에러 바운더리라는 것을 도입했습니다.

: 에러 경계라고도 하며, 어떤 컴포넌트에서 자바스크립트 에러가 났을 시, 멈춰버린 컴포넌트 대신 fallback UI를 보여주도록 하는 리액트 컴포넌트를 말합니다.

  • 하지만, Error Boundary는 생명주기 함수인 getDrivedStateFromError() 와 componentDidCatch()를 정의한 "클래스형 컴포넌트"를 만들어야 합니다. (둘 중 하나를 정의해서 만든 클래스형 컴포넌트가 에러 경계가 된다함)

  • getDrivedStateFromError() - fallback UI보여주기 용
  • componentDidCatch() - 에러 기록용

즉, ErrorBoundary는 클래스형 컴포넌트에서만 유효한 기능인데 우리는 함수형 컴포넌트를 사용하죠. 이럴 때 쓰라고 만들어진 패키지가 있습니다. 

yarn add react-error-boundary

 

활용 예시 코드

src/ErrorB.js

import React from "react";

//에러 바운더리 라는 뜻
// 여기서 react-error-boundary 패키지를 쓸 것임
function ErrorB() {
  const somethingData = "데이터아님";
  if (somethingData !== "데이터") {
    throw new Error("이건 데이터가 아닙니다.");
  }
  return <div>{somethingData}</div>;
}

export default ErrorB;

임의적으로 에러를 발생시키는 컴포넌트를 하나 만들어줍니다. 

 

src/App.js

import ErrorB from "./ErrorB";
import { ErrorBoundary } from "react-error-boundary"; //에러 바운더리 컴포넌트를 가져옵니다.


// fallback으로 넣어줄 컴포넌트를 새로 만들고
const ErrorFallback = (err) => {
  console.log(err);
  return <div>에러 폴백입니다. </div>;
};

// App.js에 return 해주는 곳에 FallbackComponent로 만들어놓은 ErrorFallback 컴포넌트를 지정해줍니다. 

return (
<ErrorBoundary FallbackComponent={ErrorFallback}>
	<ErrorB />
</ErrorBoundary>
);

이렇게 하면 ErrorB 컴포넌트에서 에러가 생겼을 때 fallback컴포넌트로 빈화면이 아니라 대체 컴포넌트인 ErrorFallback이 화면에 보이게 됩니다. (*ErrorFallback도 컴포넌트라서 따로 js파일로 빼줘도 되는데 그냥 귀찮아서 내부에 같이 썼습니다. )

댓글