멋진 앱을 만들기 위해서는 이미지, 폰트, JSON 파일 등 다양한 리소스가 필요한데요.
이러한 외부 자원들을 효율적으로 관리하고 활용하는 방법을 자세히 살펴보겠습니다.
Assets은?
Flutter에서 Assets은 앱에 포함되는 정적 파일들을 의미합니다. 보통 이미지(PNG, JPG, GIF, WebP, SVG 등), 폰트 파일(TTF, OTF), 텍스트 파일, JSON 데이터 파일, 오디오 파일, 비디오 파일, 기타 정적 리소스들 등이 이에 포함됩니다.
그럼 이제 Flutter에서 Assets를 설정하고 사용하는 방법을 단계별로 알아보겠습니다.
1. pubspec.yaml 파일에 Assets 등록하기
Flutter 앱에서 Assets를 사용하기 위한 첫 번째 단계는 pubspec.yaml
파일에 등록하는 것입니다. 이 파일은 보통 앱의 메타데이터와 종속성을 관리하는 중요한 설정 파일입니다.
flutter:
assets:
- assets/images/
- assets/icons/
- assets/data/config.json
위 예시에서는 다음과 같은 설정을 했습니다:
assets/images/
디렉토리의 모든 파일을 에셋으로 등록assets/icons/
디렉토리의 모든 파일을 에셋으로 등록assets/data/config.json
특정 파일만 등록
디렉토리를 지정하면 해당 디렉토리의 모든 파일이 자동으로 등록되어 편리합니다. 단, 하위 디렉토리는 포함되지 않습니다. 하위 디렉토리까지 모두 포함하려면 각각 명시해야 합니다.
아마, 안드로이드 개발 경험이 있는 분들은 res/drawable과 비슷한 개념이고, iOS 개발자에게는 Assets.xcassets와 유사하다고 생각하시면 됩니다. 웹 개발에서는 static 폴더와 비슷한 개념이라고 생각하시면 됩니다.
2. 프로젝트 구조 설정하기
효율적인 Assets 관리를 위해 일관된 디렉토리 구조를 만드는 것이 좋습니다. 일반적으로는 아래와 같은 트리 구조를 같습니다.
my_app/
├── lib/
│ └── main.dart
├── assets/
│ ├── images/
│ │ ├── logo.png
│ │ ├── background.jpg
│ │ └── profile_pic.png
│ ├── icons/
│ │ ├── home.png
│ │ └── settings.png
│ ├── fonts/
│ │ ├── Roboto-Regular.ttf
│ │ └── Roboto-Bold.ttf
│ └── data/
│ └── config.json
└── pubspec.yaml
이렇게 구조화하면 나중에 프로젝트가 커져도 관리하기 쉽습니다.
3. 이미지 Assets 사용하기
기본 이미지 로드
Flutter에서 이미지를 표시하는 기본적인 방법은 Image.asset()
위젯을 사용하는 것입니다:
Image.asset(
'assets/images/logo.png',
width: 200,
height: 100,
)
해상도별 이미지 관리 (1x, 2x, 3x)
다양한 해상도의 기기를 지원하기 위해 Flutter는 안드로이드의 density buckets이나 iOS의 @2x, @3x 시스템과 유사한 해상도별 이미지 관리를 지원합니다.
assets/
├── images/
│ ├── logo.png # 1x (베이스) 해상도
│ ├── 2.0x/
│ │ └── logo.png # 2x 해상도
│ └── 3.0x/
│ └── logo.png # 3x 해상도
이렇게 구성하면 Flutter가 자동으로 기기 해상도에 맞는 이미지를 선택합니다. Image.asset('assets/images/logo.png')
처럼 베이스 경로만 지정하면 됩니다.
BoxFit 속성으로 이미지 배치 조절하기
이미지의 크기와 비율을 조절하는 BoxFit
속성은 웹 개발의 CSS object-fit
과 유사합니다.
Image.asset(
'assets/images/background.jpg',
width: double.infinity,
height: 200,
fit: BoxFit.cover, // 컨테이너를 꽉 채우며 비율 유지
)
주요 BoxFit 값들:
BoxFit.fill
: 비율 무시하고 컨테이너 크기에 맞춤BoxFit.contain
: 비율 유지하며 컨테이너 안에 전체 이미지 표시BoxFit.cover
: 비율 유지하며 컨테이너를 꽉 채움 (일부 잘릴 수 있음)BoxFit.fitWidth
: 너비에 맞추고 비율 유지BoxFit.fitHeight
: 높이에 맞추고 비율 유지
네트워크 이미지 캐싱
로컬 Assets 외에도 네트워크 이미지도 많이 사용됩니다. 네트워크 이미지 캐싱을 위해 cached_network_image
패키지를 사용하면 편리하게 관리할 수 있습니다.
// pubspec.yaml에 패키지 추가
// cached_network_image: ^3.2.3
import 'package:cached_network_image/cached_network_image.dart';
CachedNetworkImage(
imageUrl: "https://example.com/image.jpg",
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)
SVG 이미지 사용하기
벡터 이미지는 다양한 화면 크기에 대응하기 좋습니다. Flutter에서 SVG를 사용하려면 flutter_svg
패키지를 활용합니다.
// pubspec.yaml에 패키지 추가
// flutter_svg: ^2.0.5
import 'package:flutter_svg/flutter_svg.dart';
SvgPicture.asset(
'assets/icons/home.svg',
width: 24,
height: 24,
)
4. 폰트 assets 사용하기
커스텀 폰트는 앱의 브랜딩과 사용자 경험에 중요한 역할을 합니다. Flutter에서 커스텀 폰트를 사용하는 방법을 알아보겠습니다.
pubspec.yaml에 폰트 등록하기
flutter:
fonts:
- family: Roboto
fonts:
- asset: assets/fonts/Roboto-Regular.ttf
- asset: assets/fonts/Roboto-Bold.ttf
weight: 700
- asset: assets/fonts/Roboto-Italic.ttf
style: italic
- family: NotoSansKR
fonts:
- asset: assets/fonts/NotoSansKR-Regular.ttf
- asset: assets/fonts/NotoSansKR-Bold.ttf
weight: 700
여러 폰트 패밀리와 각 패밀리 내의 다양한 웨이트/스타일을 등록할 수 있습니다.
전역 폰트 설정하기
앱 전체에 폰트를 적용하려면 ThemeData
를 사용합니다:
MaterialApp(
theme: ThemeData(
fontFamily: 'Roboto', // 기본 폰트 패밀리
textTheme: TextTheme(
headline1: TextStyle(
fontFamily: 'NotoSansKR',
fontWeight: FontWeight.bold,
),
// 다른 텍스트 스타일도 정의 가능
),
),
// ...
)
개별 텍스트에 폰트 적용하기
특정 텍스트에만 다른 폰트를 적용할 수도 있습니다.
Text(
'안녕하세요, Flutter!',
style: TextStyle(
fontFamily: 'NotoSansKR',
fontSize: 20,
fontWeight: FontWeight.w700,
),
)
Google Fonts 패키지 활용하기
Google Fonts를 쉽게 사용할 수 있는 패키지도 있습니다.
// pubspec.yaml에 패키지 추가
// google_fonts: ^4.0.4
import 'package:google_fonts/google_fonts.dart';
Text(
'구글 폰트로 표시되는 텍스트',
style: GoogleFonts.lato(
fontSize: 20,
fontWeight: FontWeight.w600,
),
)
아무래도 구글 폰트를 사용하면 처음 사용할 때 폰트를 다운로드하고 캐싱하므로 앱 사이즈를 줄일 수 있는 장점이 있습니다. 저 같은 경우에는 Flutter로 웹개발도 하기 때문에 Assets에 폰트를 담기 보단 구글 폰트를 이용하는 경우가 많습니다.
5. JSON 및 텍스트 파일 활용하기
앱의 설정 데이터나 정적 콘텐츠를 JSON이나 텍스트 파일로 관리하면 편리합니다.
JSON 파일 로드하기
먼저 JSON 파일을 Assets에 등록합니다.
flutter:
assets:
- assets/data/config.json
그리고 다음과 같이 로드합니다.
import 'dart:convert';
import 'package:flutter/services.dart';
Future<Map<String, dynamic>> loadConfig() async {
// JSON 파일 읽기
final String jsonString = await rootBundle.loadString('assets/data/config.json');
// JSON 파싱
final Map<String, dynamic> config = json.decode(jsonString);
return config;
}
// 사용 예시 (StatefulWidget의 initState 등에서)
void initState() {
super.initState();
loadConfig().then((config) {
setState(() {
// config 데이터 활용
apiUrl = config['api_url'];
appVersion = config['version'];
});
});
}
7. 테마 전환 앱으로 한번 예시를 만들어보자.
이제 설명한 내용을 활용해 테마 전환 기능이 있는 간단한 앱을 만들어 보겠습니다.
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isDarkMode = false;
Map<String, dynamic> themeConfig = {};
@override
void initState() {
super.initState();
_loadThemeConfig();
}
Future<void> _loadThemeConfig() async {
final String jsonString = await rootBundle.loadString('assets/data/themes.json');
setState(() {
themeConfig = json.decode(jsonString);
});
}
void _toggleTheme() {
setState(() {
isDarkMode = !isDarkMode;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Assets 데모',
theme: ThemeData(
brightness: isDarkMode ? Brightness.dark : Brightness.light,
primaryColor: isDarkMode
? Color(int.parse(themeConfig['dark']?['primary'] ?? 'FF2196F3', radix: 16) + 0xFF000000)
: Color(int.parse(themeConfig['light']?['primary'] ?? 'FF2196F3', radix: 16) + 0xFF000000),
fontFamily: 'Roboto',
),
home: HomePage(isDarkMode: isDarkMode, toggleTheme: _toggleTheme),
);
}
}
class HomePage extends StatelessWidget {
final bool isDarkMode;
final VoidCallback toggleTheme;
HomePage({required this.isDarkMode, required this.toggleTheme});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Assets 데모'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// 로고 이미지 표시
Image.asset(
isDarkMode
? 'assets/images/logo_dark.png'
: 'assets/images/logo_light.png',
width: 200,
),
SizedBox(height: 20),
// 텍스트 표시
Text(
'테마 전환 예제',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
SizedBox(height: 20),
// 테마 전환 버튼
ElevatedButton.icon(
onPressed: toggleTheme,
icon: Icon(isDarkMode ? Icons.light_mode : Icons.dark_mode),
label: Text(isDarkMode ? '라이트 모드로 전환' : '다크 모드로 전환'),
),
],
),
),
);
}
}
이 예제는 다음과 같은 Assets를 활용합니다.
- 라이트/다크 모드용 로고 이미지
- 테마 설정이 담긴 JSON 파일
- 커스텀 폰트
8. Assets과 관련된 에러와 그 해결법!
Assets 작업 시 자주 발생하는 문제와 해결 방법을 알아보겠습니다.
"Unable to load asset" 오류
아마 처음 Assets을 등록해서 사용하다보면 가장 많이 보는 에러 구문입니다. 이 오류가 발생하면,
- pubspec.yaml에 Assets가 올바르게 등록되었는지 확인
- 들여쓰기가 정확한지 확인 (YAML은 들여쓰기에 민감함)
- 파일 경로와 대소문자가 정확한지 확인
- 변경 후
flutter pub get
명령어 실행 - 에뮬레이터/시뮬레이터 재시작 또는
flutter clean
실행
등을 확인해보시면 됩니다.
해상도별 이미지가 제대로 로드되지 않을 때
- 디렉토리 구조가 정확한지 확인 (
2.0x
,3.0x
와 같이 정확히 명명) - 모든 해상도 폴더에 동일한 파일명 사용
폰트가 적용되지 않을 때
- 폰트 파일이 올바른 형식인지 확인 (TTF, OTF)
- pubspec.yaml의 font 섹션 구성 확인
- weight와 style이 올바르게 지정되었는지 확인
- 앱을 완전히 재시작해보기
이상으로 Assets에 대해서 알아보았습니다.
감사합니다.
'Developer > Flutter' 카테고리의 다른 글
[Flutter공부]네비게이션과 라우팅 - 화면 전환과 데이터 전달 방법 (0) | 2025.03.10 |
---|---|
[Flutter 공부하기] Hot Reload 와 Hot Restart (0) | 2025.03.07 |
[Flutter 공부하기] 사용자 입력 처리하기 (0) | 2025.03.04 |
[Flutter 공부하기] StatelessWidget vs StatefulWidget (0) | 2025.03.04 |
[Flutter 공부하기] 위젯. Widget. (0) | 2025.03.03 |