Flutter Web과 데스크톱 — 멀티플랫폼 빌드 전략
Flutter Web과 데스크톱 — 멀티플랫폼 빌드 전략
Flutter의 가장 큰 장점은 하나의 코드베이스로 모바일, 웹, 데스크톱을 모두 지원한다는 것입니다. 각 플랫폼의 특성과 빌드 전략을 정리해보겠습니다.
지원 플랫폼
| 플랫폼 | 안정성 | 렌더러 |
|---|---|---|
| Android | Stable | Skia/Impeller |
| iOS | Stable | Impeller |
| Web | Stable | CanvasKit/HTML |
| Windows | Stable | Skia |
| macOS | Stable | Skia |
| Linux | Stable | Skia |
Flutter Web
빌드
# 웹 빌드
flutter build web
# 렌더러 지정
flutter build web --web-renderer canvaskit # 고품질 (기본)
flutter build web --web-renderer html # 가벼움, 텍스트 SEO
flutter build web --web-renderer auto # 모바일은 html, 데스크톱은 canvaskit
렌더러 비교
| 항목 | CanvasKit | HTML |
|---|---|---|
| 렌더링 품질 | 높음 (Skia) | 브라우저 의존 |
| 초기 로딩 크기 | ~2MB | ~400KB |
| 텍스트 선택 | 제한적 | 네이티브 |
| SEO | 제한적 | 더 나음 |
| 성능 | 일관적 | 가변적 |
웹 전용 코드
import 'package:flutter/foundation.dart' show kIsWeb;
Widget build(BuildContext context) {
if (kIsWeb) {
return const WebSpecificWidget();
}
return const MobileWidget();
}
URL 전략
// main.dart
import 'package:flutter_web_plugins/url_strategy.dart';
void main() {
// URL에서 # 제거 (example.com/page 대신 example.com/#/page)
usePathUrlStrategy();
runApp(const MyApp());
}
Flutter 데스크톱
설정
# 플랫폼 지원 활성화
flutter config --enable-windows-desktop
flutter config --enable-macos-desktop
flutter config --enable-linux-desktop
# 기존 프로젝트에 데스크톱 지원 추가
flutter create --platforms=windows,macos,linux .
빌드
# Windows 빌드
flutter build windows
# macOS 빌드
flutter build macos
# Linux 빌드
flutter build linux
데스크톱 전용 고려사항
import 'dart:io' show Platform;
// 플랫폼별 분기
if (Platform.isWindows) {
// Windows 전용
} else if (Platform.isMacOS) {
// macOS 전용
} else if (Platform.isLinux) {
// Linux 전용
}
창 크기 관리 (데스크톱)
dependencies:
window_manager: ^0.4.0
import 'package:window_manager/window_manager.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await windowManager.ensureInitialized();
WindowOptions windowOptions = const WindowOptions(
size: Size(1200, 800),
minimumSize: Size(800, 600),
center: true,
backgroundColor: Colors.transparent,
title: 'My Desktop App',
titleBarStyle: TitleBarStyle.hidden,
);
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
});
runApp(const MyApp());
}
멀티플랫폼 UI 전략
반응형 레이아웃
class AdaptiveLayout extends StatelessWidget {
const AdaptiveLayout({super.key});
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 1200) {
return const DesktopLayout();
} else if (constraints.maxWidth > 600) {
return const TabletLayout();
} else {
return const MobileLayout();
}
},
);
}
}
플랫폼별 입력 처리
// 데스크톱: 키보드 단축키
Shortcuts(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyS):
const SaveIntent(),
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyZ):
const UndoIntent(),
},
child: Actions(
actions: {
SaveIntent: CallbackAction<SaveIntent>(
onInvoke: (_) => _save(),
),
},
child: const MyApp(),
),
)
// 데스크톱: 마우스 호버
MouseRegion(
onEnter: (_) => setState(() => _isHovered = true),
onExit: (_) => setState(() => _isHovered = false),
cursor: SystemMouseCursors.click,
child: Container(
color: _isHovered ? Colors.blue.shade100 : Colors.transparent,
child: const Text('호버 효과'),
),
)
// 데스크톱: 우클릭 컨텍스트 메뉴
GestureDetector(
onSecondaryTapUp: (details) {
_showContextMenu(context, details.globalPosition);
},
child: const ListTile(title: Text('우클릭하세요')),
)
플랫폼별 패키지 사용
// 조건부 import
import 'stub_impl.dart'
if (dart.library.io) 'io_impl.dart'
if (dart.library.html) 'web_impl.dart';
// 또는 패키지 수준에서 분기
// 일부 패키지는 웹에서 동작하지 않음
// sqflite → 웹 불가 (대안: drift/web)
// path_provider → 웹 제한적
// shared_preferences → 웹 가능 (localStorage 사용)
웹 SEO와 성능
// 메타 태그 설정 (web/index.html)
// <meta name="description" content="앱 설명">
// 초기 로딩 최적화
// web/index.html에서 스플래시 표시
<!-- web/index.html 로딩 표시 -->
<style>
.loading {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
</style>
<body>
<div class="loading" id="loading">
<p>로딩 중...</p>
</div>
<script>
window.addEventListener('flutter-first-frame', function() {
document.getElementById('loading').style.display = 'none';
});
</script>
</body>
빌드 전략
| 전략 | 설명 | 적합한 경우 |
|---|---|---|
| 단일 코드베이스 | 모든 플랫폼에서 동일 코드 | UI가 단순한 앱 |
| 적응형 UI | 플랫폼별 레이아웃 분기 | 대부분의 앱 (권장) |
| 별도 프로젝트 | 플랫폼별 완전 분리 | 플랫폼별 경험이 매우 다른 경우 |
면접 포인트: "Flutter Web의 한계는?"이라는 질문에 SEO 제한(CanvasKit), 초기 로딩 크기, 브라우저 API 접근 제한을 답할 수 있어야 합니다. 콘텐츠 중심 웹사이트보다는 웹 앱(SPA) 에 적합합니다.
정리
- Flutter는 모바일, 웹, 데스크톱을 하나의 코드베이스로 지원합니다
- 웹 렌더러는 CanvasKit(고품질)과 HTML(가벼움) 중 선택할 수 있습니다
- 데스크톱에서는 창 크기 관리, 키보드 단축키, 마우스 호버 등을 고려해야 합니다
LayoutBuilder로 화면 크기에 따른 적응형 레이아웃을 구현하세요- Flutter Web은 콘텐츠 사이트보다 웹 앱에 더 적합합니다
- 플랫폼별 패키지 호환성을 확인하고, 조건부 import로 분기하세요
댓글 로딩 중...