플러터 프로젝트 구조
플러터(Flutter) 프로젝트의 폴더 구조에 대해 좀 더 깊이 공부해봤다. 평소에 프로젝트를 진행하면서 여러 디렉토리가 어떻게 구성되어 있는지 궁금했는데, 이번 기회에 제대로 정리해보고자 한다.
플러터 프로젝트를 새로 생성하면 여러 폴더들이 보인다. 그 중에서 가장 중요한 곳은 lib 폴더다. 이 폴더에는 앱의 모든 주요 소스 코드가 담겨 있다.
특히 main.dart 파일이 앱의 진입점인데, 여기에 있는 main() 함수에서 앱이 시작된다. 예시 코드를 보면 이해가 쉽다.
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('My Flutter App')),
body: Center(child: Text('Hello, Flutter!')),
),
);
}
}
이 코드는 기본적인 플러터 앱의 구조다. runApp()을 통해 앱이 실행되고, MaterialApp으로 앱의 전반적인 구성을 정리한다.
좀 더 복잡한 앱을 만들 때는 lib 폴더 아래에 추가적인 디렉토리를 나누기도 한다. 예를 들어:
screens/ 폴더는 각 페이지나 화면을 정의하는 곳이다. HomeScreen.dart나 LoginScreen.dart 같은 파일을 여기에 두어 페이지별로 코드를 분리할 수 있다. 예를 들어, HomeScreen은 이렇게 만들 수 있다.
import 'package:flutter/material.dart';
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Home')),
body: Center(child: Text('Welcome to the Home Screen')),
);
}
}
widgets/ 폴더는 재사용 가능한 위젯을 모아두는 곳이다. 예를 들어, 커스텀 버튼을 만들고 싶을 때 CustomButton.dart라는 파일을 만들 수 있다.
import 'package:flutter/material.dart';
class CustomButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
CustomButton({required this.label, required this.onPressed});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(label),
);
}
}
models/ 폴더에는 데이터 모델을 정의할 수 있다. 예를 들어 UserModel.dart라는 파일을 통해 사용자 데이터를 다룰 수 있다.
class UserModel {
final String name;
final int age;
UserModel({required this.name, required this.age});
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
name: json['name'],
age: json['age'],
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'age': age,
};
}
}
이제 test/ 디렉토리에 대해 살펴보자. 여기엔 테스트 코드가 들어가며, 위젯이나 비즈니스 로직을 테스트할 수 있다. 기본적으로 생성되는 widget_test.dart 파일은 앱의 위젯이 잘 동작하는지를 확인할 수 있는 테스트를 포함하고 있다. 예시로 보이는 코드는 아래와 같다.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
import 'package:my_app/main.dart'; // main.dart를 가리키는 경로
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
await tester.pumpWidget(MyApp());
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}
그 밖에도 android/와 ios/ 디렉토리는 각각 네이티브 설정을 다룬다.
예를 들어, android 디렉토리 안의 AndroidManifest.xml에서는 앱의 퍼미션을 설정할 수 있다. ios 디렉토리의 Info.plist는 iOS 앱 설정에 필요하다.
마지막으로 pubspec.yaml 파일도 중요한데, 앱에서 사용하는 의존성과 리소스를 관리한다. 예를 들어, 새로운 패키지를 추가하고 싶을 때는 여기에 추가하면 된다.
dependencies:
flutter:
sdk: flutter
http: ^0.13.0 # 예시로 HTTP 패키지를 추가
오늘은 이렇게 정리하면서 플러터 프로젝트의 구조가 어떻게 작동하는지를 알게 되었다. 처음에는 복잡하게 보였지만, 예시 코드와 함께 보니 한결 이해가 쉽다. 앞으로 프로젝트를 진행할 때 이 구조를 잘 활용해보고 싶다.