ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React] ErrorBoundary가 이벤트 핸들러와 비동기적 코드를 감지하지 못하는 이유
    React 2025. 3. 15. 22:51

     

     

     

    이 글은 ErrorBoundary의 기본적인 개념을 알고있다는 전제 하에 진행하겠습니다.

    (추후 에러바운더리 및 그 한계와 계층적 에러바운더리에 대한 글을 써보겠습니다.)

     

    ErrorBoundary가 잡을 수 있는 에러인지 아닌지 판단하는 기준은 ErrorBoundary 실행 컨텍스트 입니다.

    그 안에 있다면 잡을 수 있고, 그 안에 없다면 잡을 수 없습니다.

     

    에러바운더리는 다음과 같은 에러를 포착하지 못합니다.

     

     

    저는 지금까지 위와 같은 사항을 단순 암기했었는데요.

    현재 제가 지내고 있는 우아한 테크 코스의 코치인 시지프의 글을 보며 

    위 사항이 자바스크립트의 실행 컨텍스트를 알면 그럴 수 밖에 없다는 것을 알게되었습니다.

     

    네 가지 사항 중 에러바운더리가 이벤트 핸들러와 비동기적 코드를 감지하지 못하는 이유에 대해

    실행 컨텍스트 관점에서 집중적으로 분석해보겠습니다.

     

     

    우테코 코치이신 시지프께서 항상 예제로 들어주는 코드가 있습니다

     

    try {
      function throwErrorFn() {
        throw new Error("will it be catched?");
      }
      setTimeout(throwErrorFn, 1000);
    } catch (e) {
      console.log(e);
    }

     

    위 코드는 에러가 잡힐까요??

     

    정답은 '잡히지 않는다' 입니다.

     

    위 코드를 이해하기 위해서는 실행 컨텍스트에 대해서 알아야 하는데요.

     

    function bar(x) {
      return function foo(y) {
        return x + y;
      };
    }
    
    const add10 = foo(10);
    console.log(add10(5)); // 15

     

    이미지는 예시. 예제 코드에서는 baz가 없다는 것을 참고.

     

    위 이미지는 예시일 뿐이고, 결국 하나의 컨텍스트에서 에러가 발생하면 상위 컨텍스트로 전파가 된다는 것을 알 수 있습니다.

    foo에서 에러가 발생한다면 foo에서 throw되고, foo에서 처리되지 않으면 bar까지 갑니다. bar에서도 처리되지 않으면 전역 실행 컨텍스트로 전파가 된다는 말이 됩니다.

     

     

    이러한 지식을 바탕으로 setTimeout 예제를 다시 보면 왜 에러가 캐치되지 않는지 알 수 있습니다.

     

    setTimeout 내부에서 실행되는 throwErrorFn은 콜백 큐(Callback Queue)에서 실행되는데,

    이 콜백이 실행될 때에는 try-catch 블록이 이미 사라진 상태라서 예외를 잡지 못합니다.

     

     

    자, 이제 에러바운더리가 이벤트 핸들러와 비동기적 코드에 대한 에러를 잡지 못하는 이유에 대해 알아봅시다.

     

     

    먼저 이벤트 핸들러입니다.

     

    이 역시 리액트에서는 어떻게 이벤트를 다루는지 알아야 하는데요.

     

    자바스크립트에서는 DOM에 직접 이벤트를 등록합니다.

    document.getElementById("button").addEventListener("click", callback);

     

    이에 반해 리액트에서는 가상 돔을 이용한 root에 모든 이벤트가 위임됩니다.

     

    여기서 잠깐, 리액트의 이벤트 위임에 대해 간략하게 소개해드리겠습니다.

     

    일단, 리액트에서는 합성 이벤트(SyntheticEvent) 라는 객체를 이용하여 이벤트를 처리합니다.

    합성 이벤트(SyntheticEvent)란,

    javascript는 이벤트를 nativeEvent로 처리하는데, syntheticEvent는 nativeEvent를 한 번 매핑한 객체입니다.

    근데 이 native 객체를 계속 매핑하면 너무 많은 객체가 필요할 뿐아니라

    GC가 메모리에서 해제하는 기능도 필요하므로 성능상 문제가 발생하게 됩니다.

     

    이를 해결하기 위해 React 16까지는 event pool 방식을 사용하였는데요.

    여기서 event pool이란,

    synthetic Event instance들이 들어있는 공간으로,

    이벤트가 발생하면 이를 사용한 뒤 끝나면 바로 초기화 시키고 다시 풀로 돌려놓는 것을 의미합니다.

     

    얼핏보면 event pool이 좋아보이지만,

    핸들러 동작이 끝나면 곧바로 이벤트 객체를 초기화 하므로 비동기적인 동작은 실행되지 않는 한계가 있습니다.

    따라서 비동기 코드 내부에서 e에 접근하려면 e.persist() 같은 처리가 계속해서 필요한 리소스도 들죠.

     

     

    따라서 React 17에서부터 이러한 버그와 브라우저 성능향상에 도움이 안된다는 점으로 인해 더이상 event pooling을 사용하지 않고,

    이벤트 위임 방식을 변경하였습니다.

     

    기존엔 document에 이벤트를 전부 등록해놨다면, 지금은 root에 모든 '이벤트를 위임'하여 메모리를 절약합니다. (아래 이미지 참고)

    이벤트 위임(Event Delegation)은 다수의 자식 요소를 가지고 있는 상태에서 각 자식 요소에 각자의 이벤트 핸들러를 바인딩하는 대신 공통의 부모 요소에 하나의 이벤트 핸들러를 바인딩함으로써 성능과 메모리 사용성을 개선하는 패턴을 말합니다.

    → 쉽게말해 아랫사람이 아니라 윗사람에게 위임한다는 것 !

     

     

    혹시 이제 Error Boundary가 이벤트 핸들러에서 발생한 에러를 잡을 수 없는 이유가 감이 오시나요?

     

    리액트에서의 이벤트 핸들링은 전부 root에서 이루어 지고 있고, 이 곳에서 에러가 발생한다면 이것의 실행 컨텍스트는 ErrorBoundary 내부에 있지 않는 것입니다.

    따라서 ErrorBoundary가 이벤트 핸들러에서 발생한 에러를 잡을 수 없습니다.

     

    이를 해결하려면 어떻게 해야할까요?

     

    이벤트 핸들러 내부에서 try-catch를 사용해야 합니다.

    function ButtonWithError() {
      function handleClick() {
        try {
          throw new Error("이벤트 핸들러에서 에러 발생!");
        } catch (e) {
          console.error("이벤트 핸들러에서 예외 처리:", e);
        }
      }
    
      return <button onClick={handleClick}>Click Me</button>;
    }

     

     

    다음, 비동기적 코드를 감지하지 못하는 이유를 말씀드리겠습니다.

    이 이유는, 위에서 언급한 setTimeout 예제와 완벽히 동일한 케이스 입니다.

     

    setTimeout의 콜백은 1초 후에 실행되므로, 그 때는 이미 ErrorBoundary의 실행 컨텍스트가 끝난 시점이기 때문에 감지하지 못하는 것입니다. 

     

    이를 해결하려면 비동기적 코드의 실행 컨텍스트를 ErrorBoundary 실행 컨텍스트 안으로 넣어야 할 것입니다.

    더 구체적인 예시와 이벤트 핸들러 & 비동기적 코드를 감지하지 못하는 이유를 제외한 서버 사이드 렌더링과 자식에서가 아닌 에러 경계 자체에서 발생하는 에러를 캐치하지 못하는 이유, 그리고 계층적 에러바운더리에 관해서는 다음 글에서 포스팅 하겠습니다.

     

    긴 글 읽어주셔서 감사합니다.

     

     

    출처

    https://happysisyphe.tistory.com/66

     

    ErrorBoundary 가 포착할 수 없는 에러와 그 이론적 원리 분석

    서언 ErrorBoundary 는 하위 컴포넌트에서 발생하는 자바스크립트 에러를 잡아서 fallback UI를 보여주는 React 컴포넌트 입니다. 하지만 하위에 존재하는 컴포넌트에서 에러가 발생한다고 하여, 모든

    happysisyphe.tistory.com

     

     

Designed by Tistory.