필사 모드: 크로스플랫폼 모바일 개발 2026 심층 가이드 — React Native·Flutter·Expo·Capacitor·Tauri 2·Kotlin Multiplatform·Compose MP·.NET MAUI·NativeScript·Lynx
한국어프롤로그 — 2026년, "한 번 짜서 둘 다"라는 약속의 새 국면
2015년 React Native가 처음 등장했을 때, "한 번 짜서 iOS·Android 둘 다 돌린다"는 약속은 거대했다. Facebook이 직접 만들고, JSX로 짜고, JavaScript 브릿지를 통해 네이티브 뷰를 조작한다. 그것은 약속이었지만 동시에 한계였다. 브릿지는 비동기였고, 직렬화 비용이 컸으며, 60fps의 부드러운 애니메이션은 늘 신경 써야 했다.
2026년 5월, 풍경이 바뀌었다.
- **React Native 0.76 (Meta, 2024년 10월 출시 → 2026년 안정 운영기)** 의 New Architecture(Fabric 렌더러 + TurboModules + JSI)가 모든 신규 앱의 기본값이 되었다. 브릿지 직렬화가 사라지고, 동기 네이티브 호출이 가능해졌다.
- **Flutter 3.27 (Google, 2024년 말 출시 → 2026년 운영기)** 은 Impeller 렌더러를 iOS와 Android 모두에서 기본값으로 만들었다. Skia 기반의 동적 셰이더 컴파일이 사라지고, 미리 컴파일된 셰이더로 첫 프레임의 끊김(jank)이 거의 사라졌다.
- **Tauri 2.0 (Tauri team, 2024년 10월)** 이 iOS·Android 모바일 타깃을 정식 지원하기 시작했다. "Rust로 모바일"이 처음으로 실현 가능해졌다.
- **JetBrains Kotlin Multiplatform 2.1 + Compose Multiplatform 1.7** 은 iOS 공유 UI를 안정 단계로 끌어올렸다.
- **ByteDance Lynx** 가 2025년 3월 오픈소스로 공개됐다. TikTok·Douyin의 라이브 커머스·미니앱을 굴리던 사내 프레임워크가 외부에 처음 풀린 사건이다.
크로스플랫폼은 더 이상 단일 정답이 없다. 이 글은 2026년의 모바일 크로스플랫폼 스택을 한 호흡으로 정리한다.
1장 · 크로스플랫폼이 푸는 본질 문제
iOS는 Swift/Objective-C + UIKit/SwiftUI를 쓰고, Android는 Kotlin/Java + Views/Jetpack Compose를 쓴다. 두 플랫폼은 언어·UI 툴킷·툴체인·배포 채널이 모두 다르다. "비즈니스 로직과 화면을 한 번만 짜고 양쪽에서 돌린다"는 욕구는 그래서 끈질기다.
크로스플랫폼은 다섯 가지 다른 전략으로 이 문제를 푼다.
- **JavaScript 브릿지(전통 React Native)**: JS 코드가 네이티브 뷰를 원격 제어한다.
- **JSI 직접 호출(현대 React Native, New Architecture)**: JS와 네이티브가 같은 메모리·동기 호출.
- **자체 렌더러(Flutter, Lynx)**: 플랫폼 뷰를 안 쓰고 캔버스에 직접 그린다.
- **WebView(Capacitor, Ionic, Cordova)**: 웹 UI를 네이티브 컨테이너에 띄운다.
- **공유 비즈니스 로직(Kotlin Multiplatform, KMP)**: 비즈니스 코드는 공유, UI는 플랫폼 네이티브.
선택은 도메인 의존이다. 게임은 자체 렌더러를 선호하고, 사내 도구는 WebView로 충분하며, 메신저·이커머스는 New Architecture RN 또는 KMP 어느 한쪽으로 수렴한다.
2장 · React Native New Architecture — JSI·Fabric·TurboModules
React Native 0.76에서 안정화된 New Architecture는 세 부분이다.
- **JSI (JavaScript Interface)**: V8/Hermes와 C++ 사이의 얇은 인터페이스. JS 객체가 C++ HostObject로 직접 노출되어 직렬화 없이 호출이 가능하다.
- **Fabric**: 새 렌더러. JS가 React 트리를 만들고, 그 트리가 C++ Shadow Tree로 변환되어 메인 스레드에서 직접 네이티브 뷰로 커밋된다. 동기 레이아웃 가능.
- **TurboModules**: 네이티브 모듈을 JSI 기반으로 재작성. 모듈은 lazy하게 초기화되고, 호출은 동기/비동기 모두 가능하다.
전통 RN에서는 모든 호출이 "직렬화 → JSON → 네이티브 디시리얼라이즈 → 비동기 콜백"이었다면, New Architecture는 JS가 직접 C++ 포인터를 잡고 함수를 호출한다. 핫 리로드는 여전히 빠르고, 디버깅은 React DevTools 그대로다.
// TurboModule 정의: spec/NativeMathModule.ts
export interface Spec extends TurboModule {
add(a: number, b: number): number
factorial(n: number): Promise<number>
getDeviceLocale(): string
}
export default TurboModuleRegistry.getEnforcing<Spec>('NativeMathModule')
// 사용: App.tsx
export default function App() {
// 동기 호출 — 브릿지 없이 직접 네이티브 함수를 호출한다
const sum = NativeMathModule.add(2, 3)
const locale = NativeMathModule.getDeviceLocale()
return null
}
`codegen` 단계가 Spec 인터페이스를 읽어 iOS의 Objective-C++ 헤더와 Android의 Java/Kotlin 인터페이스를 자동 생성한다. 옛 NativeModules보다 타입 안전성이 비약적으로 좋아졌다.
3장 · Hermes — JavaScriptCore에서 자체 엔진으로
Hermes는 Meta가 모바일 RN을 위해 만든 JS 엔진이다. 2026년 현재 RN의 기본 엔진이다.
- 바이트코드 사전 컴파일(AOT)로 시작 시간이 짧다.
- 가비지 컬렉터가 모바일에 맞게 튜닝됐다(짧은 stop-the-world).
- V8보다 메모리 풋프린트가 작다.
- ES2015+ 대부분 지원, Proxy·정규식 일부 제약이 있다.
전통적으로 iOS RN은 JavaScriptCore(JSC)를 썼고, Android는 V8 또는 JSC였다. Hermes는 두 플랫폼에서 동일한 동작을 보장하면서도 시작 시간을 절반 가까이 줄였다. `<200ms 콜드 스타트`가 가능한 이유다.
4장 · Flutter — Skia에서 Impeller로
Flutter는 처음부터 자체 렌더러를 가진 프레임워크였다. 플랫폼의 UIKit·View를 쓰지 않고, Skia로 직접 픽셀을 그린다. 2026년의 변화는 렌더 엔진이 Skia에서 Impeller로 옮겨갔다는 것이다.
- **Skia**: Chromium의 2D 그래픽 엔진. Flutter 초기부터 썼다. 강점은 호환성과 성숙도. 약점은 런타임 셰이더 컴파일로 인한 첫 프레임 끊김.
- **Impeller**: Flutter 팀이 자체 개발한 렌더러. 셰이더를 빌드 타임에 미리 컴파일하고, Metal(iOS)·Vulkan(Android)을 직접 사용. 첫 프레임 jank가 거의 사라졌다.
Flutter 3.27부터 iOS·Android 모두 Impeller가 기본이다. 일부 복잡한 셰이더 효과는 여전히 Skia 폴백을 쓰지만, 일반 앱의 99%는 Impeller에서 부드럽게 돈다.
// Flutter: 기본 Material 앱
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: '2026 Cross-Platform Demo',
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(child: Text('Count: $_count', style: const TextStyle(fontSize: 32))),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _count++),
child: const Icon(Icons.add),
),
);
}
}
Dart는 AOT 컴파일되어 네이티브 머신 코드로 변환되므로, JIT 워밍업이 없다. 시작은 빠르고, 60fps 유지가 비교적 쉽다. 단점은 바이너리 크기가 크다는 것 — 기본 Flutter 앱이 iOS에서 약 `15MB`, Android에서 `8-10MB`를 차지한다.
5장 · Expo — RN 개발자 경험의 사실상 표준
Expo는 처음에는 "RN 시작하기 쉬운 도구"였지만, 2026년에는 RN의 사실상 표준 개발 환경이 됐다. Expo SDK 53(2026년 상반기)의 핵심 컴포넌트:
- **EAS Build**: 클라우드 빌드 서비스. iOS는 Mac이 필요하지만 EAS는 클라우드에서 처리한다.
- **EAS Submit**: App Store / Play Store 자동 제출.
- **EAS Update**: OTA(Over-the-air) 업데이트. 네이티브 변경 없는 JS 업데이트를 코드 푸시한다.
- **Expo Router**: 파일 기반 라우팅. Next.js와 비슷한 `app/` 디렉터리 구조를 RN에 도입.
- **Expo Atlas**: 번들 분석 도구. 어떤 모듈이 번들 크기를 차지하는지 시각화.
- **Expo Modules**: TurboModule 작성을 더 쉽게 한 wrapper. Swift/Kotlin으로 모듈을 만들고 자동 JS 바인딩을 받는다.
Expo SDK 53 신규 앱
npx create-expo-app@latest my-app --template default
cd my-app
로컬 개발
npx expo start
클라우드 빌드
npx eas build --platform ios --profile development
npx eas build --platform android --profile production
OTA 업데이트 푸시
npx eas update --branch production --message "fix: button text typo"
Expo Router는 RN 라우팅을 근본적으로 바꿨다.
파일 구조
app/
_layout.tsx # 루트 레이아웃 (탭 또는 스택)
index.tsx # 홈 (/)
login.tsx # 로그인 (/login)
(tabs)/
_layout.tsx # 탭 레이아웃
index.tsx # 탭 홈
profile.tsx # 탭 프로필 (/profile)
product/
[id].tsx # 동적 라우트 (/product/123)
React Navigation을 손으로 구성하던 옛 RN 패턴과 비교하면, Expo Router는 Next.js 출신 개발자에게 "그대로"의 학습 곡선이다.
6장 · Capacitor 7 + Ionic — WebView 진영의 진화
Capacitor는 Ionic 팀이 만든 WebView 기반 크로스플랫폼 런타임이다. 옛 Cordova의 후계자이고, 2025년의 Capacitor 7은 의미 있는 진화를 했다.
- 웹 UI(React, Vue, Angular, Svelte 자유 선택)를 네이티브 컨테이너에 띄운다.
- 네이티브 기능(카메라, GPS, 푸시, 파일)은 플러그인 시스템으로 접근.
- 100% 네이티브 빌드 산출물(.ipa, .apk).
- PWA를 그대로 모바일 앱으로 포팅 가능.
// Capacitor: 카메라 플러그인 사용
async function takePhoto() {
const photo = await Camera.getPhoto({
quality: 90,
allowEditing: false,
resultType: CameraResultType.Uri,
})
// photo.webPath 를 <img src=...> 에 바로 쓸 수 있다
return photo.webPath
}
Capacitor 7의 핵심 변화: **iOS WKWebView, Android WebView 둘 다 최신 엔진으로 통일** 됐다. 결과적으로 모던 CSS(Container Queries, View Transitions API)와 WebGPU 일부 기능이 동작한다.
언제 Capacitor가 맞나: 콘텐츠 중심 앱, 사내 도구, 빠른 출시가 우선인 MVP, 웹팀의 기존 코드 재사용. 언제 안 맞나: 헤비 애니메이션, 게임, 60fps 스크롤이 필수인 피드.
7장 · Tauri 2 Mobile — Rust로 모바일
2024년 10월의 Tauri 2.0은 가장 큰 사건 중 하나다. 그동안 Tauri는 데스크톱 전용(Rust 백엔드 + WebView 프론트)이었지만, 2.0부터 iOS와 Android 모바일을 정식 지원한다.
- 백엔드는 Rust로 짠다. iOS는 정적 라이브러리, Android는 JNI로 빌드된다.
- 프론트는 시스템 WebView(WKWebView, Android WebView). 웹 기술(React/Vue/Svelte) 자유.
- IPC(Inter-Process Communication)는 명령(command)과 이벤트로 표준화.
- 바이너리 크기는 React Native·Flutter보다 작다(`<10MB` 가능).
Tauri 2 모바일 프로젝트 초기화
npm create tauri-app@latest my-mobile-app
cd my-mobile-app
Android 타깃 추가
npm run tauri android init
npm run tauri android dev
iOS 타깃 추가
npm run tauri ios init
npm run tauri ios dev
tauri.conf.json (요약)
app:
product-name: my-mobile-app
identifier: com.example.app
windows:
- title: my-mobile-app
width: 800
height: 600
bundle:
active: true
targets: all
android:
minSdkVersion: 24
iOS:
minimumSystemVersion: '14.0'
Tauri 2 모바일은 아직 RN/Flutter 만큼 성숙하지 않다. Hot reload는 있지만 plugin 생태계는 한참 작고, App Store 심사 노하우도 적다. 하지만 "Rust 백엔드 + 웹 프론트 + 매우 작은 바이너리"라는 조합은 시스템 프로그래머에게 매력적이다.
8장 · Kotlin Multiplatform — 비즈니스 로직 공유의 정석
JetBrains의 Kotlin Multiplatform(KMP, 옛 KMM)은 다른 길을 간다. UI는 각 플랫폼 네이티브(SwiftUI on iOS, Compose on Android), **비즈니스 로직과 데이터 레이어만 공유** 한다.
- `commonMain`: 공유 코드(Kotlin).
- `androidMain`: Android 전용.
- `iosMain`: iOS 전용. Kotlin/Native로 iOS 프레임워크(.framework)로 빌드되고, Swift에서 임포트한다.
- expect/actual 키워드로 플랫폼별 구현을 분기한다.
// shared/src/commonMain/kotlin/Repository.kt
class UserRepository(private val api: UserApi) {
suspend fun fetchUser(id: String): User = api.getUser(id)
}
expect class PlatformLogger() {
fun log(message: String)
}
// shared/src/androidMain/kotlin/PlatformLogger.android.kt
actual class PlatformLogger actual constructor() {
actual fun log(message: String) {
android.util.Log.d("KMP", message)
}
}
// iosApp/iosApp/ContentView.swift
struct ContentView: View {
let repo = UserRepository(api: UserApi())
var body: some View {
Text("Hello, KMP!")
.task {
let user = try? await repo.fetchUser(id: "1")
}
}
}
KMP의 큰 장점: **네이티브 UI를 그대로 쓴다.** iOS는 SwiftUI, Android는 Compose. 두 디자인 가이드를 완벽히 따른다. 단점은 UI를 두 번 짜야 한다는 것.
토스·당근마켓·라인·메르카리가 KMP를 일부 채택했고, 2025-2026년 카카오와 라쿠텐도 검토를 시작했다는 발표가 있었다.
9장 · Compose Multiplatform — 공유 UI까지 가는 KMP의 확장
JetBrains는 KMP의 한계(UI는 따로 짜야 함)를 해결하기 위해 **Compose Multiplatform**을 만들었다. Android의 Jetpack Compose를 iOS·데스크톱·웹에도 가져온다.
- 같은 `@Composable` 함수가 안드로이드·iOS·데스크톱·웹에서 돈다.
- iOS는 Kotlin/Native + Skia 렌더러로 돈다(Flutter와 비슷).
- 안드로이드는 그대로 Jetpack Compose이고, Material 3 디자인 시스템 호환.
Compose Multiplatform 1.7(2025년 출시)부터 iOS가 정식 안정 단계(stable)에 진입했다. JetBrains 데모와 카카오·토스의 PoC 결과 60fps가 일반적으로 가능하다.
// shared/src/commonMain/kotlin/App.kt
@Composable
fun App() {
var count by remember { mutableStateOf(0) }
MaterialTheme {
Column(modifier = Modifier.padding(16.dp)) {
Text("Compose Multiplatform on iOS/Android")
Button(onClick = { count++ }) {
Text("Clicked $count times")
}
}
}
}
장점: 진짜 한 번 짜기. 단점: iOS 네이티브 룩앤필을 정확히 따르려면 추가 노력 필요. Flutter와 비슷하지만 Kotlin이라는 점이 차이.
10장 · .NET MAUI — Xamarin의 후계자
Microsoft의 .NET MAUI(Multi-platform App UI)는 Xamarin Forms의 후계자다. .NET 9 기반에서 iOS·Android·macOS·Windows를 모두 타깃으로 한다.
- 단일 프로젝트로 멀티 플랫폼.
- C# + XAML로 UI를 짠다.
- 옛 Xamarin Native(Xamarin.iOS, Xamarin.Android)는 2024년 5월 EOL.
- Hot Reload, .NET MAUI Blazor 하이브리드(WebView + .NET)도 지원.
.NET MAUI 신규 프로젝트
dotnet new maui -n MyApp
cd MyApp
iOS 빌드 (macOS에서)
dotnet build -t:Run -f net9.0-ios
Android 빌드
dotnet build -t:Run -f net9.0-android
.NET MAUI는 엔터프라이즈 .NET 팀이 모바일에 진출할 때 자연스러운 선택이다. 일본의 SI 시장에서 점유율이 있고, 한국의 일부 금융권에서도 채택 사례가 있다. 단점은 모바일 커뮤니티 규모가 RN/Flutter보다 작다는 점.
11장 · NativeScript — Vue·Angular와 함께
NativeScript는 Cordova/Capacitor와 RN 사이의 위치다. WebView를 안 쓰지만, JS로 네이티브 뷰를 직접 조작한다. RN이 JSI로 가는 동안 NativeScript는 자체 V8 통합으로 비슷한 길을 갔다.
- Vue, Angular, Svelte와 통합이 잘 된다(특히 Vue/Angular 팀에 인기).
- iOS는 Objective-C++, Android는 Java를 메타데이터로 노출해 JS에서 직접 호출.
- 옛 NativeScript Core는 유지보수 모드, **@nativescript/core 8.x**가 현행.
- 커뮤니티 규모는 RN·Flutter보다 작지만, Vue 기반 모바일에서는 유효한 선택.
NativeScript는 2026년에 niche 포지션이지만, Vue·Angular 코드베이스를 모바일로 가져갈 때 학습 비용이 가장 낮다는 강점이 있다.
12장 · ByteDance Lynx — 2025년의 새 얼굴
2025년 3월, ByteDance가 사내 프레임워크 **Lynx**를 오픈소스로 공개했다. TikTok·Douyin의 라이브 커머스 UI, 미니앱 컨테이너, 댓글 화면 등이 Lynx로 돌아간다.
Lynx의 설계 철학:
- **렌더링 엔진 분리**: JS 스레드와 렌더링 스레드를 분리. JS가 막혀도 UI는 60fps 유지.
- **CSS-in-Style**: React Native의 StyleSheet보다 진짜 CSS에 가까운 문법. Flexbox·Grid·Animation 풀 지원.
- **Multi-Threading**: JS 코드가 메인 스레드를 막지 않도록 처음부터 설계.
- **Web과 호환**: 동일 코드가 React DOM과 Lynx에서 모두 돌도록 추상화.
// Lynx 컴포넌트 (TypeScript + JSX)
class Counter extends Component {
state = { count: 0 }
render() {
return (
)
}
}
Lynx는 2026년 시점에서 RN/Flutter만큼 성숙하지는 않지만, ByteDance 같은 거대 사용자 + 오픈소스라는 점이 매력이다. 한국·일본에서는 아직 채택 사례가 적지만, 라이브 커머스를 운영하는 기업이 관심을 보이고 있다.
13장 · 렌더링 모델 비교
| 프레임워크 | 렌더링 방식 | 그래픽 API | 첫 프레임 jank | 60fps 난이도 |
| --- | --- | --- | --- | --- |
| React Native (New Arch) | 네이티브 뷰 + Fabric | UIKit / Android Views | 낮음 | 보통 |
| Flutter (Impeller) | 자체 렌더러 | Metal / Vulkan | 매우 낮음 | 쉬움 |
| Capacitor | WebView | WebKit / Blink | 보통 | 보통 |
| Tauri 2 Mobile | WebView | WebKit / Blink | 보통 | 보통 |
| Compose Multiplatform | Skia | Metal / OpenGL | 낮음 | 쉬움 |
| .NET MAUI | 네이티브 뷰 | UIKit / Android Views | 보통 | 보통 |
| NativeScript | 네이티브 뷰 | UIKit / Android Views | 낮음 | 보통 |
| Lynx | 자체 렌더러 + 스레드 분리 | Metal / OpenGL | 낮음 | 쉬움 |
| KMP (UI 따로) | 100% 네이티브 | UIKit / Compose | 가장 낮음 | 가장 쉬움 |
자체 렌더러(Flutter, Lynx, Compose MP)는 60fps 유지가 가장 쉽다. WebView 진영(Capacitor, Tauri)은 콘텐츠 중심 앱에 적합. 네이티브 뷰 진영(RN, MAUI, NativeScript)은 OS UI 가이드를 가장 잘 따른다.
14장 · 번들 크기·바이너리 크기 비교
같은 "Hello World + 카운터 + 네트워크 요청" 앱을 빌드했을 때 대략의 크기(2026년 기준, iOS Release):
| 프레임워크 | iOS .ipa | Android .apk | 비고 |
| --- | --- | --- | --- |
| React Native (Hermes) | `~10MB` | `~7MB` | Hermes 바이트코드 포함 |
| Flutter (Impeller, AOT) | `~15MB` | `~9MB` | Dart 런타임 + 아이콘 폰트 |
| Capacitor | `~5MB` | `~3MB` | WebView 자체는 OS 제공 |
| Tauri 2 Mobile | `~4MB` | `~3MB` | Rust 정적 링킹 |
| KMP (네이티브 UI) | `~3MB` | `~2MB` | 네이티브가 기본 |
| Compose Multiplatform | `~12MB` | `~5MB` | iOS는 Skia 포함 |
| .NET MAUI | `~25MB` | `~15MB` | .NET 런타임 큼 |
| NativeScript | `~10MB` | `~8MB` | JS 엔진 포함 |
| Lynx | `~8MB` | `~6MB` | 자체 렌더러 |
KMP(네이티브 UI)와 Capacitor/Tauri(WebView)가 가장 작다. .NET MAUI가 가장 크다.
15장 · 네이티브 모듈 브릿지 — 어떻게 네이티브에 접근하나
각 프레임워크가 네이티브 기능에 접근하는 방식.
| 프레임워크 | 메커니즘 | 작성 언어 | 타입 안전성 |
| --- | --- | --- | --- |
| React Native (TurboModules) | JSI HostObject | Obj-C++ / Java / Kotlin / Swift | codegen으로 강함 |
| Flutter | Platform Channels (MethodChannel) | Swift / Kotlin | 직렬화 기반, 수동 |
| Capacitor | Capacitor Plugin | Swift / Java | 데코레이터 기반, 보통 |
| Tauri 2 | Command + IPC | Rust | Serde 기반 강함 |
| KMP / Compose MP | expect/actual | Kotlin / Swift interop | Kotlin 타입 그대로 |
| .NET MAUI | DependencyService / Handler | C# | C# 타입 그대로 |
| NativeScript | Metadata + Reflection | JS + Obj-C/Java 메타데이터 | 런타임 |
| Lynx | Native Module | Swift / Kotlin | TypeScript codegen |
// React Native TurboModule (iOS) - Swift 부분
@objc(NativeMathModule)
class NativeMathModule: NSObject {
@objc func add(_ a: Double, b: Double) -> NSNumber {
return NSNumber(value: a + b)
}
}
// Flutter MethodChannel (Android Kotlin)
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.math"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
if (call.method == "add") {
val a = call.argument<Int>("a") ?: 0
val b = call.argument<Int>("b") ?: 0
result.success(a + b)
} else {
result.notImplemented()
}
}
}
}
타입 안전성은 TurboModules와 Tauri Commands가 가장 강하고, NativeScript는 가장 약하다(런타임 메타데이터에 의존).
16장 · 핫 리로드 · 개발자 경험
| 프레임워크 | Hot Reload | Hot Restart | DevTools | 디버거 |
| --- | --- | --- | --- | --- |
| React Native | Fast Refresh | 있음 | React DevTools | Chrome / Flipper / Reactotron |
| Flutter | 매우 빠름 | 있음 | Flutter DevTools | Dart DevTools |
| Capacitor | 웹과 동일 | 있음 | 브라우저 DevTools | Chrome / Safari |
| Tauri 2 Mobile | 빠름 | 있음 | 웹 + Rust 모두 | 웹 DevTools + lldb |
| KMP | 일반 IDE | 있음 | Android Studio / Xcode | 양쪽 디버거 |
| Compose Multiplatform | 빠름 | 있음 | Compose Preview | IntelliJ / Xcode |
| .NET MAUI | XAML Hot Reload | 있음 | Visual Studio | VS / Rider |
| Lynx | 빠름 | 있음 | Lynx DevTools | Chrome 기반 |
Flutter의 Hot Reload는 여전히 업계 최고 수준이다. 상태 보존 + 위젯 트리 재빌드가 `<500ms` 안에 끝난다. RN Fast Refresh도 New Architecture에서 한 단계 빨라졌다.
17장 · 성능 벤치마크 — 60fps 유지의 비용
2026년 기준 일반적 벤치마크(중급 Android 디바이스, Pixel 6a 기준).
| 프레임워크 | 콜드 스타트 | 스크롤 60fps | 메모리 (대시보드) | 배터리 (1시간 사용) |
| --- | --- | --- | --- | --- |
| React Native (New Arch + Hermes) | `~250ms` | 가능 | `~120MB` | `~7%` |
| Flutter (Impeller, AOT) | `~150ms` | 매우 쉬움 | `~100MB` | `~6%` |
| Capacitor | `~400ms` | 어려움 | `~150MB` | `~9%` |
| Tauri 2 Mobile | `~300ms` | 어려움 | `~110MB` | `~8%` |
| KMP + 네이티브 UI | `~120ms` | 가장 쉬움 | `~80MB` | `~5%` |
| Compose Multiplatform | `~200ms` | 쉬움 | `~110MB` | `~6%` |
| .NET MAUI | `~500ms` | 보통 | `~180MB` | `~10%` |
| Lynx | `~200ms` | 매우 쉬움 (스레드 분리) | `~95MB` | `~6%` |
네이티브에 가까운 성능은 KMP가 압도적이고, 자체 렌더러 진영(Flutter, Lynx, Compose MP)이 그 뒤를 잇는다. WebView 진영은 무거운 인터랙션에서 불리하다.
18장 · 한국 사례 — 토스의 RN→Native 마이그레이션, 카카오톡의 RN 채택
**토스(Toss)** 는 2020-2022년에 RN을 적극 사용했고, 일부 화면(이체, 카드 등)을 RN으로 짰다. 2023-2024년 토스의 발표에 따르면 일부 핵심 화면은 RN에서 네이티브로 재마이그레이션했다. 이유: 60fps 유지의 어려움 + 결제 화면의 보안 요구 + 네이티브만 가능한 위젯·딥링크. 다만 토스 증권 일부와 사내 도구는 여전히 RN을 쓴다.
**카카오톡(KakaoTalk)** 은 메시지 화면을 네이티브로 유지하지만, 카카오톡 채널·쇼핑·페이의 일부 모듈은 RN 또는 WebView로 만들어진다. 카카오엔터프라이즈는 KMP 도입 PoC를 발표했다(2024년).
**당근마켓(Karrot)** 은 RN을 일찍 도입했고, 2024년 발표에서 RN New Architecture 적용 후 스크롤 성능이 30% 향상됐다고 했다.
**네이버 라인(LINE)** 의 일본 본사는 메신저를 네이티브로 유지하지만, **LINE 만화(Manga)** 와 **LINE 뮤직(Music)** 의 일부 화면은 Flutter로 작성된다.
19장 · 일본 사례 — 메르카리, 라쿠텐, ZOZO, Mercari Flutter
**메르카리(Mercari)** 는 2018년 Flutter PoC 발표 이후 Flutter를 일부 화면에 도입했고, 2022년부터 새로운 출품(listing) 화면을 Flutter로 마이그레이션했다. 2024년 발표에서 출품 플로우의 약 70%가 Flutter라고 했다.
**라쿠텐(Rakuten)** 은 거대한 슈퍼앱 생태계를 가지고 있고, 일부 앱(라쿠텐 키오스크, 라쿠텐 페이의 일부 화면)에 Flutter를 채택했다. 단, 라쿠텐 본 앱은 네이티브를 유지한다.
**ZOZO(ZOZOTOWN)** 는 2023년 ZOZOFIT(피팅 슈트) 앱을 Flutter로 만들어 출시했다. 카메라 + 3D 모델링을 다루는 앱이지만, UI 부분은 Flutter, 코어는 네이티브 모듈로 분리했다.
**라인 만화(LINE Manga, 일본)** 는 일부 화면이 Flutter로 작성되어 있고, 콘텐츠 뷰어 자체는 네이티브를 쓴다. **CyberAgent**도 일부 자회사 앱에서 Flutter를 채택했다.
일본은 한국보다 Flutter 채택률이 높은 편이고, 메르카리의 영향으로 Flutter Tokyo 커뮤니티가 활발하다.
20장 · OTA 업데이트와 App Store 정책
iOS App Store와 Android Play Store는 "네이티브 코드를 OTA로 바꾸는 것"을 금지한다. 하지만 JS 번들과 같은 인터프리트 콘텐츠는 허용된다. 그래서 RN·Capacitor·Tauri·Lynx는 OTA 업데이트가 가능하다.
- **Expo EAS Update**: RN에서 가장 성숙. branch와 channel로 카나리 배포.
- **CodePush (Microsoft, 2024 EOL)**: 운영 종료, EAS로 마이그레이션 권장.
- **Capacitor Live Updates (Ionic)**: 유료 서비스로 OTA 제공.
- **자체 구현**: S3 + 버전 매니페스트로 직접 만들 수 있다.
OTA의 함정은 **App Store 약관**이다. 핵심 기능 변경, 결제 플로우 변경, 새 카테고리 추가는 정식 심사가 필요하다. OTA는 "버그 수정·문구 변경·작은 UI 조정" 정도로 제한해야 안전하다.
21장 · 의사결정 매트릭스 — 어떤 도구를 언제 쓰나
| 상황 | 권장 |
| --- | --- |
| 네이티브 룩앤필 + 두 OS 가이드 모두 따라야 함 | KMP + 네이티브 UI |
| 한 번 짜기 + 60fps + 풍부한 애니메이션 | Flutter |
| 웹팀 코드 재활용 + 빠른 출시 | Capacitor 또는 Tauri 2 |
| React 팀이 모바일로 가야 함 | React Native + Expo |
| 기존 .NET 백엔드 팀이 모바일로 | .NET MAUI |
| Vue/Angular 팀이 모바일로 | NativeScript 또는 Capacitor |
| Kotlin 팀이 iOS도 지원해야 함 | Compose Multiplatform |
| 라이브 커머스·미니앱 같은 ByteDance 패턴 | Lynx |
| Rust 백엔드를 모바일에서 재사용 | Tauri 2 Mobile |
| 게임 | Unity, Unreal, Godot (이 글의 범위 밖) |
"어떤 게 절대 정답이냐"의 답은 없다. 팀의 기존 기술 스택, 디자인 가이드 준수 정도, 시장 출시 속도, 60fps 요구가 종합적으로 결정한다.
22장 · 모노레포 전략 — Turborepo·Nx·Yarn Workspaces
크로스플랫폼 앱은 보통 웹과 한 저장소에 둔다. 2026년의 표준 패턴.
- **Turborepo + pnpm**: React Native + Next.js 웹 + 패키지 공유.
- **Nx**: RN, Angular, NativeScript 같이 쓰는 팀에 인기.
- **Yarn Workspaces**: 단순한 패턴, 큰 빌드 매트릭스 없을 때.
- **Melos** (Flutter용): Flutter 패키지 모노레포 전용 도구.
Turborepo 패턴
my-org/
apps/
mobile/ # Expo RN 앱
web/ # Next.js 웹
packages/
ui/ # 디자인 시스템 (RN + Web 호환)
api-client/ # 공유 API 클라이언트
config/ # ESLint·TS 설정
turbo.json
package.json
RN은 웹의 React 컴포넌트와 일부 코드를 공유할 수 있다(react-native-web). 디자인 시스템 단위에서 모노레포가 진가를 발휘한다.
23장 · CI/CD — Fastlane·Codemagic·EAS·App Center
iOS 빌드는 macOS가 필요하고, 코드 사이닝은 까다롭다. 2026년의 주요 옵션:
- **Fastlane**: 가장 오래되고 성숙. 자체 호스팅도, GitHub Actions에도 통합.
- **Codemagic**: Flutter 친화적 SaaS. macOS 빌드 머신 제공.
- **EAS Build (Expo)**: RN/Expo 특화 SaaS. iOS 인증서 자동 관리.
- **Bitrise**: iOS·Android 모두 강함. 엔터프라이즈 인기.
- **GitHub Actions + macOS runner**: 직접 짜기. 가장 자유롭지만 인증서 관리 직접.
- **App Center (Microsoft, 2025 EOL)**: 운영 종료. 다른 서비스로 마이그레이션 권장.
코드 사이닝의 함정: Apple Developer 계정(`$99/yr`), Provisioning Profile, Push Notification 인증서, 자동 갱신 미흡 → 만료되면 빌드 실패. Match(Fastlane)로 Git에 암호화 저장하는 게 표준.
24장 · 디자인 시스템·접근성·국제화
| 프레임워크 | Material Design | Cupertino (iOS look) | a11y | i18n |
| --- | --- | --- | --- | --- |
| React Native | RN Paper, Tamagui | iOS 기본 컴포넌트 | accessibilityRole | i18next, FormatJS |
| Flutter | Material 위젯 (기본) | Cupertino 위젯 (별도) | Semantics | intl 패키지 |
| Compose MP | Material 3 (기본) | Cupertino-like 라이브러리 | Compose Semantics | moko-resources |
| Capacitor | Ionic Components | Ionic iOS theming | 웹 표준 ARIA | i18next |
| Tauri 2 | 웹 자유 | 웹 자유 | 웹 표준 ARIA | i18next |
| KMP (네이티브) | Android는 그대로 | iOS는 SwiftUI 그대로 | 네이티브 도구 | Android resources / iOS Localizable.strings |
| .NET MAUI | Material 일부 | iOS 일부 | AutomationProperties | .resx 파일 |
| Lynx | 웹 호환 컴포넌트 | iOS-like 컴포넌트 | ARIA-like | i18next |
접근성은 KMP·.NET MAUI·NativeScript가 강하다(네이티브 도구 그대로 쓰니까). Capacitor·Tauri는 웹 a11y 표준을 그대로 활용한다. Flutter는 Semantics 위젯을 명시적으로 붙여야 한다.
25장 · 보안·인증·결제
모바일 앱은 보안 요구가 웹보다 까다롭다.
- **Keychain (iOS) / Keystore (Android)**: 토큰·비밀번호의 안전한 저장소.
- **생체 인증**: Face ID·Touch ID·BiometricPrompt. RN은 `react-native-keychain` + `react-native-biometrics`, Flutter는 `local_auth`.
- **인앱 결제**: Apple IAP, Google Billing. RN은 `react-native-iap`, Flutter는 `in_app_purchase`. KMP는 네이티브 코드를 직접 호출.
- **푸시 알림**: FCM (Android), APNs (iOS). Expo Notifications, Flutter Firebase Messaging.
- **앱 무결성**: iOS DeviceCheck, Android Play Integrity. 루팅·탈옥 탐지.
결제 화면을 RN으로 만든 팀이 종종 후회하는 이유는, Apple/Google이 IAP 외 결제를 까다롭게 보고, RN 화면의 보안 감사가 네이티브보다 어렵기 때문이다. 토스가 일부 화면을 네이티브로 되돌린 이유 중 하나.
26장 · 에필로그 — 2027년의 풍경
크로스플랫폼은 더 이상 "한 번 짜서 둘 다"의 단일 약속이 아니다. 2027년에 일어날 세 변화:
1. **AI 코드 생성이 두 플랫폼 코드의 비용을 줄인다.** Cursor·Claude Code가 SwiftUI와 Compose를 동시에 생성하는 것이 흔해지면, "공유 코드"의 가치가 일부 침식된다. 그래도 비즈니스 로직 공유(KMP)는 여전히 매력적이다.
2. **자체 렌더러 진영의 확산.** Flutter Impeller가 표준이 되고, Lynx가 라이브 커머스에서 점유율을 키우며, Compose Multiplatform이 iOS에서 안정 단계로 정착한다.
3. **WebView 진영의 르네상스.** Capacitor 7과 Tauri 2 Mobile은 시스템 WebView의 진화(WebKit 17, Android WebView 130+)와 함께 점유율을 키운다. PWA를 모바일 앱으로 포팅하는 비용이 가장 낮다.
가장 큰 교훈은 단순하다. **모든 화면을 같은 도구로 짤 필요는 없다.** 토스가 결제 화면은 네이티브, 사내 도구는 RN을 쓰는 것처럼, 한 회사 안에서도 화면별로 적절한 도구를 고르는 게 2026년의 정답이다.
References
- React Native documentation — New Architecture. https://reactnative.dev/architecture/landing-page
- React Native New Architecture migration guide. https://reactnative.dev/docs/new-architecture-intro
- Hermes documentation. https://hermesengine.dev
- Flutter documentation. https://flutter.dev
- Flutter Impeller renderer overview. https://docs.flutter.dev/perf/impeller
- Expo documentation. https://docs.expo.dev
- Expo Router. https://docs.expo.dev/router/introduction/
- EAS Build and Update. https://docs.expo.dev/eas/
- Capacitor documentation. https://capacitorjs.com/docs
- Ionic Framework. https://ionicframework.com/docs
- Tauri 2 documentation. https://tauri.app/start/
- Tauri 2 mobile development. https://tauri.app/develop/
- Kotlin Multiplatform official. https://kotlinlang.org/lp/multiplatform/
- Compose Multiplatform. https://www.jetbrains.com/compose-multiplatform/
- .NET MAUI documentation. https://learn.microsoft.com/dotnet/maui/
- NativeScript documentation. https://docs.nativescript.org
- Lynx (ByteDance) — Lynx Framework. https://lynxjs.org
- Mercari Engineering — Flutter adoption. https://engineering.mercari.com
- Toss Tech blog — React Native experience. https://toss.tech
- LINE Engineering Blog — mobile platform stories. https://engineering.linecorp.com
현재 단락 (1/406)
2015년 React Native가 처음 등장했을 때, "한 번 짜서 iOS·Android 둘 다 돌린다"는 약속은 거대했다. Facebook이 직접 만들고, JSX로 짜고, Ja...