React Developer Tools 사용법
React Developer Tools의 Components와 Profiler로 상태 전달과 렌더링 비용을 측정해 성능 병목의 근거를 찾습니다.
10장에서는 React 애플리케이션의 성능 최적화 기초를 다룹니다. 성능 최적화에서 가장 먼저 필요한 능력은 성능 문제를 정확히 측정하고 원인을 가려내는 것입니다. 감으로 최적화를 시작하면 코드의 복잡도만 늘고 실제 병목은 그대로 남는 경우가 많습니다. 이 글에서는 성능 분석의 출발점인 React Developer Tools를 설치하고, Components 탭에서 상태의 흐름을 확인한 뒤 Profiler 탭으로 비용을 측정하고, 고친 뒤에 같은 시나리오를 다시 측정하는 기본 순서를 정리합니다.
DEVTOOLS / DIAGNOSIS LOOP
값을 관찰하고 같은 조건으로 다시 측정한다
Components는 props·state·Hook과 소유 관계를 보여준다. Profiler로 렌더링 비용과 갱신 범위를 기록한 뒤, 원인 하나를 바꾸고 같은 동작을 재측정한다.
01 / REPRODUCE
같은 사용자 동작
검색어·클릭 횟수·데이터·브라우저·빌드를 고정한다. 전후 기록의 조건이 같아야 비교할 수 있다.
02 / COMPONENTS
값의 출발점을 찾기
문제 컴포넌트의 props, state, Hook과 owner를 확인한다. 높은 곳에 둔 상태나 매번 새로 만드는 참조를 역추적한다.
03 / PROFILER
실제로 렌더된 범위 기록
Record → 고정 동작 재현 → Stop. 느린 commit에서 어떤 컴포넌트가 렌더됐고 얼마나 걸렸는지 기록한다.
04 / CHANGE + VERIFY
원인 하나, 변경 하나
가설을 정하고 한 가지를 바꾼다. 같은 동작을 다시 기록해 비용·갱신 범위와 기능 회귀를 확인하고 진단 루프로 돌아간다.
Profiler의 렌더링 시간은 전체 사용자 대기 시간과 다르다. 개발 모드·프로파일링의 추가 비용을 고려하고, 운영 조건의 측정은 profiling을 켠 production build로 수행한다.
위 그림은 값을 관찰하고 같은 조건으로 다시 측정하는 진단 루프를 보여 줍니다. 그림의 요약처럼 Components는 props와 state, Hook, 그리고 어느 컴포넌트가 그 값을 가지는지를 보여 주고, Profiler로 렌더링 비용과 갱신 범위를 기록한 뒤 원인 하나를 바꾸어 같은 동작을 다시 측정합니다. 순서는 시나리오 고정, Components에서 값과 소유 관계 확인, Profiler에서 기록과 재현, 종료, 어떤 값이 바뀌었는지에 대한 원인 가설, 구조나 참조 캐시 가운데 한 가지의 변경, 그리고 렌더 비용과 기능을 같은 조건으로 다시 측정하는 여섯 단계이며, 마지막 단계는 다시 고정한 시나리오로 돌아갑니다.
그림 아래의 네 칸은 단계마다 할 일을 덧붙입니다. 검색어와 클릭 횟수, 데이터, 브라우저, 빌드를 고정해야 전후의 기록을 비교할 수 있고, Components에서는 문제 컴포넌트의 props와 state, Hook, owner를 확인해 높은 곳에 둔 상태나 매번 새로 만드는 참조를 거슬러 올라갑니다. Profiler에서는 Record, 고정 동작의 재현, Stop의 순서로 느린 커밋에서 어떤 컴포넌트가 렌더되었고 얼마나 걸렸는지 기록하며, 가설을 정해 한 가지만 바꾼 뒤 같은 동작을 다시 기록해 비용과 갱신 범위, 기능의 회귀를 함께 확인합니다. 그림의 마지막 설명처럼 Profiler의 렌더링 시간은 사용자가 기다리는 전체 시간과 다르며, 개발 모드와 프로파일링 자체의 추가 비용을 고려해 운영 조건의 측정은 프로파일링을 켠 프로덕션 빌드로 합니다.
React Developer Tools란?
React Developer Tools는 React 팀이 제공하는 공식 브라우저 확장 도구로, React 애플리케이션의 컴포넌트 트리와 state·props의 변화, 렌더링 비용을 눈으로 확인할 수 있게 해 줍니다. 이 도구로는 실제로 렌더링된 기준의 컴포넌트 계층 구조를 따라갈 수 있고, 실행 중인 시점의 props, state, Hook 값을 확인할 수 있으며, 어떤 컴포넌트가 얼마나 자주, 얼마나 오래 렌더링되는지 측정할 수 있습니다.
설치와 활성화 확인
Chrome 웹 스토어나 Firefox, Edge의 확장 스토어에서 React Developer Tools를 설치합니다(React 문서의 React Developer Tools). 그다음 React 앱을 실행하고 브라우저의 개발자 도구(F12)를 열어, 위쪽 탭에 Components와 Profiler가 보이면 정상적으로 설치된 것입니다. 설치를 마쳤더라도 React를 쓰지 않는 페이지에서는 이 탭이 보이지 않거나 비활성화될 수 있으므로, 반드시 React 앱이 실행 중인 페이지에서 확인하세요.
Components 탭으로 상태 흐름 확인
Components 탭은 지금 화면을 이루는 React 컴포넌트 트리를 보여 주며, 여기서 얻는 정보는 구조를 확인하는 데서 그치지 않고 디버깅의 출발점이 됩니다. 먼저 왼쪽 트리에서 컴포넌트를 골라 렌더링 계층과 부모·자식 관계를 확인하고, 오른쪽 패널에서 props, state, Hook의 값을 봅니다. 예상과 다른 값이 있다면 그 값을 넘긴 상위 컴포넌트부터 거슬러 올라갑니다.
실무에서 자주 보는 문제도 이 탭에서 찾을 수 있습니다. 중간 컴포넌트가 쓰지 않는 props를 계속 넘기기만 하는 props 드릴링, 전역에 둘 필요가 없는 상태가 너무 위에 있어 리렌더링 범위가 커지는 상태 위치의 문제, 객체나 함수 props가 렌더링할 때마다 새로 만들어지는 참조의 재생성이 그 예입니다. 도구의 설정에서 “Highlight updates when components render.”를 켜면, 렌더링된 컴포넌트가 화면에서 잠깐 강조되어 갱신 범위를 대략 짐작할 수도 있습니다.
Profiler 탭으로 렌더링 비용 측정
Profiler 탭은 어떤 상호작용에서 렌더링 비용이 생겼는지를 시간 기준으로 보여 주며, 성능 최적화는 이 측정 결과를 기준으로 진행해야 합니다. Profiler 탭에서 Record를 눌러 기록을 시작하고, 입력이나 필터, 탭 전환, 모달 열기 같은 실제 사용자 동작을 재현한 뒤, Stop을 눌러 기록을 끝내고 커밋(Commit) 목록을 확인합니다.
결과를 읽을 때는 세 가지를 봅니다. 커밋마다의 렌더링 시간은 그 커밋에 참여한 컴포넌트의 React 렌더링 비용이며, 네트워크나 브라우저의 레이아웃·페인트를 포함한 사용자의 전체 대기 시간과는 구분합니다. 렌더링 횟수로는 상호작용에 비해 지나치게 자주 렌더링되는 컴포넌트가 있는지 보고, 부모의 상태 변경이 넓은 하위 트리까지 함께 리렌더링하는지도 확인합니다.
흔한 오진을 막으려면 개발 모드의 측정 결과만으로 결론을 내리지 않습니다. 개발 모드에는 검사용 추가 비용이 있어 실제 프로덕션과 차이가 크며, 프로덕션에서 React 프로파일링을 하려면 프로파일링을 지원하는 빌드가 필요하고, 상호작용 전체의 지연은 브라우저의 Performance 도구로 함께 확인합니다(React 문서의 Profiler). 한 번의 측정으로 확정하지 말고 같은 시나리오를 두세 번 되풀이해 평균적인 경향을 보며, 최적화를 적용하기 전과 후를 같은 시나리오로 비교해 효과를 수치로 확인합니다. 코드에서 쓰는 <Profiler> 컴포넌트와 확장 도구의 Profiler 탭은 이름만 같을 뿐 서로 다른 사용 방법입니다.
더 깊이: 브라우저 Performance 패널의 React 트랙
React 19.2부터는 브라우저 개발자 도구의 Performance 패널 타임라인에 React 전용 트랙이 함께 나타납니다(React 문서의 React Performance tracks). Scheduler 트랙은 작업을 우선순위에 따라 Blocking, Transition, Suspense, Idle의 네 하위 트랙으로 나누고, 렌더 한 번을 그 원인인 Update와 컴포넌트를 호출하는 Render, DOM에 반영하고 레이아웃 Effect를 실행하는 Commit, useEffect를 실행하는 Remaining Effects 단계로 나눠 보여 줍니다. Components 트랙은 컴포넌트의 렌더 시간을 플레임 그래프로 그리며, 개발 빌드에서는 항목을 눌러 바뀐 props를 볼 수 있어 불필요한 렌더를 찾는 데 쓸 수 있습니다. 그래서 React의 렌더 비용과 네트워크, 레이아웃, 페인트를 같은 시간축에서 나란히 비교할 수 있습니다.
이 트랙도 개발 빌드와 프로파일링 빌드에서만 나타납니다. 개발 빌드에서는 기본으로 켜지지만, 프로파일링 빌드에서는 Scheduler 트랙만 기본으로 켜지고 Components 트랙에는 <Profiler>로 감싼 하위 트리의 컴포넌트만 나오며, React Developer Tools 확장이 켜져 있으면 모든 컴포넌트가 나옵니다. 계측에 드는 추가 비용 때문에 프로덕션 빌드에서는 기본으로 꺼져 있습니다. 또 Components 트랙은 화면이 복잡해지지 않도록 0.05ms 이상 걸렸거나 갱신을 일으킨 Effect만 보여 줍니다.
성능 분석 루틴 체크리스트
분석할 때는 문제 동작을 재현할 수 있는 시나리오로 고정했는지, Profiler로 병목 컴포넌트와 커밋의 비용을 먼저 확인했는지, 최적화 대상이 실제 병목인지 수치로 검증했는지, 최적화한 뒤 같은 시나리오를 다시 측정해 개선되었는지 확인했는지를 차례로 점검합니다. 처음의 진단 루프처럼 가설 하나를 고치고, 같은 조건에서 비용과 기능을 다시 확인합니다.
React Developer Tools를 쓸 때는 Components 탭과 Profiler 탭의 역할을 섞지 않는 것이 중요합니다. 값이 어디서 왔는지는 Components에서 좁히고 렌더링 비용은 Profiler에서 확인해야, 원인과 증상을 나누어 볼 수 있습니다.
작은 앱으로 연습하기
앞서 만든 React 프로젝트의 App.jsx를 다음 예제로 바꾸세요. 검색 상태와 카운터 상태는 App이 가지고, ResultList는 목록을 props로 받습니다. 성능 차이를 일부러 크게 만들려는 예제가 아니라, 어떤 값이 바뀌었고 무엇을 기록하는지 익히는 작은 앱입니다.
import { useState } from 'react';
const products = [
{ id: 1, name: 'Notebook' },
{ id: 2, name: 'Pen' },
];
function ResultList({ items }) {
return <ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>;
}
export default function App() {
const [query, setQuery] = useState('');
const [count, setCount] = useState(0);
const visible = products.filter(item =>
item.name.toLowerCase().includes(query.toLowerCase())
);
return (
<main>
<label>검색어 <input value={query} onChange={event => setQuery(event.target.value)} /></label>
<button onClick={() => setCount(value => value + 1)}>횟수: {count}</button>
<ResultList items={visible} />
</main>
);
}첫 화면에는 횟수 0과 두 상품이 보입니다. 검색어에 note를 입력하면 Notebook만 남고, 버튼을 한 번 누르면 횟수만 1이 됩니다. 이때 App의 query는 'note', count는 1이고, ResultList의 items에는 id 1인 상품이 들어 있어야 합니다. Components에서 App의 Hook 값과 ResultList의 props를 나란히 확인하세요. 값을 읽는 동안 도구로 상태를 직접 편집하지 않아야 같은 사용자 동작을 다시 재현할 수 있습니다.
이제 검색어를 비우고 카운터를 0으로 되돌리려면 페이지를 새로고침합니다. Profiler 기록을 시작한 뒤 버튼만 세 번 누르고 멈춥니다. 먼저 검색 결과가 계속 두 상품이고 카운터가 3인지 확인한 다음, 기록된 커밋과 렌더링된 범위를 살펴보세요. 클릭 수와 커밋 수가 무조건 같다고 가정하거나, 이 작은 예제의 시간 차이를 곧바로 병목이라고 단정하지 않습니다. 개발 모드인지, React Compiler를 쓰는지, 도구의 설정은 어떤지도 전후 기록에 함께 남깁니다.
정리
- 성능 최적화는 시나리오를 고정하고, 값을 관찰하고, 비용을 측정하고, 한 가지만 바꾸어 같은 조건으로 다시 측정하는 루프로 진행합니다.
Components탭은 props와 state, Hook의 값과 그 값을 가진 컴포넌트를 보여 주어 값의 출처를 좁힙니다.Profiler탭은 상호작용마다의 커밋과 렌더링 비용, 렌더링된 범위를 기록합니다.Profiler의 렌더링 시간은 사용자의 전체 대기 시간과 다르므로, 전체 지연은 브라우저의 Performance 도구로 따로 확인합니다.- 개발 모드의 측정만으로 결론을 내리지 않고, 같은 시나리오를 여러 번 되풀이해 전후를 비교합니다.
연습 문제
1. 측정에서 바로 결론을 내릴 수 있을까
다음 숫자는 이 예제를 실제로 실행해 얻은 결과가 아니라 해석을 연습하려는 가정입니다. 같은 빌드와 데이터, 동작에서 React 렌더링 비용이 세 번 각각 12, 14, 10ms였고, 변경한 뒤에는 3, 4, 5ms였습니다. 두 평균과 줄어든 렌더링 비용을 계산해 보세요. 따로 측정한 상호작용 전체의 시간이 전후 모두 200ms였다면, 사용자의 대기 시간도 같은 만큼 줄었다고 말할 수 있을까요?
해설 보기
렌더링 비용의 평균은 변경 전 (12 + 14 + 10) ÷ 3 = 12ms, 변경 뒤 (3 + 4 + 5) ÷ 3 = 4ms로 8ms 줄었습니다. 그러나 상호작용 전체는 여전히 200ms이므로, 사용자의 전체 대기 시간이 8ms 줄었다는 결론은 이 자료에서 나오지 않습니다.
네트워크와 레이아웃, 페인트 같은 다른 구간과 측정 오차는 브라우저의 Performance 도구에서 따로 조사해야 합니다. 렌더링 구간의 개선과 최종적으로 느끼는 개선은 서로 다른 측정입니다.
2. 도구를 나누어 쓰기
페이지를 새로고침한 뒤 검색어에 pen을 입력하고 버튼을 두 번 누릅니다. App의 두 상태와 ResultList가 받는 상품을 예상하고, Components와 Profiler 가운데 어느 도구로 각각을 확인할지 적어 보세요.
해설 보기
query는 'pen', count는 2이고, 'notebook'에는 pen이 들어 있지 않으므로 목록에는 id 2인 Pen만 남습니다. 실제 state와 props는 Components에서 확인하고, 그 입력과 클릭 동안 렌더링된 범위와 비용은 따로 시작한 Profiler 기록에서 확인합니다.
기능이 맞는지 먼저 검증한 다음 비용을 비교해야, 상품을 잘못 숨겨서 얻은 짧은 기록을 최적화의 성공으로 오해하지 않습니다.
다음 글 불필요한 리렌더링 이해와 방지에서는 이렇게 진단한 결과를 바탕으로, 불필요한 리렌더링이 생기는 대표적인 패턴과 그것을 막는 방법을 구체적으로 다룹니다.