본문으로 건너뛰기
개발 머꼬
개발 노트React
hohyeon.dev19

useState 초기값 계산이 렌더링마다 반복 실행된 이유

  • #Engineering Note
  • #Performance
  • #React

문제 발생

초기 상태를 만들 때 무거운 계산(대용량 배열 파싱 등)을 useState에 직접 넣었는데, 그 컴포넌트가 리렌더링될 때마다 계산이 반복 실행되어 성능이 나빠졌습니다.

function Table({ rawData }) {
  const [rows, setRows] = useState(parseAndSort(rawData)); // 매 렌더링마다 호출됨
  // ...
}

원인 분석

useState(initialValue)에서 initialValue로 넘긴 표현식 자체는 함수 컴포넌트의 본문이 실행될 때마다 평가됩니다 — 이건 JavaScript 함수 호출의 기본 동작입니다. useState는 그렇게 계산된 값 중 첫 번째 렌더링에서 계산된 것만 실제 초기 상태로 사용하고 이후 렌더링에서 넘어온 값은 무시하지만, 그 계산 자체(parseAndSort(rawData) 호출)는 매번 실행된 뒤에야 무시됩니다. 즉 "결과는 안 쓰이지만 계산 비용은 매번 낸다"는 낭비가 생깁니다.

해결 방안

useState값 대신 함수를 넘기면, React는 그 함수를 첫 렌더링에서 딱 한 번만 호출합니다 — 이를 지연 초기화(lazy initialization)라고 부릅니다.

function Table({ rawData }) {
  const [rows, setRows] = useState(() => parseAndSort(rawData)); // 최초 1회만 실행
  // ...
}

차이는 parseAndSort(rawData)(즉시 호출) vs () => parseAndSort(rawData)(함수를 그대로 전달) — 괄호 하나 차이지만 동작은 완전히 다릅니다.

  1. 초기값 계산이 단순한 리터럴(useState(0), useState([]))이라면 굳이 지연 초기화를 쓸 필요가 없습니다 — 함수 호출 자체의 오버헤드가 계산 비용보다 클 수 있습니다.
  2. 초기값 계산이 props나 외부 데이터를 가공하는 등 실제로 비용이 드는 작업일 때만 지연 초기화를 씁니다.
  3. useMemo와 헷갈리기 쉬운데, useMemo의존성 배열이 바뀔 때마다 재계산하는 것이고, useState의 지연 초기화는 오직 최초 마운트 시 한 번만 계산합니다 — props가 바뀌어도 상태를 다시 계산하고 싶지 않다면(그게 useState가 상태를 다루는 원래 의도입니다) 지연 초기화가 맞고, props가 바뀔 때마다 값을 다시 계산해야 한다면 애초에 useState가 아니라 useMemo나 파생 값으로 처리하는 게 맞는 경우가 많습니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.