React Native Performance: 내가 먼저 측정하고 고치는 것

발행: (2026년 2월 17일 오후 06:39 GMT+9)
6 분 소요
원문: Dev.to

Source: Dev.to

Cover image for React Native Performance: What I Measure and Fix First

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을 구현합니다.
  • windowSizemaxToRenderPerBatch를 조정해 트리 안에 들어가는 아이템 수를 제한합니다.
  • 리스트 아이템 컴포넌트를 메모이제이션해 스크롤 시마다 재렌더링되지 않게 합니다.
  • 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

0 조회
Back to Blog

관련 글

더 보기 »

TanStack Virtual을 사용한 가상화

React에서 가상화를 활용한 성능 최적화 성능 최적화는 React 커뮤니티에서 흔히 다루는 주제입니다. 사용자들은 부드러운 스크롤링과 빠른 …