React Native Performance: 내가 먼저 측정하고 고치는 것
Source: Dev.to

What I Measure
-
Startup time – 탭을 누른 순간부터 첫 의미 있는 페인트(예: 홈 화면이 보이는 시점)까지 걸리는 시간.
Tools: Flipper, React Native 내장 성능 도구, 혹은 네이티브 코드에 간단한 타임스탬프를 삽입. 새로운 라이브러리, 초기 데이터 증가 등 변경 전후를 비교해 회귀를 방지합니다. -
Frame rate (FPS) – 특히 스크롤 및 애니메이션 시.
Tools: React Native의 “Show Perf Monitor” 또는 Flipper(JS와 UI 스레드 FPS 표시). 60 FPS(또는 성능이 좋은 기기에서는 120 FPS) 이하로 떨어지면 끊김이 발생합니다. 어느 화면이나 리스트에서 문제가 발생했는지 기록합니다. -
List scroll – 긴 리스트는 흔한 병목 구간입니다.
Check:FlatList(또는 유사 컴포넌트)를 사용하고 있는가?- 한 행에 너무 많은 아이템이나 무거운 컴포넌트를 렌더링하고 있지는 않은가?
- 스크롤 중에 JS 스레드에서 작업을 수행하고 있지는 않은가?
-
Memory – 메모리 누수(시간이 지남에 따라 메모리 증가)와 큰 할당량을 감시합니다.
Tools: Flipper 또는 Xcode/Android Studio 프로파일러. 누수는 주로 이미지, 캐시, 정리되지 않은 리스너에서 발생합니다. -
Bundle size – 큰 JS 번들은 시작 시간을 늦춥니다.
npx react-native bundle --dev false명령을 실행하고 출력 파일 크기를 확인합니다. lazy‑load가 가능하거나 교체할 수 있는 무거운 의존성을 찾아봅니다.
Baseline per release
릴리즈마다 작은 지표 집합(예: 시작 시간, 메인 피드 리스트 FPS, 5 분 후 메모리)을 추적합니다. 이를 문서나 CI 파이프라인에 저장하고, 새로운 기능이 들어올 때마다 기준과 비교해 회귀가 명확히 드러나게 합니다.
Fixes I Reach For First
Lists
// Example: enabling getItemLayout for fixed‑height items
const getItemLayout = (data, index) => ({
length: ITEM_HEIGHT,
offset: ITEM_HEIGHT * index,
index,
});
FlatList(또는 처리량이 높은FlashList)를 사용합니다.- 아이템 높이가 고정돼 있다면
getItemLayout을 구현합니다. windowSize와maxToRenderPerBatch를 조정해 트리 안에 들어가는 아이템 수를 제한합니다.- 리스트 아이템 컴포넌트를 메모이제이션해 스크롤 시마다 재렌더링되지 않게 합니다.
renderItem안에서 인라인 함수와 객체 리터럴을 피하고, 안정적인 props를 전달합니다.
Images
resizeMode를 적절히 사용합니다.- 백엔드 또는 이미지 리사이징 CDN에서 올바른 크기의 이미지를 제공하도록 합니다.
- 캐시 성능이 좋은
react-native-fast-image사용을 고려합니다. - 리스트에서 화면에 보이지 않는 이미지는 lazy‑load합니다.
Re‑renders
const MemoizedComponent = React.memo(MyComponent);
const stableCallback = useCallback(() => { /* … */ }, []);
const stableValue = useMemo(() => computeExpensiveValue(), []);
- React DevTools Profiler 또는 “Highlight updates”를 이용해 불필요한 재렌더링을 탐지합니다.
- 상태를 끌어올려 필요한 부분만 재렌더링되도록 합니다.
- 렌더링 중이나 너무 자주 실행되는 effect 안에서 state를 설정하는 것을 피합니다.
JS thread
- 무거운 작업을 JS 스레드에서 분리합니다: 네이티브 모듈을 사용하거나
InteractionManager.runAfterInteractions로 작업을 배치합니다. - 시작 시나 스크롤 중에 메인 JS 스레드에서 큰 동기 작업을 수행하지 않도록 합니다.
Startup
- 초기 JS 번들 크기를 줄입니다: 화면을 lazy‑load(
React.lazy+ 코드 스플리팅 지원 시)하고, 비핵심 import를 연기하며, 의존성을 정리합니다. - 아직 사용하지 않았다면 Hermes를 활성화합니다. 일반적으로 시작 시간과 메모리 사용량이 개선됩니다.
Cleanup
- 리스너 구독을 해제하고, 타이머를 정리하며,
useEffect정리 단계에서 요청을 취소합니다. - 이벤트 리스너와 연결을 닫아 메모리 누수와 불필요한 백그라운드 작업을 방지합니다.
한 번에 하나의 영역을 고치고, 다시 측정하고, 반복합니다. 작지만 검증된 개선이 누적돼 큰 효과를 냅니다; 측정 없이 무작위 “최적화”는 대개 효과가 없습니다.
Saad Mehmood — Portfolio