axios 요청의 try-catch문에서 catch (error) {console.log(error)}로 인해 콘솔창에 나온 메세지였습니다. canceled라는 에러가 발생하였는데 요청을 취소하는 로직을 추가한 상태도 아니었습니다. Tanstack ReactQuery 라이브러리를 사용하고 있었기에 Tanstack에서 뭔가 이상을 감지하고 취소하였을 거라 생각도 해보았지만 요청이 취소될 이유가 전혀 없어 보였습니다. error의 signal 항목을 살펴보니 다음과 같이 reason이라는 항목이 있어 확인해보았습니다.


code: 20에 message: signal is aborted without reason"라는 것을 확인할 수 있었습니다. 아무런 정보를 얻지못했죠. 그래서 다른 곳에 원인이 있을 것이라고 생각하여 요청이 취소되는 상황에 대해서 검색해 보았습니다. 검색 결과 이는 React의 StrictMod로 인해 발생한 일입니다. 본론으로 들어가기 전에 StrictMode가 없었던 에러를 발생시키지는 않는다는 점을 명확히하겠습니다. 해당 문제의 정확한 원인은 글의 마지막에 설명하겠습니다.

StrictMode

React의 StrictMode는 개발 중의 버그를 더 빨리 발견할 수 있도록 도와줍니다. 공식 문서에서는 다음과 같은 역할을 한다고 설명합니다.

  • 순수하지 않은 렌더링으로 인해 발생하는 버그를 찾기 위해 컴포넌트가 추가로 다시 렌더링됩니다.
  • Effect 클린업이 누락되어 발생하는 버그를 찾기 위해 컴포넌트가 추가로 Effect를 다시 실행합니다.
  • 더 이상 사용되지 않는 API의 사용 여부를 확인하기 위해 컴포넌트를 검사합니다.(이러한 API는 주로 이전 클래스 컴포넌트에서 사용되므로 최신 앱에서는 거의 나타나지 않습니다.)
    StrictMode는 프로젝트를 생성할 때 기본적으로 모든 컴토넌트 트리에 대해 적용됩 상태로 만들어집니다.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';

const root = createRoot(document.getElementById('root'));
root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

위와 같이 App 컴포넌트가 StrictMode라는 컴포넌트에 둘러쌓여 있죠. 만약 특정 컴포넌트에 대한 검사만 필요하다면 <StrictMode>를 트리의 하단으로 옮길 수 있고 Strict모들를 원하지 않는다면 지울 수 있습니다. 하지만 React에서는 전체 컴포넌트를 Strict Mode로 래핑하는 것을 권장하고 있습니다. Strict Mode 검사는 개발 환경에서만 실행되지만, 이미 코드에 존재하지만 프로덕션에서 제대로 재현하기 어려울 수 있는 버그를 찾는 데 도움이 되기 때문입니다. 그럼 어떻게 버그를 재현하는데 도움을 줄까요?

개발 중 이중 렌더링을 통해 버그 발견하기

앞서 Strict Mode는 순수하지 않은 렌더링으로 인해 발생하는 버그를 찾기 위해 컴포넌트를 추가로 다시 렌더링한다고 하였습니다. 만약 다시 렌더링하였을 때, 두 번째 동작에서 내용이 변경된다면 이는 순수 함수가 아니며 순수 함수가 아닌 컴포넌트는 의도치 않은 동작을 수행할 수 있습니다. 즉 이중 렌더링에서 의도치 않은 동작이 발생한다면 순수 함수가 아닌 컴포넌트에 의해 버그가 발생한 것입니다.

React를 처음 실행해 보면 당황하게 되는 두 번의 console.log도 이 동작이 원인입니다. 브라우저의 확장프로그램인 React DevTools를 설치한다면 Strict Mode로 인한 console.log호출이 회색으로 표시되어 구분하기 쉬워집니다.

개발 환경에서 Effect를 다시 실행하여 발견된 버그 수정

Strict Mode는 Effects의 버그를 찾는 데도 도움이 될 수 있습니다. React는 컴포넌트가 마운트와 언마운트될 때 셋업과 클린업을 실행합니다. 그리고 useEffect의 의존성 배열이 들어있는 요소가 변경된 경우 클린업을 호출한 뒤 리렌더링이 시작되고 셋업을 호출하면서 리렌더링이 진행됩니다. Strict Mode가 켜져 있으면 셋업과 클린업 사이클이 한번 더 실행됩니다. 이를 통해 잘못된(보통은 클린업이 누락된) Effects를 쉽게 찾을 수 있습니다.

트러블 슈팅

결국 제가 마주쳤던 문제는 Strict Mode의 이중 렌더링으로 인한 두 번의 요청 중 하나는 취소되는 것이었습니다. 더 자세하게 하자면 요청의 취소는 Tanstack Query에 의한 것으로 생각되며, Tanstack Query는 요청을 보낸 페이지를 벗어나면 요청을 자동으로 취소하는 기능이 내장되어 있습니다. 혹은 요청을 보낸 돔 요소가 (리렌더링으로 인해) 삭제되었기 때문에 요청이 취소된 것일수도 있습니다. 둘 중 정확한 원인이 무엇이지는 확인할 방법을 모르겠으나 결국 트러블이 아니었던 트러블 슈팅이었습니다.

 

참고 자료 

React, <StrictMode>

+ Recent posts