본문으로 건너뛰기

버그 제보

이상한 점을 알려 주시면 고칠게요. 지금 보고 있는 페이지와 브라우저 정보가 함께 담겨요.

페이지:

Frontend

리렌더링 원인과 추적

작성 2026. 10. 13.추가
목차6개 항목
출처와 참고

「프론트엔드 면접 준비: React」 정리로 리렌더링 조건을 보강했다.

React 컴포넌트는 state가 바뀌거나, 부모가 렌더링되거나, 구독한 Context·외부 스토어 값이 바뀌면 다시 렌더링된다. 리렌더링 자체는 나쁜 것이 아니지만, 값이 그대로인데 여러 번 다시 그려진다면 원인을 찾아 줄일 수 있다. React.memo를 여기저기 씌우기보다 렌더링을 일으키는 원인을 먼저 찾는다. 메모이제이션 API 자체는 Hooks의 메모이제이션 절 참고.

무엇이 리렌더링을 일으키나

일어난 일리렌더링비고
자기 state 갱신된다새 값이 이전 값과 Object.is로 같으면 건너뛴다(bail out). 확인하려고 컴포넌트를 한 번 더 호출할 수는 있지만 자식으로 내려가지 않는다. 객체를 직접 고쳐 같은 참조를 넘기면 바뀐 것이 보이지 않는다(State와 useState)
부모 리렌더링된다props가 그대로여도 다시 호출된다. 「props가 바뀌어서」 다시 그려지는 것도 결국 부모가 렌더링했기 때문이다. memo가 막는 것이 이 경우다
구독한 Context 값 변경된다Provider value의 참조가 바뀌면 그 Context를 읽는 컴포넌트 전부. memo로 막지 못한다(Context)
외부 스토어 변경된다useSyncExternalStore나 useSelector가 고른 값이 바뀔 때
ref.current 변경안 된다렌더링과 상관없는 값을 담는 상자다(Hooks의 useRef 절)
일반 변수·props 객체를 직접 수정안 된다React가 바뀐 줄 알 방법이 없다
클래스 컴포넌트의 forceUpdate()된다비교 없이 강제로 다시 그린다. 함수 컴포넌트에는 대응 API가 없다

React.memo가 효과 없는 경우

React.memo는 props가 같을 때만 렌더링을 건너뛴다. 아래 경우에는 감싸도 렌더링 횟수가 줄지 않는다.

원인설명해결
부모가 새 객체·배열·함수를 props로 넘김부모가 렌더링될 때마다 참조가 바뀌어 props가 달라진 것으로 판단된다useMemo/useCallback으로 참조 고정
컴포넌트 자신의 state·Context가 바뀜memo는 props 비교만 하므로 내부 변화는 막지 못한다state를 쓰는 쪽으로 내리거나 Context 분리
렌더링의 출발점이 부모 쪽 데이터 흐름자식만 감싸면 부모가 계속 렌더링되고, 계층 전체를 감싸도 같다어느 층에서 렌더링이 시작되는지 찾아 그 층을 고친다

자식 컴포넌트가 여러 번 렌더링될 때는 자식이 아니라 그 위 컴포넌트가 이미 여러 번 렌더링되고 있는 것일 수 있다. 자식은 결과일 뿐이다.

useQuery가 최소 두 번 렌더링하는 이유

react-query(TanStack Query)의 useQuery를 쓰는 컴포넌트는 최소 두 번 렌더링된다.

  1. 데이터를 받기 전(로딩 상태)에 한 번
  2. 데이터를 받은 뒤 한 번 더

isFetching처럼 요청 상태가 바뀔 때마다 렌더링이 추가로 생길 수도 있다. 따라서 useQuery를 쓰는 부모 아래의 자식은 여러 번 렌더링될 수 있다. 이런 동작을 알고 있어야 어디에 로그를 찍을지도 판단할 수 있다.

참고: TanStack Query는 기본적으로 컴포넌트가 실제로 읽은 속성이 바뀔 때만 다시 렌더링하도록 최적화되어 있다(tracked queries). 그래도 로딩→성공 전환에 따른 렌더링은 피할 수 없다.

추적 방법

  • 계층별 console.log: 부모부터 자식까지 렌더링 시점을 찍어 보면 어느 층에서 렌더링이 시작되는지 보인다. 로그 순서상 자식이 부모보다 먼저 끝나는 것처럼 보일 수 있는데, 렌더 완료·커밋이 자식부터 이루어지기 때문이다.
  • React DevTools → "Highlight updates when components render": 렌더링될 때마다 해당 컴포넌트에 테두리가 깜빡여 눈으로 바로 추적할 수 있다.
  • React DevTools Profiler: 각 렌더링이 왜 일어났는지(props 변경, state 변경, 부모 렌더링)와 소요 시간을 기록해 준다. "Record why each component rendered" 옵션을 켜 둔다. 컴포넌트 단위가 아니라 메인 스레드 전체 비용(스크립트·레이아웃·페인트)은 Chrome DevTools의 Performance 패널로 본다(DevTools로 성능·메모리 분석하기).

줄이는 순서

  1. 렌더링을 촉발하는 원인을 찾는다: 자기 state 변경인가, 부모 렌더링인가, Context인가, 데이터 패칭 상태인가.
  2. 원인을 제거한다: state를 실제로 쓰는 컴포넌트 가까이로 내리기, 부모에서 바뀌는 부분과 안 바뀌는 부분을 컴포넌트로 분리하기, 필요 없는 state 제거하기.
  3. 그래도 남는 경우에만 React.memo + useCallback/useMemo로 props 참조를 고정한다. React Compiler를 켠 프로젝트에서는 이 단계의 메모이제이션을 컴파일러가 자동으로 넣으므로, 손으로 고정할 곳은 컴파일러가 건너뛴 컴포넌트 정도다.
  4. 측정한다. 렌더링 몇 번이 실제로 느린지 Profiler로 확인하고, 느리지 않다면 굳이 최적화하지 않는다.

핵심 정리

  • 리렌더링 원인: state, 부모 렌더링, Context·외부 스토어 변경.
  • React.memo는 props가 같을 때만 효과가 있다. 원인을 모른 채 씌우면 소용없다.
  • useQuery 같은 데이터 패칭 훅은 로딩→성공 전환만으로도 최소 두 번 렌더링한다.
  • DevTools의 하이라이트와 Profiler로 원인을 찾은 다음 고친다.

관련 노트: INP 최적화: 긴 작업 쪼개기와 양보, 프론트엔드 성능 최적화: 언제, 무엇을

원본 기록2

댓글

GitHub 계정으로 댓글을 남길 수 있어요.