useState의 지연 초기화 (Lazy Initialization) 완벽 정리 🚀

앗가이·2025년 3월 25일

React

목록 보기
2/2

리액트를 사용하다 보면 컴포넌트의 상태를 관리하기 위해 useState를 많이 사용합니다. 하지만 초기 상태를 설정할 때 무거운 연산이 포함된 함수를 잘못 사용하면 성능 저하가 발생할 수 있습니다. 이를 해결하는 방법이 지연 초기화(Lazy Initialization) 입니다.


1️⃣ 지연 초기화란?

지연 초기화(Lazy Initialization)란, useState를 사용할 때 초기 상태를 설정하는 함수가 렌더링 시마다 불필요하게 실행되지 않도록 최적화하는 기법입니다.

❌ 잘못된 예: useState(veryHeavyComputation())

function App() {
  const [id, setId] = useState(veryHeavyComputation());
  return <div>{id}</div>;
}

위 코드에서는 veryHeavyComputation()이 즉시 실행되어 useState로 함수의 실행 결과 값이 전달됩니다. 컴포넌트가 렌더링될 때마다 불필요한 연산이 반복되기 때문에 성능 낭비가 발생합니다.


2️⃣ useState 내부 동작 원리

리액트의 useState는 내부적으로 초기 상태를 설정할 때 전달된 값이 함수인지 확인하는 로직이 포함되어 있습니다.

function useState(initialState) {
  let state;
  
  // 🟢 전달된 값이 함수라면 실행하여 초기 상태 설정
  if (typeof initialState === "function") {
    state = initialState();
  } else {
    state = initialState;
  }

  function setState(newState) {
    state = newState;
    render(); // 컴포넌트 리렌더링
  }

  return [state, setState];
}
  • useState초기 상태를 함수로 전달받으면, 초기 렌더링 시에만 해당 함수를 실행합니다.

  • 이후 setState를 호출해 상태를 변경해도 초기화 함수는 다시 실행되지 않습니다.

  • 다음은 리액트가 상태 정보을 배열에 저장하여 기억하는 원리가 포함된 코드입니다.

let stateStore = []; // 🔥 컴포넌트 바깥에 있어서 렌더링과 관계없이 유지됨
let stateIndex = 0;  // 🔥 컴포넌트 바깥에 있지만, 함수 내부에서 매번 0으로 초기화되도록 설계됨

function useState(initialState) {
  if (stateStore[stateIndex] === undefined) {
    stateStore[stateIndex] = typeof initialState === "function"
      ? initialState() // 🟢 함수라면 한 번만 실행
      : initialState;
  }

  let currentIndex = stateIndex; 

  function setState(newState) {
    stateStore[currentIndex] = newState; 
    render(); // 리렌더링 트리거
  }

  return [stateStore[stateIndex++], setState];
}
  • 다음은 useState 호출시 stateStore 배열에 저장되는 모습과 매번 초기화 되도록 설계된 stateIndex를 간단하게 설명하는 코드입니다.
stateIndex = 0;

useState(0);      // stateStore[0] = 0
useState("hello"); // stateStore[1] = "hello"

stateIndex === 2 (컴포넌트 실행 끝)

3️⃣ 지연 초기화 적용 방법

✅ 올바른 예: useState(veryHeavyComputation)

function App() {
  const [id, setId] = useState(veryHeavyComputation); // ✅ 지연 초기화 적용
  return <div>{id}</div>;
}

위처럼 useState함수를 직접 전달하면 useState 내부에서 함수인지 확인 후 한 번만 실행되므로 불필요한 연산을 방지할 수 있습니다.

✅ useState(() => veryHeavyComputation()) 형태도 가능

const [id, setId] = useState(() => veryHeavyComputation());

이렇게 작성해도 동일하게 동작합니다. 함수가 실행되지 않고 useState 내부에서만 실행되도록 하는 역할을 합니다.


4️⃣ 최종 정리 ✨

초기값 설정 방식내부 동작성능 문제
useState(veryHeavyComputation())veryHeavyComputation()이 즉시 실행됨❌ 렌더링마다 실행됨 (비효율적)
useState(veryHeavyComputation)함수인지 체크 후, 한 번만 실행✅ 최적화됨
useState(() => veryHeavyComputation())동일하게 한 번만 실행✅ 최적화됨

📌 결론

  • 무거운 연산이 포함된 초기 상태는 useState(함수) 형태로 사용해야 합니다!
  • 이 방법을 지연 초기화(Lazy Initialization)라고 합니다.
  • 렌더링 성능 최적화를 위해 반드시 기억해 두세요! 🚀

0개의 댓글