React 19 공식문서 딥다이브 - 2편 리렌더링 제어와 외부 스토어

이가 은·2026년 2월 15일

react

목록 보기
9/9

React 19 공식 문서를 기반으로 진행한 몇 달 간의 스터디 내용을 시리즈로 정리하고자 한다.

이 시리즈는 hooks, 컴포넌트, API처럼 특정 기능 단위로 정리하기보다는,
React가 어떤 시점에 무엇을 실행하는지, 그리고 그 실행 시점과 동작 흐름 기준으로 내용을 묶는다.

공식 문서를 단순히 요약하는 것이 아니라,
각 개념을 읽으며 자연스럽게 생기는 “왜 이렇게 동작할까?”라는 질문을 출발로 React 전체를 이해하는 것을 목표로 한다.

따라서 공식 문서만으로 충분히 학습할 수 있는 기본적인 사용법은 다루지 않는다.


memo

memo의 두 번째 인자: arePropsEqual (optional)

기본 memo는 props를 Object.is()로 비교한다(shallow comparison).
따라서 배열이나 객체처럼 참조 타입을 props로 받으면, 내용이 같더라도 참조가 바뀌면 리렌더링이 발생한다.

위 예제에서 setDataPoints(prev => [...prev])는 스프레드 연산자로 새 배열을 생성한다. 데이터는 [{x:1, y:1}]로 동일하지만 참조가 달라지므로 리렌더링이 발생된다.

이때 두 번째 인자로 커스텀 비교 함수를 전달하면, 참조 대신 실제 값(x, y)을 비교하여 불필요한 리렌더링을 방지할 수 있다.

실무 예시: react-query와 background refetch

react-query에서 staleTime: 0으로 설정하면 컴포넌트 마운트나 윈도우 포커스 시 background refetch가 빈번하게 발생한다.

이때 서버 응답 값이 이전과 동일해도, react-query는 새 배열/객체 참조를 생성한다.
Child 컴포넌트가 memo로 감싸져 있어도 기본 shallow comparison으로는 리렌더링을 막을 수 없다.

이런 경우 arePropsEqual로 실제 값을 비교하거나, react-query의 structuralSharing 옵션을 활용할 수 있다.


useCallback

현재는 React가 캐시를 거의 삭제하지 않지만, 향후 가상화 목록 지원이 추가되면 뷰포트 밖으로 스크롤된 항목의 캐시는 삭제하는 방식으로 최적화될 수 있다.
useCallback의 캐싱 동작이 개발자 예상과 다르게 작동할 수 있다면, ref나 state를 사용하는 것이 더 적절할 수 있다.

컴포넌트가 다른 컴포넌트를 시각적으로 감싸고 있다면 JSX를 자식으로 받게 하세요. 감싸는 컴포넌트가 자신의 State를 업데이트하면, React는 자식들은 리렌더링할 필요가 없다는 것을 알게 됩니다.

jsx 기준으로 확인해보면 리렌더링을 예방할 수 있는데, AS-IS는 child를 객체 내부에 전달하고 TO-BE는 컴포넌트 자체로 전달하여 object.is 비교했을때 원본 비교하여 리렌더링 이슈를 해결할 수 있다.


useImperativeHandle

React는 부모 → 자식 단방향 데이터 흐름을 기반으로 상태 변화를 예측 가능하게 만든다.
useImperativeHandle은 이 흐름을 역전시키므로 되도록 피하는 것이 좋다.
참고로 props로 setState를 내려주는 것은 여전히 부모 → 자식 방향이므로 단방향 원칙을 깨지 않는다.


useSyncExternalStore

Transition(non-blocking) 업데이트 중에 외부 스토어가 변경되면, React는 DOM 적용 직전에 getSnapshot을 한 번 더 호출한다.
이때 값이 달라졌다면 업데이트를 처음부터 blocking 방식으로 다시 수행하여, 모든 컴포넌트가 동일한 스토어 버전을 반영하도록 보장한다.

useSyncExternalStore가 반환한 값을 기반으로 Suspense를 트리거하는 것 (lazy 컴포넌트 조건부 렌더링, use()로 Promise 호출 등)은 권장하지 않는다.
외부 스토어 변경은 Transition으로 처리할 수 없어서, 스토어가 바뀔 때마다 무조건 Suspense fallback 내부 컴포넌트가 표시되기 때문이다.

profile
독서와 개발을 좋아하는 프론트 개발자 🍀

0개의 댓글