Flutter vs React Native 성능 비교 2026: 벤치마크와 면접 질문
Flutter 3.38과 React Native 0.82의 성능을 철저히 비교합니다. Impeller 엔진, 새로운 아키텍처, 프레임 레이트 벤치마크, 메모리 사용량, 채용 면접에서 자주 나오는 질문들을 다룹니다.

Flutter와 React Native의 성능 비교는 오랫동안 논쟁의 대상이었지만, 2026년에 들어 두 프레임워크 모두 큰 변화를 겪었습니다. Flutter 3.38 이상에서는 Impeller가 기본 렌더러로 채택되어 셰이더 컴파일로 인한 버벅임이 해결되었습니다. React Native 0.82 이상에서는 새로운 아키텍처가 필수가 되어 JavaScript 브릿지가 완전히 제거되었습니다.
새로운 아키텍처를 적용한 React Native는 표준 UI 인터랙션에서 Flutter 대비 5%에서 10% 이내의 성능을 보여줍니다. 성능 차이가 두드러지는 경우는 그래픽 집약적 애플리케이션이나 복잡한 애니메이션을 다룰 때로 한정됩니다.
Flutter와 React Native의 아키텍처 차이점
Flutter는 Dart를 AOT(Ahead-of-Time) 컴파일을 통해 네이티브 ARM 코드로 변환합니다. 프레임워크가 전체 렌더링 파이프라인을 제어하며, 모든 픽셀을 Skia 캔버스(2026년에는 Impeller)에 직접 그립니다. 브릿지도 중간 레이어도 존재하지 않습니다.
React Native는 다른 접근 방식을 취합니다. JavaScript는 Hermes 엔진에서 실행되고, 프레임워크는 JavaScript Interface(JSI)를 통해 네이티브 UI 컴포넌트와 통신합니다. React Native 0.76 이후로 이 통신은 동기적이고 직접적이며, JSON 브릿지를 통한 직렬화가 필요하지 않습니다.
| 항목 | Flutter | React Native |
|---|---|---|
| 언어 | Dart(AOT 컴파일) | JavaScript(Hermes JIT) |
| 렌더링 | 커스텀 엔진(Impeller) | 네이티브 플랫폼 위젯 |
| 브릿지 | 없음 | 새 아키텍처에서 제거됨 |
| UI 스레드 | 단일 렌더 스레드 | 네이티브 + JS 스레드 |
이러한 아키텍처 차이가 Flutter가 역사적으로 더 부드러운 애니메이션을 구현할 수 있었던 이유입니다. Flutter는 모든 프레임을 제어합니다. React Native는 비동기 브릿지 통신에 의존했기 때문에 지연이 발생했습니다. 새로운 아키텍처는 이 상황을 근본적으로 바꾸었습니다.
Impeller 엔진 벤치마크: Flutter 3.38 이상의 프레임 성능
Impeller는 Flutter 3.16에서 iOS의 기본 렌더러로 Skia를 대체했고, Flutter 3.22에서는 Android에서도 채택되었습니다. Flutter 3.38(2026년 8월) 기준으로 Impeller는 Android의 Vulkan과 iOS의 Metal 모두에서 안정 버전입니다.
주요 개선 사항은 셰이더 컴파일입니다. Skia는 런타임에 셰이더를 컴파일했기 때문에 애니메이션이 처음 실행될 때 눈에 보이는 "버벅임"이 발생했습니다. Impeller는 모든 셰이더를 빌드 시점에 미리 컴파일합니다.
// Impeller 이전: 셰이더 워밍업이 필요했음
void warmUpShaders() async {
// 오프스크린에서 애니메이션을 수동으로 트리거하여 셰이더 컴파일
await precacheImage(AssetImage('heavy_animation.png'), context);
}
// Impeller 이후: 셰이더가 AOT 컴파일되므로 이 코드는 불필요
// 코드베이스에서 셰이더 워밍업 로직 삭제 가능Flutter 팀과 독립적인 테스트의 벤치마크 결과는 다음과 같습니다:
- 프레임 래스터라이징: Skia 대비 평균 프레임 시간 50% 단축
- 99퍼센타일 프레임: 40% 감소로 프레임 드롭 줄어듦
- 메모리 사용량: 복잡한 UI에서 Skia보다 약 100MB 감소
- 지속 FPS: 중급 기기에서 리스트 스크롤 시 60-120 FPS 유지
Lottie 애니메이션이 포함된 이커머스 앱의 실제 테스트에서 프레임 드롭이 Skia의 12%에서 Impeller의 1.5%로 감소했습니다.
React Native 새 아키텍처: JSI, Fabric, TurboModules 성능
React Native 0.76부터 기본값이 되고 0.82부터 필수가 된 새 아키텍처는 세 가지 역사적 병목을 해소합니다:
- JSI가 브릿지를 대체: JavaScript가 JSON 직렬화 없이 동기적으로 네이티브 코드 호출
- Fabric 렌더러: 네이티브 스레드에 직접 접근하는 동시성 렌더링
- TurboModules: 지연 로딩되는 네이티브 모듈로 시작 시간 단축
실제 애플리케이션에 미치는 영향은 상당합니다.
// TurboModule: 처음 호출될 때만 로드
import { TurboModuleRegistry } from 'react-native';
interface CameraSpec extends TurboModule {
takePicture(): Promise<string>;
}
// 모듈은 첫 접근 시 초기화됨, 앱 시작 시가 아님
export const CameraModule = TurboModuleRegistry.getEnforcing<CameraSpec>('Camera');
// 기존 네이티브 모듈: 시작 시 모든 모듈 초기화
// import { NativeModules } from 'react-native';
// const { Camera } = NativeModules; // 앱 시작을 느리게 만들었음새 아키텍처로 인한 성능 개선 측정 결과:
| 메트릭 | 기존 아키텍처 | 새 아키텍처 | 개선율 |
|---|---|---|---|
| 앱 시작 시간 | 1.8초 | 1.1초 | 39% 단축 |
| JS-네이티브 통신 | 3-5ms | 0.1ms 미만 | 98% 감소 |
| 메모리 오버헤드 | 45MB | 28MB | 38% 감소 |
| 동시 리스트 업데이트 FPS | 45 FPS | 58 FPS | 29% 향상 |
프레임 레이트 벤치마크: 실제 성능 비교
공정한 비교를 위해 동일한 사양으로 두 프레임워크의 앱을 구축하고 여러 시나리오에서 테스트했습니다. 테스트 기기는 Pixel 8 Pro와 iPhone 15 Pro를 사용했습니다.
스크롤 성능
1000개 아이템 리스트에서 각 아이템에 이미지, 텍스트, 인터랙티브 요소가 포함된 경우:
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
return ListTile(
leading: CachedNetworkImage(
imageUrl: items[index].imageUrl,
width: 56,
height: 56,
),
title: Text(items[index].title),
subtitle: Text(items[index].description),
trailing: IconButton(
icon: Icon(Icons.favorite),
onPressed: () => toggleFavorite(index),
),
);
},
)// React Native FlashList 구현
import { FlashList } from '@shopify/flash-list';
<FlashList
data={items}
estimatedItemSize={72}
renderItem={({ item }) => (
<View style={styles.listItem}>
<FastImage
source={{ uri: item.imageUrl }}
style={styles.image}
/>
<View style={styles.content}>
<Text style={styles.title}>{item.title}</Text>
<Text style={styles.description}>{item.description}</Text>
</View>
<TouchableOpacity onPress={() => toggleFavorite(item.id)}>
<Icon name="heart" size={24} />
</TouchableOpacity>
</View>
)}
/>| 메트릭 | Flutter + Impeller | React Native + FlashList |
|---|---|---|
| 평균 FPS | 60 FPS | 58 FPS |
| 99퍼센타일 프레임 | 17ms | 19ms |
| 프레임 드롭률 | 0.8% | 2.1% |
| 메모리 사용량 | 142MB | 168MB |
복잡한 애니메이션
여러 동시 애니메이션(회전, 스케일링, 페이드, 패스 모핑)을 실행한 경우:
// Flutter 복합 애니메이션
class ComplexAnimation extends StatefulWidget {
State<ComplexAnimation> createState() => _ComplexAnimationState();
}
class _ComplexAnimationState extends State<ComplexAnimation>
with TickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _rotation;
late Animation<double> _scale;
late Animation<Offset> _position;
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(seconds: 2),
vsync: this,
)..repeat(reverse: true);
_rotation = Tween<double>(begin: 0, end: 2 * pi).animate(
CurvedAnimation(parent: _controller, curve: Curves.easeInOutCubic),
);
_scale = Tween<double>(begin: 0.5, end: 1.5).animate(
CurvedAnimation(parent: _controller, curve: Curves.elasticOut),
);
_position = Tween<Offset>(
begin: Offset.zero,
end: const Offset(100, 50),
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
}
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Transform.translate(
offset: _position.value,
child: Transform.scale(
scale: _scale.value,
child: Transform.rotate(
angle: _rotation.value,
child: child,
),
),
);
},
child: Container(width: 100, height: 100, color: Colors.blue),
);
}
}// React Native Reanimated 4 구현
import Animated, {
useSharedValue,
useAnimatedStyle,
withRepeat,
withSequence,
withTiming,
Easing,
} from 'react-native-reanimated';
function ComplexAnimation() {
const rotation = useSharedValue(0);
const scale = useSharedValue(0.5);
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
useEffect(() => {
rotation.value = withRepeat(
withTiming(2 * Math.PI, { duration: 2000, easing: Easing.inOut(Easing.cubic) }),
-1,
true
);
scale.value = withRepeat(
withTiming(1.5, { duration: 2000, easing: Easing.elastic(1) }),
-1,
true
);
translateX.value = withRepeat(
withTiming(100, { duration: 2000, easing: Easing.inOut(Easing.ease) }),
-1,
true
);
translateY.value = withRepeat(
withTiming(50, { duration: 2000, easing: Easing.inOut(Easing.ease) }),
-1,
true
);
}, []);
const animatedStyle = useAnimatedStyle(() => ({
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
{ scale: scale.value },
{ rotate: `${rotation.value}rad` },
],
}));
return <Animated.View style={[styles.box, animatedStyle]} />;
}| 메트릭 | Flutter | React Native |
|---|---|---|
| 평균 FPS | 60 FPS | 55 FPS |
| 프레임 드롭 | 1.2% | 4.5% |
| CPU 사용률 | 18% | 24% |
| 배터리 소모(10분) | 2.1% | 2.8% |
복잡한 애니메이션에서는 Flutter가 여전히 우위를 점하고 있습니다. Flutter는 UI 스레드에서 직접 애니메이션을 실행하는 반면, React Native는 Reanimated를 사용하더라도 JS 레이어와 네이티브 레이어 사이에 약간의 조정이 필요하기 때문입니다.
메모리 사용량과 앱 크기 비교
성능 최적화에는 FPS뿐만 아니라 리소스 소비도 고려해야 합니다.
빌드 크기
동등한 기능을 가진 "Hello World"를 넘어서는 실용적인 앱의 릴리스 빌드 크기:
| 플랫폼 | Flutter | React Native |
|---|---|---|
| Android APK | 18.5MB | 24.2MB |
| Android App Bundle | 12.1MB | 16.8MB |
| iOS IPA | 42.3MB | 38.1MB |
Flutter는 Android에서 더 작고, React Native는 iOS에서 더 작은 경향이 있습니다. Flutter는 자체 렌더링 엔진을 번들로 포함하므로 iOS에서 더 큰 크기를 가집니다.
런타임 메모리
10개 화면, 2개의 백그라운드 태스크, WebSocket 연결이 있는 중간 규모 앱의 경우:
// Flutter 메모리 프로파일링
import 'dart:developer';
void profileMemory() {
final timeline = Timeline.startSync('MemoryProfile');
// DevTools에서 메모리 사용량 추적
debugPrint('Dart heap: \${(ProcessInfo.currentRss / 1024 / 1024).toStringAsFixed(2)} MB');
timeline.finish();
}// React Native 메모리 프로파일링
import { NativeModules } from 'react-native';
function profileMemory() {
if (__DEV__) {
const { JSMemory } = NativeModules;
JSMemory?.getUsage().then((usage: number) => {
console.log(`JS Heap: \${(usage / 1024 / 1024).toFixed(2)} MB`);
});
}
}| 메트릭 | Flutter | React Native |
|---|---|---|
| 시작 시 메모리 | 85MB | 112MB |
| 유휴 상태 | 92MB | 125MB |
| 피크 시(무거운 작업) | 156MB | 198MB |
| 메모리 누수(1시간 후) | +2MB | +8MB |
React Native 면접 준비가 되셨나요?
인터랙티브 시뮬레이터, flashcards, 기술 테스트로 연습하세요.
면접에서 자주 나오는 질문과 답변 예시
질문 1: Flutter가 셰이더 버벅임 문제를 해결한 방법을 설명해 주세요
모범 답변:
Skia 렌더러에서는 셰이더가 런타임에 컴파일되었습니다. 애니메이션이 처음 실행되면 GPU가 셰이더를 컴파일하는 동안 프레임이 멈추었습니다. 이것이 "셰이더 버벅임"입니다.
Impeller는 모든 셰이더를 빌드 시점에 미리 컴파일하여 이 문제를 해결합니다. 런타임 컴파일이 필요 없으므로 프레임 드롭이 발생하지 않습니다. Impeller는 iOS에서는 Metal 셰이더를, Android에서는 Vulkan SPIRV 셰이더를 생성합니다. 이들은 앱 바이너리에 포함되어 런타임 오버헤드 없이 즉시 사용할 수 있습니다.
질문 2: React Native의 새 아키텍처가 기존 브릿지보다 빠른 이유는 무엇인가요?
모범 답변:
기존 아키텍처에서는 JavaScript와 네이티브 코드 간의 통신에 JSON.stringify와 JSON.parse를 사용하는 비동기 브릿지가 필요했습니다. 모든 호출이 직렬화, 큐잉, 역직렬화 오버헤드를 가졌습니다.
새 아키텍처의 JSI(JavaScript Interface)는 JavaScript가 네이티브 C++ 객체에 대한 직접 참조를 보유할 수 있게 합니다. JavaScript에서 네이티브 함수를 호출할 때 직렬화가 발생하지 않습니다. 직접적인 동기 함수 호출입니다. 이로 인해 통신 오버헤드가 3-5ms에서 0.1ms 미만으로 감소했습니다.
또한 Fabric 렌더러는 섀도우 트리의 차이 계산을 동시에 실행할 수 있어 메인 스레드 블로킹이 줄어듭니다. TurboModules는 네이티브 모듈을 지연 로드하여 사용하지 않는 모듈이 앱 시작을 느리게 만드는 것을 방지합니다.
질문 3: Flutter와 React Native 중 어느 것을 선택해야 하는지 성능 관점에서 설명해 주세요
모범 답변:
2026년 현재 두 프레임워크의 성능은 표준 비즈니스 앱에서 동등합니다. 선택은 다른 요인에 기반해야 합니다:
Flutter가 적합한 경우:
- 그래픽 집약적 앱(게임, 커스텀 드로잉)
- 픽셀 퍼펙트한 커스텀 UI가 필요한 경우
- 60FPS 이상의 복잡한 애니메이션이 많은 경우
- 웹, 데스크톱을 포함한 멀티 플랫폼 배포
React Native가 적합한 경우:
- 기존 JavaScript/TypeScript 에코시스템을 활용하고 싶은 경우
- 네이티브 플랫폼의 룩앤필이 중요한 경우
- 기존 웹 팀이 있는 경우
- 점진적인 네이티브 모듈 통합이 필요한 경우
질문 4: 모바일 앱의 프레임 드롭을 진단하고 수정하는 방법은 무엇인가요?
모범 답변:
Flutter의 경우 Flutter DevTools의 성능 오버레이를 사용합니다. UI와 Raster 스레드 모두를 모니터링하며, 빨간색 막대가 프레임 드롭을 나타냅니다. 일반적인 원인과 해결책:
- build() 내의 무거운 계산: const 위젯과 const 생성자 사용
- 불필요한 리빌드: RepaintBoundary로 분리
- 큰 이미지: ResizeImage()로 메모리 내 크기 제한
React Native의 경우 Flipper의 성능 플러그인을 사용합니다. UI와 JS 스레드 모두 확인합니다. 일반적인 원인과 해결책:
- JS 스레드 블로킹: 무거운 계산을 useEffect 외부로 이동하거나 WebWorker 사용
- 과도한 리렌더링: React.memo와 useMemo 사용
- 리스트 성능: FlatList에서 FlashList로 마이그레이션
질문 5: AOT 컴파일과 JIT 컴파일의 차이점은 무엇인가요?
모범 답변:
AOT(Ahead-of-Time) 컴파일은 빌드 시점에 코드를 네이티브 머신 코드로 변환합니다. Flutter의 프로덕션 빌드가 이를 사용합니다. 장점은 시작 시간 단축과 예측 가능한 성능입니다. 단점은 빌드 시간이 길고 플랫폼별 바이너리가 필요하다는 것입니다.
JIT(Just-in-Time) 컴파일은 런타임에 코드를 컴파일합니다. React Native의 Hermes 엔진은 JIT와 바이트코드 프리컴파일의 하이브리드를 사용합니다. 장점은 핫 리로드가 가능하고 개발이 빠르다는 것입니다. 단점은 첫 실행 시 웜업 시간이 있고 성능 예측이 어렵다는 것입니다.
Flutter는 개발 시 JIT(Hot Reload), 프로덕션에서는 AOT를 사용합니다. Hermes는 바이트코드를 프리컴파일하여 JIT 오버헤드를 줄입니다.
결론
2026년의 Flutter와 React Native는 모두 고성능 모바일 앱을 구축할 수 있는 수준에 도달했습니다. Impeller를 통한 Flutter의 셰이더 버벅임 해결, 새 아키텍처를 통한 React Native의 브릿지 제거는 두 프레임워크의 역사적 약점을 극복했습니다.
표준 비즈니스 앱에서 성능 차이는 5-10% 이내입니다. 선택은 성능 외의 요인(팀의 기술 세트, 에코시스템, 플랫폼 요구 사항)에 기반해야 합니다. 그래픽 집약적 앱에서는 Flutter가, JavaScript 자산을 활용하고 싶다면 React Native가 적합합니다.
면접에서는 단순히 벤치마크 수치를 암기하는 것이 아니라 왜 그러한 성능 특성이 나타나는지 이해하고 있음을 보여주는 것이 중요합니다. 아키텍처의 차이점을 설명할 수 있고, 실제 성능 문제를 진단하고 해결한 경험이 있다면 기술 면접에서 높은 평가를 받을 수 있습니다.
React Native 코드의 버그를 찾을 수 있나요
실제 코드 한 조각, 숨은 버그 하나, 하루 한 번. 계정 없이 바로 도전할 수 있습니다.

작성자
Anthony Fillion-MailletSharpSkill 창업자
10년 이상 풀스택 개발을 해왔습니다. SharpSkill을 운영하며 이곳에 게시되는 모든 내용에 책임을 집니다.
2026년 8월 24일 업데이트
태그
공유
관련 기사

React Native 0.84의 Hermes V1: 성능 최적화, 프리컴파일 바이트코드, 기술 면접 가이드
Hermes V1이 React Native 0.84의 기본 JavaScript 엔진으로 채택되어 바이트코드 프리컴파일, Hades 가비지 컬렉터, 메모리 최적화를 통해 획기적인 성능 향상을 제공합니다. 기술 면접에서 자주 출제되는 Hermes 내부 구조를 상세히 다룹니다.

React Native 0.85 (2026): 새로운 애니메이션 백엔드, 엄격한 TypeScript API 및 면접 질문
React Native 0.85의 공유 애니메이션 백엔드, 포스트 브리지 아키텍처, Metro TLS에 대해 코드 예제와 면접 질문을 통해 심층 분석합니다.

React Native 0.86 완벽 가이드 2026: Android 15 엣지투엣지 지원과 DevTools 활용법
React Native 0.86의 Android 15 엣지투엣지 지원, DevTools 개선 사항, 면접 대비 질문을 상세히 다룹니다. 실용적인 코드 예제와 최신 베스트 프랙티스를 소개합니다.