| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | ||||||
| 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 9 | 10 | 11 | 12 | 13 | 14 | 15 |
| 16 | 17 | 18 | 19 | 20 | 21 | 22 |
| 23 | 24 | 25 | 26 | 27 | 28 | 29 |
| 30 | 31 |
- html
- 인텔리제이
- 단위테스트
- junit5
- javascript
- SpringBoot
- 자바
- HashMap
- 자바스크립트
- Java
- Array
- Eclipse
- 정규식
- input
- java테스트
- 배열
- js
- math
- 자바문법
- 스프링부트
- vscode
- Visual Studio Code
- ArrayList
- 테스트자동화
- string
- 문자열
- junit
- IntelliJ
- list
- CSS
- Today
- Total
어제 오늘 내일
React StrictMode 완벽 가이드: 왜 내 코드는 두 번 실행될까? 본문
React 프로젝트를 진행하다 보면 한 번쯤 이런 경험을 하게 됩니다.
"어? 분명 useEffect에 console.log를 하나만 찍었는데 왜 터미널과 브라우저 콘솔에 두 번씩 나올까?"
"API 요청을 한 번만 보내야 하는데 왜 두 번이나 호출되는 거지?"
이 현상의 주범은 바로 React StrictMode입니다. 이번 글에서는 StrictMode가 무엇인지, 왜 개발 환경에서 코드를 두 번 실행하는지, 그리고 이를 통해 어떤 이점을 얻을 수 있는지 정리해 보겠습니다.
1. React StrictMode란?
React.StrictMode는 React 애플리케이션 내의 잠재적인 문제를 감지하기 위한 개발 전용 도구입니다.
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
- UI 영향 없음:
StrictMode는 별도의 DOM 요소를 생성하지 않으며, visual한 UI를 변경하지 않습니다. - 개발 모드 전용: Development 빌드에서만 동작합니다. Production 빌드에서는 이 모든 검사 로직이 자동으로 비활성화되어 성능이나 동작에 아무런 영향을 주지 않습니다.
2. StrictMode의 핵심 기능 3가지
① 순수성(Purity) 검사를 위한 이중 렌더링 (Double Rendering)
React의 컴포넌트 렌더링 함수는 순수 함수(Pure Function)여야 합니다. 동일한 입력(props, state)이 주어지면 항상 동일한 JSX를 반환해야 하며, 렌더링 도중에 외부 변수를 수정하는 등의 사이드 이펙트(Side Effect)가 없어야 합니다.
StrictMode는 컴포넌트가 순수한지 확인하기 위해 렌더링 단계를 의도적으로 두 번 실행합니다.
// ❌ 잘못된 작성법: 렌더링 중에 외부 변수를 직접 수정 (Impure)
let count = 0;
function Counter() {
count++; // 사이드 이펙트 발생!
return <h1>Count: {count}</h1>;
}
- StrictMode가 없다면: 1, 2, 3...처럼 동작하는 것처럼 보여 버그를 놓치기 쉽습니다.
- StrictMode가 있다면: 첫 렌더링 시
count가 바로 2가 되는 이상 현상이 관찰되어, 코드가 비순수(Impure)하다는 것을 빠르게 감지할 수 있습니다.
② Effect의 Cleanup 검사 (React 18 이상)
React 18부터 StrictMode는 컴포넌트가 마운트될 때 useEffect를 다음과 같은 순서로 실행합니다.
$$\text{Mount (Setup)} \longrightarrow \text{Unmount (Cleanup)} \longrightarrow \text{Remount (Setup)}$$
즉, [Setup $\rightarrow$ Cleanup $\rightarrow$ Setup] 과정을 강제로 한 번 더 거칩니다.
왜 이렇게까지 할까요?
미래의 React 기능(예: Offscreen API, 화면 전환 시 이전 상태 보존 등)은 컴포넌트를 DOM에서 제거했다가 나중에 이전 상태 그대로 다시 복원(Remount)할 수 있어야 합니다.
만약 useEffect에 정리(Cleanup) 로직이 빠져 있다면, 컴포넌트가 재마운트될 때 메모리 누수나 중복 이벤트 리스너가 등록되는 버그가 발생합니다. StrictMode는 이 문제를 미리 찾아낼 수 있도록 강제로 재마운트 시뮬레이션을 수행합니다.
③ 레거시 API 사용 감지
오래된 React 코드베이스나 구버전 라이브러리를 사용할 때 발생할 수 있는 위험 요소들을 감지하여 콘솔 경고를 띄워줍니다.
- String Ref 사용 경고:
ref="myInput"대신useRef()또는 콜백 Ref 사용 권장 - findDOMNode 사용 경고: 캡슐화를 깨뜨리는
findDOMNode대신 Ref 사용 권장 - 구버전 라이프사이클 메서드 경고:
componentWillMount등 안전하지 않은 메서드 감지
3. 자주 묻는 질문: "API 두 번 호출되는 건 어떻게 해결하나요?"
가장 흔한 실수는 "API가 두 번 호출되니까 StrictMode를 꺼버려야지!" 라고 생각하는 것입니다. 이는 문제를 해결하는 것이 아니라 버그를 숨기는 것입니다.
❌ 잘못된 해결 방법 (StrictMode 제거)
index.jsx에서 <React.StrictMode>를 지워버리면 개발 모드에서 더 이상 두 번 실행되지 않지만, 클린업이 누락된 잠재적 버그는 그대로 남게 됩니다.
✅ 올바른 해결 방법 (Cleanup 로직 작성)
useEffect에서 비동기 데이터 패칭 시 AbortController 또는 클린업 플래그를 활용하여 언마운트 시 취소 처리 로직을 작성해야 합니다.
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// ① 네트워크 요청을 취소할 수 있는 AbortController 인스턴스 생성
const controller = new AbortController();
async function fetchUser() {
try {
// fetch 옵션의 signal에 controller.signal을 전달하여 연동
const response = await fetch(`/api/users/${userId}`, {
signal: controller.signal
});
const data = await response.json();
setUser(data);
} catch (error) {
// 의도적인 요청 취소(AbortError)인 경우 콘솔에 에러를 출력하지 않고 무시
if (error.name !== 'AbortError') {
console.error('Fetch error:', error);
}
}
}
// 비동기 요청 함수 실행
fetchUser();
// ② Cleanup 함수 반환:
// 컴포넌트가 언마운트되거나 userId가 바뀔 때 실행됩니다.
// StrictMode가 [Setup -> Cleanup -> Setup]을 시뮬레이션할 때,
// 첫 번째 Setup에서 발생한 요청을 안전하게 취소(abort)하여 데이터 유실 및 중복 요청 문제를 방지합니다.
return () => {
controller.abort();
};
}, [userId]); // userId 변경 시 Effect 재실행
return <div>{user ? user.name : 'Loading...'}</div>;
}
이렇게 클린업 함수를 올바르게 구현하면, StrictMode가 첫 번째 실행 후 즉시 클린업을 호출하므로 첫 번째 네트워크 요청이 취소되고 두 번째 요청만 정상 반영됩니다.
4. 요약: Development vs Production
| 항목 | 개발 환경 (Development) | 프로덕션 (Production) |
|---|---|---|
| 렌더링 횟수 | 2회 실행 (사이드 이펙트 검사) | 1회 실행 |
| useEffect 실행 | Setup $\rightarrow$ Cleanup $\rightarrow$ Setup | Setup만 실행 (마운트 시) |
| 성능 영향 | 검사 로직으로 인한 약간의 부하 | 영향 없음 (0%) |
| 콘솔 경고 | 레거시 API 및 이펙트 경고 출력 | 출력 안 됨 |
💡 결론
React StrictMode는 개발자를 괴롭히기 위한 도구가 아니라, 더 안전하고 예측 가능한 코드를 작성할 수 있도록 돕는 든든한 가드레일입니다.
'IT > React' 카테고리의 다른 글
| [React] useEffect 사용법 (개념, 의존성 배열, Clean-up 함수) (0) | 2026.08.02 |
|---|---|
| [React] useState 사용법 (개념, 흔히 하는 실수, 주의할 점) (0) | 2026.08.02 |
| [React] AboutController로 비동기 처리하기 (0) | 2026.08.02 |
| [React] Create React App (CRA) 으로 개발환경 구성하기 (1) | 2021.11.04 |
| [React] babel과 webpack의 차이 (0) | 2021.11.04 |