useState 초기값 계산이 렌더링마다 반복 실행된 이유
문제 발생
초기 상태를 만들 때 무거운 계산(대용량 배열 파싱 등)을 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)(함수를 그대로 전달) — 괄호 하나 차이지만 동작은 완전히 다릅니다.
- 초기값 계산이 단순한 리터럴(
useState(0),useState([]))이라면 굳이 지연 초기화를 쓸 필요가 없습니다 — 함수 호출 자체의 오버헤드가 계산 비용보다 클 수 있습니다. - 초기값 계산이 props나 외부 데이터를 가공하는 등 실제로 비용이 드는 작업일 때만 지연 초기화를 씁니다.
useMemo와 헷갈리기 쉬운데,useMemo는 의존성 배열이 바뀔 때마다 재계산하는 것이고,useState의 지연 초기화는 오직 최초 마운트 시 한 번만 계산합니다 — props가 바뀌어도 상태를 다시 계산하고 싶지 않다면(그게useState가 상태를 다루는 원래 의도입니다) 지연 초기화가 맞고, props가 바뀔 때마다 값을 다시 계산해야 한다면 애초에useState가 아니라useMemo나 파생 값으로 처리하는 게 맞는 경우가 많습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.