[IT 심층 분석]
React Profiler로 밝혀낸 불필요한 리렌더링의 주범과 memo, useCallback 올바른 사용 전략
김민준 · IT 시스템 엔지니어|
사용자 목록과 필터링 기능이 복합적으로 엮인 관리자 대시보드 페이지에서 인터랙션이 눈에 띄게 느린 현상이 보고되었습니다. 필터 입력창에 글자를 하나 입력할 때마다 UI 업데이트가 버벅이는 심각한 사용성 문제였습니다. React DevTools의 Profiler 탭을 열고 실제로 렌더링이 어떻게 발생하는지 녹화해보았을 때 눈앞의 광경은 당혹스러웠습니다. 필터창에 글자 하나를 칠 때마다 무관해 보이는 목록 아이템 카드 컴포넌트 수백 개가 전부 빨갛게 리렌더링되고 있었습니다.
문제의 원인은 React의 리렌더링 동작 방식에 대한 잘못된 이해에 있었습니다. 부모 컴포넌트의 상태가 변경되면 기본적으로 자식 컴포넌트들도 모두 리렌더링됩니다. 필터 검색어를 관리하는 state가 상위 컨테이너에 위치해 있었고 이 state가 변경될 때마다 자식으로 전달되는 onCardClick 콜백 함수의 레퍼런스가 매번 새롭게 생성되고 있었습니다. 자식 컴포넌트들은 props로 전달된 함수의 레퍼런스가 달라졌으니 다시 렌더링해야 한다고 판단했습니다. 실제 동작은 동일하더라도 자바스크립트에서 함수는 매번 새로운 객체로 생성되기 때문에 얕은 비교에서 항상 다른 참조로 취급됩니다.
해결은 두 가지 훅의 올바른 적용이었습니다. 부모에서 자식으로 내려보내는 콜백 함수들을 useCallback으로 감싸 의존성 배열에 명시한 값이 변경되지 않는 한 동일한 함수 레퍼런스를 재사용하도록 했습니다. 자식 카드 컴포넌트 자체는 React.memo로 감싸 props가 실제로 변경된 경우에만 리렌더링하도록 메모이제이션 처리를 했습니다. 단순히 memo만 쓰거나 useCallback만 쓰면 효과가 없고 두 가지를 세트로 사용해야 실질적인 리렌더링 방지 효과가 납니다. 최적화 적용 후 Profiler에서 필터 입력 시 목록 카드들의 빨간 불이 완전히 꺼졌고 인터랙션 지연은 체감상 완전히 사라졌습니다. 최적화는 항상 프로파일링으로 병목을 먼저 확인한 후 적용하는 해결 수순이 중요합니다.