Flutter 튜토리얼 55편: 플랫폼 통합 기초

Flutter 플랫폼 통합 기초#

Flutter는 단일 코드베이스로 여러 플랫폼에 앱을 배포할 수 있습니다. 이 튜토리얼에서는 Flutter가 지원하는 플랫폼과 네이티브 코드를 통합하는 기본 방법을 배웁니다.

학습 목표#

  • Flutter가 지원하는 플랫폼 이해하기
  • 데스크톱 앱 개발 환경 설정하기
  • FFI로 네이티브 코드 호출하기
  • 플랫폼별 고려사항 파악하기

1. 지원 플랫폼 개요#

Why: 플랫폼별 특성을 알아야 하는 이유#

각 플랫폼은 고유한 특성과 요구사항이 있습니다. 이를 이해하면 최적화된 앱을 개발할 수 있습니다.

What: Flutter가 지원하는 플랫폼#

flowchart TD A[Flutter] --> B[모바일] A --> C[데스크톱] A --> D[웹] B --> B1[Android] B --> B2[iOS] C --> C1[Windows] C --> C2[macOS] C --> C3[Linux] D --> D1[Chrome] D --> D2[Firefox] D --> D3[Safari] D --> D4[Edge]

What: 모바일 플랫폼 지원#

플랫폼지원 버전아키텍처
AndroidAPI 21+ (Android 5.0)arm64-v8a, armeabi-v7a, x86_64
iOSiOS 12+arm64

What: 데스크톱 플랫폼 지원#

플랫폼지원 버전아키텍처
WindowsWindows 10+x64, arm64
macOSCatalina (10.15)+x64, arm64
LinuxDebian, Ubuntu 등x64

What: 웹 플랫폼 지원#

브라우저지원
Chrome✅ 완전 지원
Firefox✅ 완전 지원
Safari✅ 완전 지원
Edge✅ 완전 지원

How: 플랫폼별 프로젝트 생성#

Terminal window
# 모든 플랫폼 지원 프로젝트 생성
flutter create my_app
# 특정 플랫폼만 지원
flutter create --platforms=android,ios,web my_app
# 기존 프로젝트에 플랫폼 추가
flutter create --platforms=windows,macos,linux .

Watch out: 주의사항#

모든 패키지가 모든 플랫폼을 지원하는 것은 아닙니다. pub.dev에서 패키지의 플랫폼 호환성을 확인하세요.


2. 데스크톱 개발 환경 설정#

Why: 데스크톱 앱이 필요한 이유#

데스크톱 앱은 모바일보다 더 많은 리소스와 화면 공간을 활용할 수 있습니다. 생산성 도구, 전문 소프트웨어 등에 적합합니다.

What: 플랫폼별 필수 도구#

flowchart TD A[데스크톱 개발] --> B[Windows] A --> C[macOS] A --> D[Linux] B --> B1[Visual Studio 2022] B --> B2["Windows 10 SDK"] C --> C1[Xcode] C --> C2[CocoaPods] D --> D1[clang] D --> D2[cmake] D --> D3[GTK 3]

How: Windows 개발 환경#

Terminal window
# 필수 도구 설치
# 1. Visual Studio 2022 with "Desktop development with C++" 워크로드
# 2. Windows 10 SDK
# Flutter 설정 확인
flutter doctor
# Windows 앱 실행
flutter run -d windows

How: macOS 개발 환경#

Terminal window
# Xcode 설치 (App Store에서)
# Xcode Command Line Tools 설치
xcode-select --install
# CocoaPods 설치
sudo gem install cocoapods
# Flutter 설정 확인
flutter doctor
# macOS 앱 실행
flutter run -d macos

How: Linux 개발 환경#

Terminal window
# Ubuntu/Debian 필수 패키지 설치
sudo apt-get update
sudo apt-get install clang cmake ninja-build pkg-config \
libgtk-3-dev liblzma-dev libstdc++-12-dev
# Flutter 설정 확인
flutter doctor
# Linux 앱 실행
flutter run -d linux

How: 데스크톱 앱 빌드#

Terminal window
# Windows 빌드
flutter build windows
# macOS 빌드
flutter build macos
# Linux 빌드
flutter build linux

빌드 결과물 위치:

플랫폼위치
Windowsbuild/windows/x64/runner/Release/
macOSbuild/macos/Build/Products/Release/
Linuxbuild/linux/x64/release/bundle/

Watch out: 주의사항#

데스크톱 앱은 화면 크기가 다양합니다. 반응형 레이아웃을 구현하여 다양한 윈도우 크기에 대응하세요.


3. FFI 기초 (Foreign Function Interface)#

Why: FFI가 필요한 이유#

Flutter만으로 구현하기 어려운 기능이 있습니다. FFI1를 사용하면 C/C++ 등의 네이티브 라이브러리를 직접 호출할 수 있습니다.

What: FFI vs Platform Channels#

flowchart TD A[네이티브 코드 통합] --> B[FFI] A --> C[Platform Channels] B --> B1[직접 C 호출] B --> B2[빠른 성능] B --> B3[dart:ffi 사용] C --> C1[플랫폼 API 호출] C --> C2[비동기 메시지] C --> C3[MethodChannel 사용]
방식장점단점적합한 경우
FFI빠름, 직접 호출플랫폼별 빌드 필요C 라이브러리 사용
Platform Channels유연함, 플랫폼 API 접근오버헤드 있음플랫폼 기능 호출

What: FFI 지원 플랫폼#

플랫폼FFI 지원
Android
iOS
Windows
macOS
Linux
Web❌ (WASM 별도)

How: FFI 패키지 프로젝트 생성#

Terminal window
# FFI 템플릿으로 프로젝트 생성
flutter create --template=package_ffi native_add

생성되는 구조:

native_add/
├── lib/
│ └── native_add.dart # Dart API
├── src/
│ └── native_add.c # C 소스 코드
├── hook/
│ └── build.dart # 빌드 훅
└── pubspec.yaml

How: C 코드 작성#

src/native_add.c
#include <stdint.h>
// FFI_PLUGIN_EXPORT는 함수를 외부에 노출합니다
#if _WIN32
#define FFI_PLUGIN_EXPORT __declspec(dllexport)
#else
#define FFI_PLUGIN_EXPORT
#endif
// 간단한 덧셈 함수
FFI_PLUGIN_EXPORT int32_t native_add(int32_t a, int32_t b) {
return a + b;
}
// 문자열 반환 예제
FFI_PLUGIN_EXPORT const char* get_platform_name() {
#if _WIN32
return "Windows";
#elif __APPLE__
return "macOS";
#elif __linux__
return "Linux";
#elif __ANDROID__
return "Android";
#else
return "Unknown";
#endif
}

How: Dart 바인딩 작성#

lib/native_add.dart
import 'dart:ffi';
import 'dart:io';
import 'package:ffi/ffi.dart';
// 네이티브 함수 시그니처 정의
typedef NativeAddFunc = Int32 Function(Int32 a, Int32 b);
typedef NativeAdd = int Function(int a, int b);
typedef GetPlatformNameFunc = Pointer<Utf8> Function();
typedef GetPlatformName = Pointer<Utf8> Function();
class NativeAddBindings {
late final DynamicLibrary _lib;
late final NativeAdd _nativeAdd;
late final GetPlatformName _getPlatformName;
NativeAddBindings() {
_lib = _loadLibrary();
_nativeAdd = _lib
.lookupFunction<NativeAddFunc, NativeAdd>('native_add');
_getPlatformName = _lib
.lookupFunction<GetPlatformNameFunc, GetPlatformName>('get_platform_name');
}
DynamicLibrary _loadLibrary() {
if (Platform.isAndroid) {
return DynamicLibrary.open('libnative_add.so');
} else if (Platform.isIOS) {
return DynamicLibrary.process();
} else if (Platform.isWindows) {
return DynamicLibrary.open('native_add.dll');
} else if (Platform.isMacOS) {
return DynamicLibrary.open('libnative_add.dylib');
} else if (Platform.isLinux) {
return DynamicLibrary.open('libnative_add.so');
}
throw UnsupportedError('현재 플랫폼은 지원하지 않습니다');
}
int add(int a, int b) => _nativeAdd(a, b);
String getPlatformName() {
final ptr = _getPlatformName();
return ptr.toDartString();
}
}

Watch out: 주의사항#

FFI는 메모리를 직접 다루므로 주의가 필요합니다. 잘못된 포인터 사용은 앱 크래시를 유발할 수 있습니다.


4. 빌드 훅 시스템#

Why: 빌드 훅이 필요한 이유#

네이티브 코드는 플랫폼마다 다르게 빌드해야 합니다. 빌드 훅2을 사용하면 이 과정을 자동화할 수 있습니다.

What: 빌드 훅의 역할#

flowchart TD A[flutter build] --> B[hook/build.dart 실행] B --> C{플랫폼 확인} C -->|Android| D[NDK로 빌드] C -->|iOS| E[clang으로 빌드] C -->|Windows| F[MSVC로 빌드] C -->|macOS| G[clang으로 빌드] C -->|Linux| H[GCC로 빌드] D --> I[네이티브 라이브러리] E --> I F --> I G --> I H --> I

How: 빌드 훅 작성#

hook/build.dart
import 'package:native_assets_cli/native_assets_cli.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';
void main(List<String> args) async {
await build(args, (config, output) async {
final packageName = config.packageName;
final cBuilder = CBuilder.library(
name: packageName,
assetName: '${packageName}_bindings_generated.dart',
sources: [
'src/$packageName.c',
],
);
await cBuilder.run(
config: config,
output: output,
logger: Logger('')..level = Level.ALL,
);
});
}

How: pubspec.yaml 설정#

name: native_add
description: A native add package using FFI.
version: 1.0.0
environment:
sdk: ">=3.0.0 <4.0.0"
flutter: ">=3.0.0"
dependencies:
ffi: ^2.0.0
dev_dependencies:
ffigen: ^9.0.0
native_assets_cli: ^0.5.0
native_toolchain_c: ^0.5.0

Watch out: 주의사항#

빌드 훅은 Flutter 3.16 이상에서 사용할 수 있습니다. 이전 버전에서는 수동으로 네이티브 코드를 빌드해야 합니다.


5. 플랫폼별 고려사항#

Why: 플랫폼마다 다른 점을 알아야 하는 이유#

각 플랫폼은 고유한 UI 가이드라인과 사용자 기대가 있습니다. 이를 무시하면 어색한 사용자 경험을 제공하게 됩니다.

What: 플랫폼별 UI 특성#

플랫폼내비게이션스크롤텍스트 선택
Android뒤로가기 버튼물리 기반드래그 핸들
iOS스와이프 백바운스커서 + 루페
데스크톱메뉴바마우스휠마우스 드래그
브라우저 네비게이션네이티브네이티브

How: 플랫폼 감지#

import 'dart:io';
import 'package:flutter/foundation.dart';
// 플랫폼 확인
if (Platform.isAndroid) {
// Android 전용 로직
}
if (Platform.isIOS) {
// iOS 전용 로직
}
if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
// 데스크톱 전용 로직
}
// 웹 확인 (dart:io 사용 불가)
if (kIsWeb) {
// 웹 전용 로직
}

How: 플랫폼별 UI 적용#

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class AdaptiveButton extends StatelessWidget {
final VoidCallback onPressed;
final Widget child;
const AdaptiveButton({
super.key,
required this.onPressed,
required this.child,
});
@override
Widget build(BuildContext context) {
final platform = Theme.of(context).platform;
if (platform == TargetPlatform.iOS ||
platform == TargetPlatform.macOS) {
return CupertinoButton(
onPressed: onPressed,
child: child,
);
}
return ElevatedButton(
onPressed: onPressed,
child: child,
);
}
}

How: 플랫폼별 네비게이션#

import 'package:flutter/material.dart';
class AdaptiveScaffold extends StatelessWidget {
final Widget body;
final String title;
const AdaptiveScaffold({
super.key,
required this.body,
required this.title,
});
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width > 600;
if (isDesktop) {
return Scaffold(
body: Row(
children: [
NavigationRail(
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home),
label: Text('홈'),
),
NavigationRailDestination(
icon: Icon(Icons.settings),
label: Text('설정'),
),
],
selectedIndex: 0,
onDestinationSelected: (index) {},
),
Expanded(child: body),
],
),
);
}
return Scaffold(
appBar: AppBar(title: Text(title)),
body: body,
bottomNavigationBar: NavigationBar(
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: '홈',
),
NavigationDestination(
icon: Icon(Icons.settings),
label: '설정',
),
],
selectedIndex: 0,
onDestinationSelected: (index) {},
),
);
}
}

Watch out: 주의사항#

dart:io는 웹에서 사용할 수 없습니다. 웹을 지원하는 앱에서는 kIsWeb으로 먼저 확인한 후 Platform 클래스를 사용하세요.


6. 플랫폼 통합 체크리스트#

환경 설정#

  • 타겟 플랫폼별 개발 도구 설치
  • flutter doctor로 환경 확인
  • 플랫폼별 시뮬레이터/에뮬레이터 설정

FFI 사용 시#

  • C 코드 작성 및 테스트
  • 플랫폼별 빌드 설정
  • Dart 바인딩 작성
  • 메모리 관리 확인

배포 준비#

  • 플랫폼별 UI 테스트
  • 성능 테스트 (각 플랫폼에서)
  • 패키지 호환성 확인
  • 플랫폼별 빌드 및 서명

마무리#

이번 튜토리얼에서는 Flutter가 지원하는 플랫폼과 네이티브 코드를 통합하는 기본 방법을 배웠습니다.

핵심 정리#

주제핵심 내용
지원 플랫폼Android, iOS, Windows, macOS, Linux, Web
데스크톱 개발플랫폼별 개발 도구 필요
FFIdart C 코드 직접 호출
빌드 훅네이티브 코드 자동 빌드
플랫폼 적응Platform, kIsWeb으로 분기

다음 단계#


참고 자료#

Footnotes#

  1. FFI(Foreign Function Interface)는 한 프로그래밍 언어에서 다른 언어로 작성된 코드를 호출하는 메커니즘입니다.

  2. 빌드 훅(Build Hook)은 빌드 과정에서 자동으로 실행되는 스크립트입니다. 네이티브 코드 컴파일에 사용됩니다.

공유

이 글이 도움이 되었다면 다른 사람과 공유해주세요!

Flutter 튜토리얼 55편: 플랫폼 통합 기초
https://moodturnpost.net/posts/flutter/flutter-platform-basics/
작성자
Moodturn
게시일
2026-01-08
Moodturn

목차