<StrictMode>는 안의 모든 것에 대해 개발 환경에서만 추가 검사를 켜는 React 컴포넌트입니다. 각 컴포넌트를 두 번 렌더링하고, 마운트 시 모든 이펙트의 setup, cleanup, setup을 다시 실행하며, ref 콜백을 두 번 실행하고, 지원 중단된 API를 경고합니다. 그래서 개발 환경에서 useEffect가 "두 번 실행"됩니다. 프로덕션 빌드에서는 아무것도 하지 않습니다.
보통 main.jsx에서 앱을 감싸서 켭니다.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
createRoot(document.getElementById('root')).render(
<StrictMode>
<App />
</StrictMode>,
);
이 페이지의 미리보기는 프로덕션 빌드처럼 실행되므로 StrictMode가 눈에 보이는 효과를 내지 않습니다. 대신 아래의 각 예제에는 StrictMode가 잡아내려고 존재하는 종류의 버그가 들어 있고, 직접 그 버그를 일으켜 볼 수 있습니다. 여기서는 이펙트가 document 클릭 리스너를 추가하고 절대 제거하지 않습니다. 패널을 몇 번 숨겼다가 보여 준 다음 페이지를 클릭해 보세요.
Show를 누를 때마다 리스너가 하나씩 더 추가되고 아무것도 제거되지 않으므로, 클릭 한 번이 마운트 횟수만큼 로그를 남깁니다. 사용자가 패널을 열고 닫는 것만으로 일으킬 수 있는 누수입니다. StrictMode라면 첫 마운트에서 바로 보여 줄 것입니다. 개발 환경에서 마운트, 언마운트, 마운트를 하므로 버튼을 건드리기도 전에 클릭마다 로그가 두 줄씩 보일 것입니다.
이펙트: setup, cleanup, setup
마운트 시 StrictMode는 이펙트를 실행하고, 그 클린업을 실행하고, 다시 이펙트를 실행합니다. 사용자가 화면을 떠났다가 돌아오는 상황을 흉내 내면서 클린업이 setup을 되돌리는지 확인하는 것입니다. 되돌린다면 두 번째 setup은 setup을 한 번 했을 때와 정확히 같은 상태를 남기고, 아무 문제도 보이지 않습니다. 되돌리지 않는다면 버그가 바로 나타납니다.
로거를 고치는 방법은 추가한 것과 같은 함수를 제거하는 클린업입니다.
원하는 만큼 토글해 보세요. 로거가 보이는 동안 클릭마다 한 줄씩 기록됩니다. 개발 환경의 StrictMode라면 콘솔이 setup, cleanup, setup으로 시작하고, 앱은 여전히 올바르게 동작할 것입니다. 이 순서는 끄고 싶은 문제가 아니라 건강한 이펙트의 신호입니다.
클린업이 필요한 흔한 이펙트는 이벤트 리스너, setInterval과 setTimeout, 구독, WebSocket이나 채팅 연결, 그리고 컴포넌트가 사라졌다면 응답을 무시해야 하는 요청입니다. useEffect 페이지에서 각각의 클린업 패턴을 보여 줍니다.
렌더링: 두 번 호출
StrictMode는 렌더링마다 컴포넌트 함수를 두 번 호출하기도 합니다(그리고 useState, useMemo, useReducer에 넘기는 함수, 클래스의 constructor, render, shouldComponentUpdate도). React는 한 호출의 결과를 유지하고 다른 하나는 버립니다. 순수한 컴포넌트는 두 번 모두 같은 JSX를 반환하므로 아무것도 바뀌지 않습니다.
렌더링 중에 자기 바깥의 무언가를 바꾸는 컴포넌트는 순수하지 않으며, 두 번 호출하면 그 변경이 두 번 일어납니다. 이 컴포넌트는 prop으로 받은 배열에 "rendered at" 항목을 추가합니다.
다시 렌더링할 때마다 아무도 요청하지 않은 항목이 추가됩니다. 여기서는 클릭한 뒤에야 알아차립니다. StrictMode라면 첫 렌더링부터 새 항목이 하나가 아니라 두 개 보일 것입니다. 해결 방법은 변경하지 않고 렌더링 중에 계산하는 것입니다: const shown = [...entries, 'extra'].
React 19에서 세부 사항이 하나 추가되었습니다. 두 번 렌더링하는 동안 useMemo와 useCallback은 두 번 계산하지 않고 첫 번째 호출의 결과를 재사용합니다.
ref 콜백
React 19부터 StrictMode는 마운트 시 ref 콜백도 두 번 실행합니다. 연결, 클린업, 연결 순입니다. 리스너를 추가하거나 노드를 어딘가에 등록하는 ref 콜백은 이펙트와 똑같이 클린업을 반환해야 합니다.
<div
ref={(node) => {
const observer = new ResizeObserver(onResize);
observer.observe(node);
return () => observer.disconnect();
}}
/>
지원 중단된 API 경고
StrictMode는 사라질 예정인 API를 경고하며, 주로 클래스 컴포넌트의 오래된 UNSAFE_componentWillMount, UNSAFE_componentWillReceiveProps, UNSAFE_componentWillUpdate 생명주기 같은 것들입니다. 예전에 경고하던 문자열 ref와 findDOMNode 같은 일부 API는 React 19에서 완전히 제거되었습니다.
앱의 일부에만 켜기
<StrictMode>는 일반 컴포넌트이므로 트리의 일부만 감쌀 수 있습니다. 큰 앱에 도입할 때 도움이 됩니다. 새 화면부터 시작해서 넓혀 가세요.
function App() {
return (
<>
<Header />
<StrictMode>
<Sidebar />
<Content />
</StrictMode>
<Footer />
</>
);
}
Sidebar와 Content(그리고 그것들이 렌더링하는 모든 것)는 검사를 받고, Header와 Footer는 받지 않습니다. strict한 부분 안의 하위 트리에서 끌 방법은 없습니다.
기본으로 켜져 있는 곳은 다음과 같습니다.
- Vite: React 템플릿이
main.jsx에서<App />을<StrictMode>로 감쌉니다. - Next.js: App Router가 기본으로 켜며,
next.config.js의reactStrictMode로 제어합니다.
프로덕션에서는 아무것도 하지 않습니다
프로덕션 빌드는 <StrictMode>를 무시합니다. 렌더링 한 번, 이펙트 실행 한 번, 경고 없음입니다. 그래서 두 번 호출은 사용자에게 아무 비용도 들게 하지 않으며, 배포 전에 래퍼를 제거할 이유도 없습니다.
개발 환경에서 이펙트가 두 번 실행되어 요청이 두 번 가거나 분석 이벤트가 두 번 기록되는 등 무언가 깨진다면, 그것은 정보입니다. 대부분은 클린업이 해결책입니다(요청에는 ignore 플래그, 연결에는 연결 해제). SDK 초기화처럼 정말로 앱 로드당 한 번만 일어나야 하는 일이라면, 어떤 컴포넌트 바깥의 모듈 최상위에서 실행하거나 모듈 수준 플래그로 막으세요.
let didInit = false;
function App() {
useEffect(() => {
if (didInit) return;
didInit = true;
initAnalytics(); // runs once per page load, even in StrictMode
}, []);
}
두 번째 이펙트 실행을 건너뛰는 ref라는 흔한 우회책은 피하세요. 빠진 클린업을 고치지 않고 경고만 숨기며, 위의 로거처럼 컴포넌트가 실제로 언마운트되었다가 다시 마운트되는 순간 버그가 돌아옵니다.
자주 묻는 질문
React StrictMode는 무엇을 하나요?
개발 환경에서만, 안의 모든 컴포넌트를 두 번 렌더링하고, 컴포넌트가 마운트될 때 각 이펙트의 setup, cleanup, setup을 다시 실행하며, ref 콜백도 같은 방식으로 두 번 실행하고, 지원 중단된 API를 경고합니다. 추가 호출은 순수하지 않은 렌더링과 클린업이 빠진 이펙트를 드러냅니다.
useEffect가 두 번 실행되는 이유는 무엇인가요?
컴포넌트가 <StrictMode> 안에 있고 개발 빌드를 실행하고 있기 때문입니다. React가 컴포넌트를 마운트하고, 언마운트를 흉내 내고(클린업이 실행됩니다), 다시 마운트합니다. 두 번째 setup이 버그를 일으킨다면 그 이펙트는 클린업이 빠진 것입니다.
StrictMode는 프로덕션에 영향을 주나요?
아니요. 프로덕션 빌드는 <StrictMode>가 있든 없든 한 번 렌더링하고 각 이펙트를 한 번 실행합니다. 코드에 그대로 두어도 됩니다.
StrictMode는 어떻게 끄나요?
보통 main.jsx나 index.js에 있는 <StrictMode> 래퍼를 제거하거나, next.config.js에서 reactStrictMode: false를 설정하세요. 대개는 클린업을 고치는 편이 낫습니다. 두 번 실행은 실제 사용자도 겪을 수 있는 버그를 가리키고 있습니다.
console.log가 두 번 출력되는 이유는 무엇인가요?
StrictMode는 개발 환경에서 렌더링마다 컴포넌트 함수를 두 번 호출합니다. React DevTools 설정에서 두 번째 호출의 로그를 흐리게 표시하거나 숨길 수 있습니다.