MOBILE

크로스플랫폼 앱 성능 최적화 실전법

junetapa 2026. 2. 18 업데이트 2026. 6. 6 12 min read

크로스플랫폼 앱이 네이티브보다 느리다는 건 사실이지만, 제대로 된 최적화를 거치면 사용자가 체감할 수 없을 정도로 성능 차이를 줄일 수 있다. React Native와 Flutter에서 앱 성능을 극적으로 끌어올린 실전 노하우를 정리했다.

2026년 6월 업데이트

2026년 기준으로 두 진영의 판도가 바뀌었다. React Native는 0.76부터 신아키텍처(Bridgeless·JSI·Fabric·TurboModules)가 기본값이 되어 직렬화 비용이 따르던 구형 브릿지가 사실상 제거됐고, Flutter는 Impeller가 iOS와 안드로이드(API 29+)의 기본 렌더러로 자리잡아 셰이더 컴파일 잭(jank)이 거의 사라졌다. 이 글의 수치와 권장 도구를 2026년 6월 시점에 맞춰 갱신했다.

크로스플랫폼 앱을 개발하다 보면, "네이티브보다 느리다"는 말을 정말 많이 듣게 된다. 솔직히 말하면, 틀린 말은 아닙니다. 하지만 제대로 된 최적화를 거치면 사용자가 체감할 수 없을 정도로 성능 차이를 줄일 수 있다는 것도 사실이에요. 저는 React Native와 Flutter를 활용한 프로젝트를 다수 진행하면서, 실제로 앱 성능을 극적으로 끌어올린 경험이 있다. 오늘은 그 실전 노하우를 가감 없이 공유해 보겠다.

크로스플랫폼 앱 성능 저하의 주요 원인

브릿지 통신과 렌더링 오버헤드

크로스플랫폼 프레임워크의 전통적인 병목은 네이티브 레이어와의 통신 구간이었다. 구형 React Native는 JavaScript 스레드와 네이티브 스레드 사이의 브릿지(Bridge)로 데이터를 주고받으며 직렬화/역직렬화 비용을 치렀고, 리스트를 빠르게 스크롤하거나 복잡한 애니메이션을 구현할 때 프레임 드롭이 두드러졌다. 다만 이 비동기 브릿지는 신아키텍처에서 JSI 직접 호출로 대체되며 사실상 역사 속으로 사라졌다(아래 최적화 전략 참조). Flutter는 처음부터 자체 렌더링 엔진을 써서 브릿지 문제는 적었지만, 위젯 트리가 깊어지면 빌드 비용이 급격히 증가하는 특성은 여전하다.

메모리 관리 실수와 상태 과다 리렌더링

앱 성능 저하의 또 다른 주범은 비효율적인 메모리 관리다. 이미지 캐싱을 제대로 하지 않으면 메모리 사용량이 폭증하고, 특히 저사양 안드로이드 기기에서 OOM(Out of Memory) 크래시가 빈번하게 발생한다. 또한 상태 관리를 잘못 설계하면 하나의 상태 변경이 전체 위젯 트리나 컴포넌트 트리를 다시 렌더링하게 되어 불필요한 연산이 기하급수적으로 늘어납니다. 실제 프로젝트에서 Redux 스토어 구조를 잘못 잡아서 단순 텍스트 입력에도 전체 화면이 리렌더링되던 경험이 있었는데, 이를 수정하자 체감 속도가 완전히 달라졌다.

네이티브 모듈 의존성 문제

크로스플랫폼이라고 해도 카메라, GPS, 블루투스 같은 하드웨어 기능은 결국 네이티브 코드에 의존한다. 서드파티 네이티브 모듈의 품질이 들쭉날쭉하고, 업데이트가 느린 라이브러리를 쓰면 전체 앱 성능에 악영향을 미칩니다. 가능하면 공식 지원 플러그인이나 커뮤니티에서 검증된 패키지를 선택하고, 정말 중요한 기능은 직접 네이티브 모듈을 작성하는 것도 고려해야 한다.

프레임워크별 최적화 핵심 전략

React Native 최적화 실전법

2026년 기준으로 React Native의 신아키텍처는 더 이상 선택이 아니다. 0.76부터 Bridgeless 모드와 JSI, Fabric, TurboModules가 모든 신규 프로젝트의 기본값이 됐고, 직렬화 비용이 따르던 비동기 브릿지가 사실상 사라졌다. 벤치마크상 JS와 네이티브 간 호출 속도는 구 브릿지 대비 30~50% 빨라지고, 시작 시간은 약 25% 단축된다. 리스트는 여전히 FlatList 대신 FlashList가 정석이며, Fabric과 Reanimated 위에서 동작하는 LegendList도 대용량 리스트의 빈 화면 깜빡임을 줄이는 새 선택지로 떠올랐다. 애니메이션은 Reanimated 4의 워클릿(worklet)으로 UI 스레드에서 직접 실행해 JS 스레드 부하 없이 처리한다. 번들은 Metro 트리 셰이킹과 Hermes 바이트코드로 줄이는 것이 표준이다.

React Native — FlashList + memo import { FlashList } from "@shopify/flash-list"; import { memo, useCallback } from "react"; const Row = memo(({ item }) => ( <Text>{item.title}</Text> )); export function Feed({ data }) { const renderItem = useCallback( ({ item }) => <Row item={item} />, [] ); return ( <FlashList data={data} renderItem={renderItem} keyExtractor={(it) => it.id} /> ); }

Flutter 최적화 실전법

Flutter에서는 2026년부터 Impeller가 iOS와 안드로이드(API 29+)의 기본 렌더러다. 셰이더를 빌드 시점에 AOT로 미리 컴파일하기 때문에, 과거 Skia 시절 첫 애니메이션마다 발생하던 셰이더 컴파일 잭이 거의 사라졌다. Vulkan을 지원하지 않는 구형 기기에서는 자동으로 레거시 백엔드로 폴백한다. 그 위에서 개발자가 지켜야 할 원칙은 변하지 않았다. "위젯 트리를 최대한 얕고 가볍게" — const 생성자를 적극 활용하고, 상태가 바뀌는 부분만 별도 위젯으로 분리해 리빌드 범위를 최소화한다. RepaintBoundary로 불필요한 페인팅을 차단하고, ListView.builder로 화면에 보이는 아이템만 렌더링한다. 측정은 반드시 프로파일 모드에서 Dart DevTools의 Performance 탭으로, 빌드·레이아웃·페인트 단계별 소요 시간을 직접 확인한다.

Flutter — RepaintBoundary + const class Feed extends StatelessWidget { const Feed({super.key, required this.items}); final List<Item> items; @override Widget build(BuildContext context) { return ListView.builder( itemCount: items.length, itemBuilder: (context, i) => RepaintBoundary( child: _Row(item: items[i]), ), ); } }

공통 최적화 포인트

프레임워크에 관계없이 적용 가능한 최적화 포인트도 있다. 이미지는 반드시 적절한 크기로 리사이즈하고, WebP 포맷을 사용하면 PNG/JPEG 대비 30~70% 용량을 절감할 수 있다. 네트워크 요청은 캐싱 전략을 명확하게 수립하고, 오프라인 우선(Offline-first) 설계를 적용하면 사용자 체감 속도가 크게 향상된다. 번들 크기를 줄이면 콜드 스타트 시간도 거의 항상 함께 줄어든다. 시작 시간을 단축하려면 초기 화면에 필요하지 않은 모듈을 지연 로딩(deferred loading)으로 분리하고, 릴리스 빌드의 트리 셰이킹을 활용해 사용하지 않는 코드를 걷어내는 것이 효과적이다.

2026년 현장 성능 개선 체크리스트

60fps를 넘어 120fps를 기본값으로

2026년의 사용자는 더 이상 60fps를 칭찬하지 않는다. 프로모션(ProMotion)·LTPO 패널이 보편화되면서 120fps 수준의 부드러움이 사실상 기본 기대치가 됐다. 120fps에서는 한 프레임에 허용되는 시간이 8.33ms로 떨어지므로, 빌드·레이아웃·페인트가 그 안에 끝나야 한다. Flutter에서는 UI 스레드 약 4ms, 래스터 스레드 약 4ms를 목표선으로 잡고 프로파일 모드에서 측정하는 것이 실전 기준이다. 안드로이드에서 고주사율을 강제로 켜야 한다면 flutter_displaymode 같은 패키지로 디스플레이 모드를 지정한다.

프로파일링 도구 — Flipper에서 내장 도구로

예전에 표준처럼 쓰이던 Flipper는 신아키텍처와의 호환 문제로 권장 도구에서 빠졌다. 2026년 React Native에서는 Hermes 샘플링 프로파일러로 트레이스를 기록한 뒤 Chrome DevTools에서 플레임 그래프로 JS 스레드 병목을 바로 읽어내는 방식이 표준이다. Flutter는 여전히 Dart DevTools의 Performance·CPU·Memory 탭이 가장 강력한 무기다. 어느 쪽이든 반드시 릴리스에 가까운 프로파일 빌드에서 측정해야 디버그 빌드의 거짓 병목에 속지 않는다.

메모리와 리렌더링 방어

저사양 안드로이드의 OOM 크래시는 대부분 디코딩된 이미지 캐시가 원인이다. 표시 크기에 맞춰 리사이즈된 이미지를 캐싱하고, 화면 밖 아이템은 적극적으로 해제한다. 리렌더링 측면에서는 React Native라면 React.memo와 안정적인 콜백(useCallback)으로 가상화 리스트의 셀 재생성을 막고, Flutter라면 상태가 바뀌는 구간만 별도 위젯으로 떼어내 const 생성자로 빌드 비용을 0에 수렴시킨다.

프레임워크 성능 비교 (2026년 6월 기준)

항목React Native (0.76+)Flutter (Impeller)
렌더링 모델네이티브 뷰 + Fabric자체 캔버스 + Impeller(Vulkan/Metal)
네이티브 통신JSI 직접 호출(브릿지 제거)FFI / 플랫폼 채널
JS-네이티브 호출 속도구 브릿지 대비 30~50% 개선해당 없음(단일 엔진)
시작 시간JSI 적용 시 약 25% 단축트리 셰이킹·deferred로 단축
셰이더 잭해당 없음AOT 사전 컴파일로 거의 제거
대표 프로파일러Hermes 샘플링 프로파일러Dart DevTools
선택 기준

픽셀 단위로 동일한 화면과 풍부한 커스텀 애니메이션이 핵심이라면 Impeller 기반 Flutter가 120fps를 더 일관되게 뽑아낸다. 반대로 기존 네이티브 화면과의 혼합, 웹/RN 코드 공유가 중요하다면 신아키텍처 React Native가 유리하다. 정답은 팀의 기존 자산과 화면 특성에 달려 있다.

마무리

크로스플랫폼이 느리다는 말은 2026년에는 더 이상 자동으로 참이 아니다. React Native의 신아키텍처와 Flutter의 Impeller가 각각 가장 큰 병목이던 브릿지와 셰이더 잭을 구조적으로 걷어냈기 때문이다. 결국 남는 것은 개발자의 손에 달린 일들이다. 측정 가능한 지표를 정하고, 프로파일 빌드에서 병목을 직접 눈으로 확인하고, 리렌더링과 메모리를 끈질기게 방어하는 습관. 이 세 가지를 갖추면 사용자는 그 앱이 크로스플랫폼인지조차 알아채지 못할 것이다.

크로스플랫폼 성능 최적화 React Native Flutter 프로파일링 모바일
junetapa
junetapa
AI 도구를 직접 써보고 솔직한 경험을 공유하는 개발자.
Twitter Facebook URL 복사