Flutter란 무엇인가
크로스플랫폼의 새로운 패러다임
Flutter는 Google이 2018년 정식 출시한 오픈소스 UI 프레임워크입니다. 하나의 Dart 코드베이스로 iOS, Android, Web, Windows, macOS, Linux 총 6개 플랫폼의 앱을 빌드할 수 있습니다.
2025년 기준 주요 지표:
- GitHub 스타: 165,000+
- 전 세계 개발자: 300만+ (Google 발표)
- Google Play 상위 1,000개 앱 중 Flutter 사용: 약 30%
- 국내 채용 공고에서 Flutter 언급 비율: 2021년 대비 3배 이상 증가
Flutter의 핵심 차별점
기존 크로스플랫폼 프레임워크(React Native, Xamarin 등)와의 결정적 차이는 렌더링 방식입니다.
React Native: JavaScript → Bridge → 네이티브 위젯. 네이티브 UI 컴포넌트를 사용하므로 플랫폼 간 미세한 차이 존재
Flutter: Dart → Skia/Impeller 엔진 → 직접 GPU 렌더링. OS의 네이티브 위젯을 사용하지 않고 자체 렌더링 엔진으로 모든 픽셀을 직접 그림
이로 인한 장점:
- 완벽한 UI 일관성: iOS와 Android에서 동일한 외관
- 60/120fps 성능: 자체 렌더링으로 브릿지 오버헤드 없음
- 디자인 자유도: OS 위젯에 구속받지 않는 커스텀 UI 가능
---
Dart 언어의 특징
왜 Dart인가
Flutter가 JavaScript나 Kotlin이 아닌 Dart를 선택한 이유:
- AOT(Ahead-of-Time) 컴파일: 릴리스 빌드에서 네이티브 ARM 코드로 컴파일 → 빠른 시작 속도
- JIT(Just-in-Time) 컴파일: 개발 중 Hot Reload 지원 → 코드 변경 즉시 반영 (1초 미만)
- Null Safety: Dart 2.12부터 사운드 널 안전성 도입. 컴파일 타임에 NPE 방지
- 단일 스레드 + Isolate: 메인 스레드 블로킹 없는 비동기 프로그래밍 모델
Dart 핵심 문법 (5분 속성)
```dart // 변수 선언 final name = '얼웨이즈'; // 불변 (런타임 상수) const pi = 3.14159; // 불변 (컴파일타임 상수) var count = 0; // 가변
// Null Safety String? nullable; // null 가능 String nonNull = 'hello'; // null 불가
// 클래스 class Pet { final String name; final int age;
const Pet({required this.name, required this.age});
String get description => '$name, $age세'; }
// 비동기 Future<String> fetchData() async { final response = await http.get(Uri.parse('https://api.example.com')); return response.body; } ```
---
위젯 시스템 — "Everything is a Widget"
위젯 트리의 이해
Flutter에서 화면의 모든 요소는 위젯(Widget)입니다. 텍스트, 버튼, 패딩, 레이아웃, 심지어 앱 자체도 위젯입니다. 위젯은 트리 구조로 조합되어 UI를 구성합니다.
위젯의 두 가지 유형:
- StatelessWidget: 불변 상태. 한 번 빌드되면 변경 불가. 정적 UI에 적합
- StatefulWidget: 가변 상태. `setState()`로 상태 변경 시 UI 재빌드
핵심 레이아웃 위젯
| 위젯 | 역할 | HTML/CSS 대응 | |------|------|-------------| | Container | 박스 모델 (패딩, 마진, 배경) | `<div>` + CSS box | | Row | 수평 배치 | `display: flex; direction: row` | | Column | 수직 배치 | `display: flex; direction: column` | | Stack | 겹치기 배치 | `position: absolute` | | ListView | 스크롤 목록 | 스크롤 컨테이너 | | Expanded | 남은 공간 채우기 | `flex: 1` | | SizedBox | 고정 크기 / 간격 | `width/height` |
Material 3 디자인
Flutter 3.x부터 Material 3 (Material You)가 기본 디자인 시스템입니다. `ColorScheme.fromSeed()`로 씨드 색상 하나만 지정하면 조화로운 색상 팔레트가 자동 생성됩니다.
```dart MaterialApp( theme: ThemeData( colorSchemeSeed: Colors.blue, useMaterial3: true, ), ); ```
---
상태 관리 — 가장 중요한 아키텍처 결정
상태 관리가 왜 중요한가
앱의 규모가 커질수록 "어떤 데이터가, 어디에 저장되고, 어떻게 변경되며, 누가 관찰하는가"가 복잡해집니다. Flutter 커뮤니티에서 가장 활발히 논의되는 주제이며, 여러 접근법이 있습니다.
주요 상태 관리 솔루션 비교
Provider: Flutter 팀이 추천하는 기본 솔루션. InheritedWidget의 래퍼. 단순한 앱에 적합
Riverpod: Provider의 창시자(Remi Rousselet)가 만든 차세대 솔루션. Provider의 한계(BuildContext 의존, 런타임 에러) 해결. 컴파일 타임 안전성, 테스트 용이성이 강점. 2025년 기준 가장 빠르게 성장하는 솔루션
Bloc/Cubit: 이벤트 기반 상태 관리. 대규모 팀 프로젝트에서 일관된 패턴을 강제. 보일러플레이트가 많지만 예측 가능성이 높음
GetX: 최소 보일러플레이트. 빠른 프로토타이핑에 유리하나, 마법 같은 동작이 많아 디버깅과 테스트에 불리. 프로덕션에서는 논란
Riverpod 기본 예시
```dart // Provider 선언 final counterProvider = StateNotifierProvider<CounterNotifier, int>((ref) { return CounterNotifier(); });
class CounterNotifier extends StateNotifier<int> { CounterNotifier() : super(0); void increment() => state++; }
// Widget에서 사용 class CounterPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final count = ref.watch(counterProvider); return Text('Count: $count'); } } ```
---
프로젝트 구조 — 확장 가능한 아키텍처
Feature-First 구조 (권장)
``` lib/ ├── app/ # 앱 진입점, 라우팅 ├── core/ # 공통 유틸, 상수, 테마 │ ├── theme/ │ ├── utils/ │ └── constants/ ├── features/ # 기능별 모듈 │ ├── auth/ │ │ ├── data/ # Repository, API │ │ ├── domain/ # Model, Entity │ │ └── presentation/ # Screen, Widget │ ├── home/ │ └── profile/ ├── shared/ # 공유 위젯, 서비스 │ ├── widgets/ │ └── services/ └── main.dart ```
이 구조의 장점:
- 기능 추가/삭제가 독립적
- 팀원 간 충돌 최소화
- 테스트 경계가 명확
---
성능 최적화 핵심 전략
1. const 생성자 적극 활용
`const` 위젯은 빌드 시점에 인스턴스가 재사용되어 불필요한 리빌드를 방지합니다. Flutter 린트 규칙(`prefer_const_constructors`)을 활성화하세요.
2. 리빌드 범위 최소화
`setState()`를 최상위 위젯에서 호출하면 전체 서브트리가 리빌드됩니다. 상태가 변하는 부분만 별도 위젯으로 분리하여 리빌드 범위를 줄이세요.
3. ListView.builder 사용
`ListView(children: [...])`는 모든 아이템을 한 번에 생성하지만, `ListView.builder`는 보이는 아이템만 지연 생성(lazy rendering)합니다. 100개 이상의 목록에서는 성능 차이가 확연합니다.
4. 이미지 최적화
- `cached_network_image` 패키지로 네트워크 이미지 캐싱
- 적절한 `width`/`height` 지정으로 불필요한 디코딩 방지
- WebP 포맷 사용으로 파일 크기 30~50% 절감
5. Impeller 렌더링 엔진
Flutter 3.16+부터 iOS에서 Impeller가 기본 렌더링 엔진. Skia 대비 셰이더 컴파일 지연(jank) 문제를 근본적으로 해결. Android에서도 점진적으로 기본 엔진이 되고 있습니다.
---
Flutter vs React Native vs 네이티브 — 선택 가이드
| 기준 | Flutter | React Native | 네이티브(Swift/Kotlin) | |------|---------|-------------|---------------------| | 학습 곡선 | 중간 (Dart 학습 필요) | 낮음 (JS/TS 활용) | 높음 (2개 언어) | | 성능 | 네이티브에 근접 | 약간 낮음 (Bridge) | 최고 | | UI 일관성 | 최고 (자체 렌더링) | 플랫폼 의존 | 플랫폼 최적화 | | 생태계 | 빠르게 성장 | 성숙 | 가장 풍부 | | Hot Reload | 매우 빠름 | 빠름 | Xcode만 일부 지원 | | 팀 규모 | 1개 팀 | 1개 팀 | 최소 2개 팀 | | 적합한 경우 | 커스텀 UI 앱 | 웹 개발자의 모바일 전환 | 하드웨어 밀착 앱 |
Flutter를 선택해야 하는 경우
- 디자인 일관성이 중요한 브랜드 앱
- 빠른 MVP 출시 + 이후 멀티 플랫폼 확장
- 소규모 팀(1~5명)으로 iOS/Android 동시 개발
- 게임, 애니메이션, 커스텀 UI가 많은 앱
네이티브를 선택해야 하는 경우
- AR/VR, 카메라, 블루투스 등 하드웨어 깊은 통합 필요
- 플랫폼별 최적 UX가 핵심인 앱 (예: iOS 감성)
- 전담 iOS/Android 팀이 이미 있는 대규모 조직
Flutter는 "80/20 법칙"의 앱 개발 버전입니다. 전체 앱의 80%에 해당하는 유스케이스를 하나의 코드베이스로 효율적으로 해결합니다. 나머지 20% — 극단적 성능이나 깊은 네이티브 통합이 필요한 경우 — 에서는 플랫폼 채널(Platform Channel)로 네이티브 코드를 호출할 수 있어, 필요할 때만 네이티브로 내려가는 유연한 접근이 가능합니다.