라이브러리와 파트 시스템
Dart에서 "라이브러리"는 단순히 파일 하나를 의미하기도 하고, 여러 파일을 묶은 단위를 의미하기도 합니다. library, part, part of, export 키워드로 라이브러리의 범위와 가시성을 정밀하게 제어할 수 있습니다. 개인 프로젝트에서는 자주 안 쓰지만, 패키지를 개발하거나 코드 생성 도구를 다룰 때 반드시 알아야 합니다.
library 선언
파일 맨 위에 library 키워드로 라이브러리 이름을 선언합니다.
// lib/my_package.dart
library my_package;
Dart 2.x까지는 필수였지만, Dart 3.x에서는 생략 가능합니다. 지금은 문서화 목적이나 part 시스템과 함께 쓸 때만 명시합니다.
이름이 있는 라이브러리는 다른 파일에서 library 지시자로 참조할 수 있고, dart doc이 문서를 생성할 때 이름을 사용합니다.
export
다른 파일의 공개 API를 현재 파일을 통해 다시 노출합니다. 앞 챕터의 배럴 파일이 바로 export를 사용하는 방식입니다.
// lib/my_package.dart
export 'src/models/user.dart';
export 'src/services/user_service.dart';
export 'src/utils/string_utils.dart';
show / hide 로 선택적 export
필요한 심볼만 노출하거나 특정 심볼을 숨깁니다.
// 특정 클래스만 export
export 'src/models/user.dart' show User, UserRole;
// 특정 클래스를 제외하고 export
export 'src/models/user.dart' hide InternalUserHelper;
// 조합 사용
export 'src/services/auth_service.dart' show AuthService hide AuthError;
import show / hide
import할 때도 필요한 심볼만 가져올 수 있습니다.
// 특정 클래스만 import
import 'package:my_package/my_package.dart' show User, UserService;
// 특정 클래스를 제외하고 import
import 'package:http/http.dart' hide Client;
// 이름 충돌 해결 — as로 별명 지정
import 'package:http/http.dart' as http;
import 'package:dio/dio.dart' as dio;
void main() async {
final res1 = await http.get(Uri.parse('...'));
final dio1 = dio.Dio();
}
가시성 — 밑줄(_) private
Dart의 접근 제어는 클래스 수준이 아닌 라이브러리(파일) 수준입니다. 밑줄(_)로 시작하는 이름은 같은 라이브러리(파일) 내에서만 접근 가능합니다.
// lib/src/user_service.dart
class UserService {
// 공개 메서드
Future<User> findById(int id) async {
return _fetchFromDatabase(id); // 내부에서 접근 OK
}
// private 메서드 — 이 파일 밖에서 접근 불가
Future<User> _fetchFromDatabase(int id) async {
final raw = await _db.query('SELECT * FROM users WHERE id = ?', [id]);
return User.fromJson(raw.first);
}
final _db = DatabaseConnection(); // private 필드
}
// 같은 파일 내의 다른 클래스
class _InternalHelper { // private 클래스
static String normalize(String input) => input.trim().toLowerCase();
}
다른 파일에서는 _로 시작하는 멤버에 접근할 수 없습니다.
// bin/main.dart
import 'package:my_app/src/user_service.dart';
void main() {
final service = UserService();
service.findById(1); // OK
// service._fetchFromDatabase(1); // 컴파일 오류!
// _InternalHelper.normalize('...'); // 컴파일 오류!
}
part / part of
하나의 라이브러리를 여러 파일로 분할합니다. part of가 선언된 파일은 라이브러리의 일부가 되어 _ private 멤버를 서로 공유합니다.
// lib/src/user.dart — 라이브러리 본체
library user;
part 'user_validation.dart';
part 'user_serialization.dart';
class User {
final int id;
final String _rawName; // private 필드
User({required this.id, required String name}) : _rawName = name;
String get name => _normalizedName; // user_validation.dart의 함수 사용
}
// lib/src/user_validation.dart
part of user;
// User의 private 필드에 직접 접근 가능
String get _normalizedName => user._rawName.trim();
bool isValidEmail(String email) {
return RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(email);
}
// lib/src/user_serialization.dart
part of user;
extension UserSerialization on User {
Map<String, dynamic> toJson() => {
'id': id,
'name': _rawName, // private 접근 가능
};
static User fromJson(Map<String, dynamic> json) => User(
id: json['id'] as int,
name: json['name'] as String,
);
}
part 시스템이 필요한 경우
주로 코드 생성 도구와 함께 사용됩니다.
// lib/src/user.dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart'; // json_serializable이 생성하는 파일
@JsonSerializable()
class User {
final int id;
final String name;
User({required this.id, required this.name});
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
Map<String, dynamic> toJson() => _$UserToJson(this);
}
build_runner로 user.g.dart 파일을 생성합니다.
dart run build_runner builddart run build_runner watch # 변경 감지 후 자동 생성
part vs export 선택 기준
| 상황 | 권장 방식 |
|---|---|
| 공개 API 묶기 | export (배럴 파일) |
코드 생성 (.g.dart, .freezed.dart) |
part / part of |
| private 멤버 공유 | part / part of |
| 일반 파일 분리 | import (별도 파일) |
part 시스템은 private 멤버를 공유하는 강한 결합이므로 코드 생성 외에는 사용을 자제합니다. 대부분의 경우 export로 충분합니다.
라이브러리 지시자 요약
// 다른 패키지 가져오기
import 'dart:core'; // SDK 내장 라이브러리
import 'dart:async';
import 'package:http/http.dart' as http; // pub 패키지
// 상대 경로 import (같은 패키지 내)
import 'src/models/user.dart';
// 선택적 import
import 'package:my_lib/my_lib.dart' show User hide UserHelper;
// 재export
export 'src/user.dart';
export 'src/user.dart' show User;
// 파트 분할
part 'user.g.dart';
part of user;
정리
이번 챕터에서 다룬 내용입니다.
export와show/hide로 패키지의 공개 API를 정밀하게 제어합니다.import as로 이름 충돌을 해결하고,import show/hide로 필요한 심볼만 가져옵니다.- Dart의 가시성은 파일(라이브러리) 수준입니다.
_로 시작하면 같은 파일 내에서만 접근 가능합니다. part/part of는 코드 생성 도구와 함께 사용할 때 주로 씁니다. 일반적인 파일 분리에는 적합하지 않습니다.
다음 챕터에서는 Dart 공식 코딩 컨벤션인 Effective Dart를 다룹니다.