개념/2025 학습

React 18에서 Strict Mode

주섬이 2025. 4. 20. 18:27
반응형

React 18에서 Strict Mode

 

 

React Strict Mode란?

개발 환경에서만 작동하며 잠재적인 버그나 비권장 사용으로 인해 사이드 이펙트를 조기에 감지하기 위해 동작하는 React의 개발 도구이다.

<React.StrictMode> 
	<App /> 
</React.StrictMode>

 

 

React 18에서의 Strict Mode는 '더 엄격해졌다'

React 18에서 Concurrent Features (비동기 렌더링, 자동 배치 등)이 도입되면서 Strict Mode 아래에서 일부 함수형 컴포넌트가 의도적으로 두번 호출된다. - (관련글, 가상돔(Virtual DOM)과 React Fiber 구조)

즉, 개발자들이 혼란스러워 할 수 있는 '컴포넌트가 두 번 렌더링 되는 현상'이 여기에 포함된다.

개발 모드 환경에서만 한정으로 의도적으로 추가적인 재실행을 발생시키는데,

초기 렌더와 cleanup 을 검증하기 위해서 진행되며 개발 도구로서 테스트 하는 동작이며 의도하지않은 사이드 이펙트를 방지하려는 의도가 포함되어있다.

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>
);

 

 

예시

개발 모드에서 mount → unmount → mount 과정을 거치게 된다.

실제로 일어나는 건 '렌더링 2번'이 아니라 컴포넌트의 '생명주기 전체를 반복'한다는 의미이다.

function Component() { 
	useEffect(() => { 
    	console.log('✅ mount'); 
        
        return () => { 
        	console.log('❌ unmount'); 
        }; 
    }, []); 
    
    console.log('🎯 render'); 
    
    return <div>hello</div>; 
}

 

출력

  • 🎯 render
  • ✅ mount
  • ❌ unmount
  • 🎯 render
  • ✅ mount

 

의도적인 재마운트를 통한 개선점이 React 18에서 가장 큰 변화이며 useEffect, cleanup 함수, useLayoutEffect, useState 초기화, 컴포넌트 함수 전체에서 발생시킨다.

즉, 안전한 Concurrent Mode에서 문제가 없도록 코드의 강건성을 강화하고 마운트/언마운트를 감지하여 잘못된 로직을 조기에 발견하여 안전한지 미리 테스트하려는 목적을 가졌다. 

 

목적

항목  체크 포인트
useEffect 안에서 API 호출 등 side effect  idempotent(재실행 안전) 해야 함
useRef, setInterval, setTimeout ✅ 두 번 설정되지 않도록 정리 함수(cleanup) 필요
외부 SDK 초기화 ✅ 초기화 중복 방지 필요 (mountedRef 등으로 방어)
이벤트 핸들러 등록 ✅ 한 번만 등록되도록 useEffect 내 조건 분기 필수

 

 

Strict Mode 비활성화

개발 중 StrictMode를 끄고 싶다면 <React.StrictMode> 대신 <></> 로 감싸면 된다.

하지만 개발 품질 보장에 도움이 되는 기능이니 안전하게 코드를 작성하기 위해서 활성화시키는 것을 추천한다.

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

 

 


 

 

Q. '실제로 일어나는 건 '렌더링 2번'이 아니라 '컴포넌트의 생명주기 전체를 반복'한다는 의미' 와 '렌더링이 2번 반복된다' 랑 같은 의미 아냐?

A. 차이가 있다.

 

'어디까지가 렌더링이라 보느냐'의 문제이고 'React에서 렌더링과 생명주기를 어떻게 개념적으로 나눌수 있느냐'의 부분인것같다.

  • 렌더링(rendering) = 컴포넌트 함수 실행 + JSX 반환
  • 생명주기(lifeCycle) = mount → effect → cleanup → unmount 등 React가 관리하는 전체 흐름

 

구조적 동작

렌더링

  • 컴포넌트 함수가 실행되어 JSX를 반환한다. 그리고 이를 바탕으로 가상 DOM 트리를 구성하는 과정이다.
  • 실제 DOM에 반영여부는 아직 결정되지 않았고 오직 'UI를 어떻게 그릴지' 결정만 하는 단계이다. 
    • 화면에 반영할지 말지 판단만하는 논리적 구성단계이며 렌더링은 실제로 보이는 것이 아니다. 
  • React의 함수 컴포넌트의 순수 함수 실행에 가까운 단계이다.

생명주기

  • 리액트 컴포넌트가 생성되고 DOM에 부착되고 제거되기까지의 일련의 과정 

 

따라서 개념으로 봤을때 Strict Mode는 추가적으로 발생하는 것은 '렌더링 부터 생명주기까지 1회'가 맞는 표현이다. 

하지만 실제로 렌더링이 발생하면 전체 생명주기가 실행되기 때문에 '렌더링이 1회 추가로 됐다'라는 표현도 실제 개발 현장에서는 통용 가능하고 자연스럽다.

 

 


 

 

주요 방어 패턴

useRef를 사용한 hasMounted

마운트 여부 추적

import { useEffect, useRef } from 'react'; 

export function useHasMountedRef() { 
	const hasMountedRef = useRef(false);
    
    useEffect(() => { 
    	hasMountedRef.current = true; 
    }, []); 
    
    return hasMountedRef; 
}

const hasMountedRef = useHasMountedRef(); 

useEffect(() => { 
	if (!hasMountedRef.current) return; 
    
    // React.StrictMode 중복 방지 
    doSomething(); 
}, [someDependency]);

 

 

useIfFirstRender()

첫 렌더 여부 체크

import { useRef } from 'react'; 

export function useIsFirstRender() { 
	const isFirst = useRef(true); 
    
    if (isFirst.current) { 
    	isFirst.current = false; 
        
        return true; 
    } 
    return false; 
}

// 사용
const isFirst = useIsFirstRender(); 
useEffect(() => { 
	if (!isFirst) { 
    	fetchData(); 
    } 
}, []);

 

 

useEffectOnce()

진짜로 한 번만 실행

import { useEffect, useRef } from 'react'; 

export function useEffectOnce(effect: () => void | (() => void)) { 
const hasRun = useRef(false); 

useEffect(() => { 
    if (hasRun.current) return; 

    hasRun.current = true; 
    	return effect();
    }, []); 
}

useEffectOnce(() => { console.log('실제 마운트에 딱 1번만 실행됨!'); });

 

 

반응형