의존성 주입(Dependency Injection, DI)은 객체 간의 의존성을 외부에서 주입하는 설계 패턴입니다.
쉽게 말해, 클래스가 직접 다른 객체를 생성하는 것이 아니라, 외부에서 제공된 객체를 사용하는 것을 의미합니다. 이를 통해 코드의 유지보수성을 높이고, 결합도를 낮출 수 있습니다.
우리가 식당에서 음식을 먹을 때, 주방장이 모든 재료를 직접 구하고, 요리하고, 서빙까지 한다면 엄청 비효율적입니다.
하지만 주방장은 재료 공급업체(외부)에서 재료를 받아서 요리를 만들기만 하면 됩니다.
이와 마찬가지로, 코드에서도 클래스가 직접 필요한 객체를 만들지 않고, 외부에서 주입받으면 더 효율적으로 관리할 수 있습니다.
의존성 주입을 사용하면 다음과 같은 장점이 있습니다.
의존성 주입 없이 코드를 작성하면 다음과 같은 문제가 발생할 수 있습니다.
class ApiService {
void fetchData() {
print("Fetching data from API...");
}
}
class HomeScreen {
final ApiService apiService = ApiService(); // 직접 객체 생성
}
📌 문제점
HomeScreen
이 ApiService
를 직접 생성하므로 두 클래스 간의 강한 결합(Tightly Coupled) 이 발생합니다.ApiService
를 변경하면 HomeScreen
도 함께 수정해야 하는 상황이 발생할 수 있습니다.🔍 강한 결합(Tightly Coupled)이란?
강한 결합이란, 한 클래스가 다른 클래스에 강하게 의존하고 있는 상태를 말합니다. 즉, 한 클래스가 변경되면, 이를 사용하는 다른 클래스도 변경해야 하는 상황을 의미합니다.
💡 그럼 강한 결합이 있으면 약한 결합도 있을까?
- 강한 결합(Tightly Coupled): 클래스 간의 의존성이 높아 변경이 어렵다.
- 약한 결합(Loosely Coupled): 클래스 간의 의존성이 낮아 변경이 용이하다.
의존성 주입을 활용하면 강한 결합을 약한 결합으로 바꿀 수 있어 코드의 유지보수성과 확장성이 높아집니다.
의존성 주입을 구현하는 다양한 방법이 있습니다.
class ApiService {
void fetchData() {
print("Fetching data from API...");
}
}
class HomeScreen {
final ApiService apiService;
HomeScreen(this.apiService); // 외부에서 객체를 주입
}
이제 HomeScreen
이 ApiService
를 직접 생성하는 것이 아니라, 외부에서 주입받도록 변경되었습니다. 이를 통해 클래스 간의 결합도를 낮추고, 코드의 유연성과 테스트 용이성이 높아집니다.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ApiService {
void fetchData() {
print("Fetching data from API...");
}
}
void main() {
runApp(
MultiProvider(
providers: [
Provider(create: (_) => ApiService()), // ApiService 주입
],
child: MyApp(),
),
);
}
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
void setupLocator() {
getIt.registerLazySingleton<ApiService>(() => ApiService());
}
import 'package:get_it/get_it.dart';
import 'package:injectable/injectable.dart';
final getIt = GetIt.instance;
()
void configureDependencies() => $initGetIt(getIt);
import 'package:flutter_riverpod/flutter_riverpod.dart';
final apiServiceProvider = Provider((ref) => ApiService());
방식 | 장점 | 단점 |
---|---|---|
Provider | 공식 권장, 간단하고 직관적 | 규모가 커질수록 관리 어려움 |
Riverpod | Provider 개선판, 안전하고 간결함 | 러닝 커브가 있음 |
GetIt | 전역적 객체 관리 가능, 사용이 간편 | Service Locator 패턴에 대한 이해 필요 |
Injectable | GetIt과 함께 사용 가능, DI 자동화 | 설정이 필요함 |
Flutter에서 의존성 주입(Dependency Injection)은 코드의 유지보수성을 높이고, 확장성을 증가시키는 중요한 개념입니다.
코드의 결합도를 줄여 유지보수를 쉽게 하고,
테스트가 용이해지며,
재사용 가능한 구조를 만들 수 있습니다.
의존성 주입을 구현하는 방법에는 Provider, GetIt, Riverpod, Injectable과 같은 다양한 패키지가 있으며, 프로젝트의 규모와 필요에 따라 적절한 방법을 선택하는 것이 중요합니다.