์์กด์ฑ ์ฃผ์ - get_it, Riverpod
์ง๋ ๊ธ์์ View, ViewModel, Repository, Service๋ก ๊ณ์ธต์ ๋๋ด๋ค. ๊ทธ๋ฐ๋ฐ ์ด ๊ณ์ธต๋ค์ ๋๊ฐ ๋ง๋ค์ด์ ์ฐ๊ฒฐํ๋์ง๋ ์์ง ์ผ๋ฒ๋ฌด๋ ธ๋ค. ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ViewModel ์์์ ํ์ํ ๊ฑธ ์ง์ ๋ง๋๋ ๊ฒ์ด๋ค.
class UserViewModel {
final UserRepository repo = UserRepositoryImpl(
UserApiService(
Dio(BaseOptions(baseUrl: 'https://example.com/api')),
),
); // ํ์ํ ๊ฑธ ์์์ ์ง์ ๋ง๋ฆ
Future<List<User>> load() => repo.getUsers();
}๋์๊ฐ๋ค. ํ์ง๋ง ์ด UserViewModel์ ์ํํ๋ ค๊ณ ๊ฐ์ง Repository๋ฅผ ๋ผ์ฐ๋ ค๋ ์๊ฐ ๋งํ๋ค. UserRepositoryImpl์ด ํด๋์ค ์์ ๋ฐํ ์์ด์ ๋ฐ๊ฟ ๋ผ์ธ ์๊ฐ ์๋ค. ์ฌ์ฉ์ ํ๋ฉด์ด ๋์ด๋ฉด Dio๋ ๋ ๋ง๋ค์ด์ ธ์ ์๋ฒ ์ฃผ์ ์ค์ ์ด ๋ ๊ตฐ๋ฐ๋ก ํฉ์ด์ง๊ณ , UserApiService๊ฐ ์์ฑ์๋ฅผ ๋ฐ๊พธ๋ฉด ์ด๊ฑธ ์ฐ๋ ๋ชจ๋ ๊ณณ์ ๊ณ ์ณ์ผ ํ๋ค.
์์ธ: ์๊ธฐ๊ฐ ์ธ ๊ฒ์ ์๊ธฐ๊ฐ ๋ง๋ค๊ณ ์๋ค
ViewModel์ด UserRepositoryImpl์ด๋ผ๋ ๊ตฌ์ฒด์ ์ธ ํด๋์ค๋ฅผ ์ง์ ์๊ณ ๋ง๋ ๋ค. ์ด๋ฌ๋ฉด ViewModel๊ณผ ๊ทธ ๊ตฌํ์ด ๋จ๋จํ ๋ถ์ด๋ฒ๋ฆฐ๋ค. ํด๊ฒฐ์ ํต์ฌ์ ๋จ์ํ๋ค. ํ์ํ ๊ฑด ๋ฐ์์ ๋ฐ๋๋ค. ์์ฑ์ ๊ธ์์ ๋ณธ this.ํ๋ ๋ฌธ๋ฒ ๊ทธ๋๋ก๋ค.
class UserViewModel {
final UserRepository repo; // ์ด๋ค ๊ตฌํ์ธ์ง ๋ชจ๋ฅธ๋ค. ์ฝ์(์ถ์ ํด๋์ค)๋ง ์๋ค
UserViewModel(this.repo);
Future<List<User>> load() => repo.getUsers();
}์ด์ UserViewModel์ UserRepository๋ผ๋ ์ฝ์๋ง ์๋ค. ์ง์ง๊ฐ ์ฌ์ง ๊ฐ์ง๊ฐ ์ฌ์ง๋ ๋ง๋๋ ์ชฝ์ด ์ ํ๋ค. ์ด๋ ๊ฒ ํ์ํ ๊ฑธ ๋ฐ์์ ๋ฃ์ด์ฃผ๋ ๋ฐฉ์์ด ์์กด์ฑ ์ฃผ์
(Dependency Injection, DI)์ด๋ค.
๊ทธ๋ฐ๋ฐ ์๋ก์ด ๋ฌธ์ ๊ฐ ์๊ธด๋ค. UserViewModel(repo)๋ฅผ ๋ง๋ค๋ ค๋ฉด repo๊ฐ ํ์ํ๊ณ , repo๋ฅผ ๋ง๋ค๋ ค๋ฉด UserApiService๊ฐ, ๊ทธ๊ฑธ ๋ง๋ค๋ ค๋ฉด Dio๊ฐ ํ์ํ๋ค. ์ด ์กฐ๋ฆฝ ๊ณผ์ ์ ์ด๋์ ์ธ ๊ฒ์ธ๊ฐ๊ฐ DI์ ์ค์ ๊ณ ๋ฏผ์ด๋ค. ๋ ๊ฐ์ง ๋๊ตฌ๊ฐ ์๋ค.
ํด๊ฒฐ 1: get_it - ํ ๊ณณ์ ๋ฑ๋กํด๋๊ณ ๊บผ๋ด ์ด๋ค
flutter pub add get_itget_it์ ์๋น์ค ๋ก์ผ์ดํฐ๋ค. ์ฑ ์์ ๋ "์ด ํ์
์ด ํ์ํ๋ฉด ์ด๋ ๊ฒ ๋ง๋ค์ด๋ผ"๋ฅผ ํ ๊ณณ์ ๋ฑ๋กํ๊ณ , ํ์ํ ๊ณณ์์ ๊บผ๋ด ์ด๋ค.
import 'package:get_it/get_it.dart';
final getIt = GetIt.instance;
void setupDependencies() {
getIt.registerLazySingleton<Dio>(
() => Dio(BaseOptions(baseUrl: 'https://example.com/api')),
);
getIt.registerLazySingleton<UserApiService>(
() => UserApiService(getIt<Dio>()),
);
getIt.registerLazySingleton<UserRepository>(
() => UserRepositoryImpl(getIt<UserApiService>()),
);
getIt.registerFactory<UserViewModel>(
() => UserViewModel(getIt<UserRepository>()),
);
}
void main() {
setupDependencies(); // ์ฑ์ด ์์๋๊ธฐ ์ ์ ํ ๋ฒ
runApp(const MyApp());
}ํ์ํ ๊ณณ์์๋ getIt<ํ์
>()์ผ๋ก ๊บผ๋ธ๋ค.
final viewModel = getIt<UserViewModel>();getIt<UserViewModel>()์ ๋ถ๋ฅด๋ฉด UserViewModel์ ๋ง๋๋ ๋ฐ ํ์ํ UserRepository, UserApiService, Dio๊ฐ ์์์ ์ด์ด์ ธ์ ๋ง๋ค์ด์ง๋ค. ๋ฑ๋กํ๋ ๊ณณ์์ ์กฐ๋ฆฝ์ ํ ๋ฒ ์ฐ๋ฉด ๋์ด๋ค.
๋ฑ๋ก ๋ฐฉ์์ ์ธ ๊ฐ์ง๋ค
registerSingleton<T>(T instance): ์ด๋ฏธ ๋ง๋ ๊ฐ์ฒด๋ฅผ ๋ฑ๋กํ๋ค. ์ฑ ๋ด๋ด ๊ฐ์ ๊ฐ์ฒด๋ฅผ ์ด๋ค. ๋ฑ๋กํ๋ ์๊ฐ ์ด๋ฏธ ๋ง๋ค์ด์ ธ ์๋คregisterLazySingleton<T>(() => ...): ์ฒ์ ๊บผ๋ผ ๋ ํ ๋ฒ ๋ง๋ค๊ณ ๊ทธ ๋ค๋ก๋ ๊ฐ์ ๊ฐ์ฒด๋ฅผ ์ด๋ค.Dio, Repository์ฒ๋ผ ์ฑ ์ ์ฒด์์ ํ๋๋ฉด ๋๋ ๊ฒ์ ์๋ง๋คregisterFactory<T>(() => ...): ๊บผ๋ผ ๋๋ง๋ค ์๋ก ๋ง๋ ๋ค. ํ๋ฉด๋ง๋ค ๋ฐ๋ก ์์ด์ผ ํ๋ ViewModel์ ์๋ง๋ค
registerSingleton๊ณผ registerFactory๋ฅผ ํท๊ฐ๋ฆฌ๋ ์ค์๊ฐ ๊ฐ์ฅ ํํ๋ค. registerSingleton์ ํจ์๊ฐ ์๋๋ผ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ฅผ ๋ฐ๋๋ค.
getIt.registerSingleton<UserService>(() => UserService()); // ์๋ฌ: ๊ฐ์ฒด๊ฐ ์๋๋ผ ํจ์๋ฅผ ๋๊น
getIt.registerSingleton<UserService>(UserService()); // OK
getIt.registerLazySingleton<UserService>(() => UserService()); // ํจ์๋ก ๋๊ธฐ๋ ค๋ฉด ์ด์ชฝ์ฝ์ง ํฌ์ธํธ 1: ๋ฑ๋กํ์ง ์์ ๊ฑธ ๊บผ๋ธ๋ค
getIt<UserRepository>()๋ฅผ ๋ถ๋ฅด๋๋ฐ ๋ฑ๋ก์ ๋น ๋จ๋ ธ๋ค๋ฉด "๋ฑ๋ก๋์ง ์์๋ค"๋ ์ทจ์ง์ ์๋ฌ๊ฐ ์คํ ์ค์ ๋๋ค. Provider ๊ธ์ ProviderNotFoundException๊ณผ ๊ฐ์ ์ ํ์ผ๋ก, ์ปดํ์ผ๋ฌ๋ getIt<๋ฌด์์ด๋ >()์ด ๋ง๋์ง ํ์ธํ์ง ๋ชปํ๋ค. ๋ฐ๋๋ก ๊ฐ์ ํ์
์ ๋ ๋ฒ ๋ฑ๋กํ๋ฉด ์ด๋ฏธ ๋ฑ๋ก๋์๋ค๋ ์๋ฌ๊ฐ ๋๋ค.
์ฝ์ง ํฌ์ธํธ 2: ์ํ์์ ๊ฐ์ง๋ก ๋ฐ๊พธ๋ ค๋ฉด ์ด๊ธฐํ๋ถํฐ
get_it์ ์ฑ ์ ์ฒด์์ ํ๋๋ง ์๋ ๊ฐ์ฒด๋ผ์, ์ํ ํ๋๊ฐ ๋ฑ๋กํ ๊ฑธ ๋ค์ ์ํ์ด ์ด์ด๋ฐ๋๋ค. ์ํ ์ ์ getIt.reset()์ผ๋ก ๋ชจ๋ ๋น์ฐ๊ณ ๊ฐ์ง๋ฅผ ๋ฑ๋กํ๋ค.
setUp(() async {
await getIt.reset();
getIt.registerSingleton<UserRepository>(FakeUserRepository());
getIt.registerFactory<UserViewModel>(() => UserViewModel(getIt<UserRepository>()));
});get_it์ ํ๊ณ: ์์กด์ฑ์ด ์จ๋๋ค
get_it์ ๊ณต์ ์ค๋ช
์ "์ํ ๊ด๋ฆฌ ๋๊ตฌ๊ฐ ์๋๋ค"๋ผ๊ณ ๋ชป ๋ฐ๋๋ค. ๊ฐ์ ๊บผ๋ด๋ ๊ฒ์ผ ๋ฟ, ๊ฐ์ด ๋ฐ๋์์ ๋ ํ๋ฉด์ ๋ค์ ๊ทธ๋ ค์ฃผ์ง ์๋๋ค. ๊ทธ๋์ ChangeNotifier๋ Cubit ๊ฐ์ ์ํ ๊ด๋ฆฌ์ ๊ฐ์ด ์ด๋ค.
๋ ํ๋์ ์ฝ์ ์ด ์๋ค. UserViewModel(this.repo)์ฒ๋ผ ์์ฑ์๋ก ๋ฐ์ผ๋ฉด "์ด ํด๋์ค๊ฐ ๋ฌด์์ ํ์๋ก ํ๋์ง" ์์ฑ์๋ง ๋ด๋ ์ ์ ์๋ค. ๊ทธ๋ฐ๋ฐ ํด๋์ค ์ ์ด๋์๋ getIt<UserRepository>()๋ฅผ ๋ถ๋ฅผ ์ ์์ด์, ์ด๋ฐ ์์ผ๋ก ์ฐ๋ฉด ์์กดํ๋ ๊ฒ์ด ์ฝ๋ ์์ ์จ๋๋ค. ์ด ๋ฐฉ์์ ์์ฑ์์์๋ง getIt์ ์ฐ๊ณ , ํด๋์ค ์์ชฝ์์๋ ๋ฐ์ ๊ฑธ ์ฐ๋ ์์ผ๋ก ๊ท์น์ ์ ํด๋๋ ๊ฒ ์ข๋ค.
ํด๊ฒฐ 2: Riverpod - provider๊ฐ ๊ณง DI
์ง๋ ๊ธ์์ ์ด๋ฏธ ํ๋ค. ์ฌ์ค Riverpod์ DI ๋๊ตฌ์ด๊ธฐ๋ ํ๋ค.
final dioProvider = Provider<Dio>(
(ref) => Dio(BaseOptions(baseUrl: 'https://example.com/api')),
);
final userApiServiceProvider = Provider<UserApiService>(
(ref) => UserApiService(ref.watch(dioProvider)),
);
final userRepositoryProvider = Provider<UserRepository>(
(ref) => UserRepositoryImpl(ref.watch(userApiServiceProvider)),
);ref.watch(๋ค๋ฅธProvider)๋ก ์์กดํ๋ ๊ฒ์ ๊บผ๋ด ์ฐ๊ฒฐํ๋ค. get_it์ registerLazySingleton๊ณผ ๊ฐ์ ์ญํ ์ provider ํ๋๊ฐ ํ๋ค. ๊ทธ๋ฆฌ๊ณ ๊ฐ์ง๋ก ๋ฐ๊พธ๋ ๋ฐฉ๋ฒ์ ์ง๋ ๊ธ์์ ๋ณธ overrideWithValue๋ค.
ProviderScope(
overrides: [userRepositoryProvider.overrideWithValue(FakeUserRepository())],
child: const MyApp(),
)์ํ์ด ๋๋ ๋ ProviderScope๊ฐ ๊ฐ์ด ์ฌ๋ผ์ง๋ reset()์ ๋ถ๋ฅผ ํ์๊ฐ ์๋ค. ๋ณ์๋ก ์ฐพ์ผ๋ฏ๋ก ๋ฑ๋ก ๋๋ฝ์ด ์ปดํ์ผ ๋ ์๋ํฐ์ ์กํ๋ ๊ฒ๋ get_it๋ณด๋ค ๋์ ์ ์ด๋ค.
์ด๋ ์ชฝ์ ์ฐ๋
- Riverpod์ ์ํ ๊ด๋ฆฌ๋ก ์ฐ๊ณ ์๋ค๋ฉด: ๋ณ๋ DI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ ์๋ค. provider๊ฐ DI๋ฅผ ๊ฒธํ๋ค
- Provider, Bloc, Cubit์ ์ด๋ค๋ฉด:
get_it์ ๊ฐ์ด ์ฐ๋ ์กฐํฉ์ด ํํ๋ค. ์ํ ๊ด๋ฆฌ๋ Bloc์ด ํ๊ณ , ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ณ ์ฐ๊ฒฐํ๋ ๊ฑดget_it์ด ํ๋ค - ์์ฃผ ์์ ์ฑ์ด๋ผ๋ฉด:
main์์ ์ง์ UserViewModel(UserRepositoryImpl(...))์ ์กฐ๋ฆฝํด์ ์์ฑ์๋ก ๋๊ฒจ๋ ์ถฉ๋ถํ๋ค. DI ๋๊ตฌ๊ฐ ์์ด๋ "๋ฐ์์ ๋ฐ๋๋ค"๋ ์์น์ ์งํฌ ์ ์๋ค
Android, iOS์ ๋น๊ตํ๋ฉด
// Kotlin - Hilt
@HiltViewModel
class UserViewModel @Inject constructor(
private val repo: UserRepository
) : ViewModel()
@Module @InstallIn(SingletonComponent::class)
object AppModule {
@Provides @Singleton
fun provideRepo(api: UserApi): UserRepository = UserRepositoryImpl(api)
}// Swift - ์ด๋์
๋ผ์ด์ ๋ก ์ง์ ์ฃผ์
final class UserViewModel {
private let repo: UserRepository
init(repo: UserRepository) { self.repo = repo }
}- ํ์ํ ๊ฑธ ๋ฐ์์ ๋ฐ๊ธฐ: ์ธ ๊ณณ ๋ชจ๋ ์์ฑ์(์ด๋์ ๋ผ์ด์ )๋ก ๋ฐ๋๋ค
- ์กฐ๋ฆฝํ๋ ๊ณณ: Flutter
get_it๋ฑ๋ก์ด๋ Riverpod provider / Android Hilt@Module/ iOS๋ ์ง์ ์กฐ๋ฆฝํ๊ฑฐ๋ ์์กด์ฑ ์ฃผ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ - ๋ง๋๋ ๊ท์น: Flutter
registerLazySingleton(์ฑ์ ํ๋),registerFactory(๋งค๋ฒ ์๋ก) / Hilt@Singleton(์ฑ์ ํ๋), ๊ธฐ๋ณธ๊ฐ(๋งค๋ฒ ์๋ก) - ๋น ๋จ๋ ธ์ ๋:
get_it์ ์คํ ์ค ์๋ฌ / Riverpod์ ์ปดํ์ผ ๋ ์๋ํฐ๊ฐ ์ก์ / Hilt๋ ์ปดํ์ผ ๋ ๋น๋๊ฐ ์คํจ
Hilt๋ ๋ฑ๋ก์ด ๋น ์ง๋ฉด ๋น๋๊ฐ ์คํจํ๋๋ฐ, get_it์ ์คํํด๋ด์ผ ์๋ค. ์ด ์ฐจ์ด๋ get_it์ด ์ฝ๋ ์์ฑ ์์ด ์คํ ์ค์ ์ฐพ๋ ์๋น์ค ๋ก์ผ์ดํฐ์ด๊ธฐ ๋๋ฌธ์ด๋ค. Riverpod์ ๋ณ์๋ก ์ฐพ์์ ์ปดํ์ผ๋ฌ์ ๋์์ ๋ฐ๋๋ค.
์ ๋ฆฌ
- ํด๋์ค ์์์ ํ์ํ ๊ฑธ ์ง์ ๋ง๋ค๋ฉด ๊ฐ์ง๋ก ๋ฐ๊ฟ ๋ผ์ธ ์ ์์ผ๋ฏ๋ก, ์์ฑ์๋ก ๋ฐ์์ ๋ฐ๋ ์์กด์ฑ ์ฃผ์ ์ ์ด๋ค
- ๋ฐ๋ ์ชฝ์ ๊ตฌ์ฒด์ ์ธ ํด๋์ค๊ฐ ์๋๋ผ
UserRepository๊ฐ์ ์ถ์ ํด๋์ค(์ฝ์)๋ง ์๋ค get_it์registerSingleton(๋ง๋ ๊ฐ์ฒด),registerLazySingleton(์ฒ์ ๊บผ๋ผ ๋ ํ ๋ฒ),registerFactory(๋งค๋ฒ ์๋ก)๋ก ๋ฑ๋กํ๊ณgetIt<T>()๋ก ๊บผ๋ธ๋คregisterSingleton์ ํจ์๊ฐ ์๋๋ผ ๊ฐ์ฒด๋ฅผ ๋ฐ๊ณ , ๋ฑ๋ก์ ๋น ๋จ๋ฆฌ๋ฉด ์คํ ์ค์ ์๋ฌ๊ฐ ๋๋ฉฐ, ์ํ์์๋getIt.reset()์ผ๋ก ๋น์ฐ๊ณ ๊ฐ์ง๋ฅผ ๋ฑ๋กํ๋คget_it์ ์ํ ๊ด๋ฆฌ ๋๊ตฌ๊ฐ ์๋๊ณ , ์์กดํ๋ ๊ฒ์ด ์ฝ๋ ์์ ์จ์ ์ ์์ผ๋ ์์ฑ์์์๋ง ๊บผ๋ด๋ ์์ผ๋ก ๊ท์น์ ๋๋ค- Riverpod์ provider๊ฐ DI๋ฅผ ๊ฒธํ๋ฏ๋ก ๋ณ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ ์๊ณ ,
overrideWithValue๋ก ๊ฐ์ง๋ฅผ ๋ผ์ด๋ค
๋ค์ ๊ธ์์๋ ํ๋ฉด์ ์์ง์์ ์ฃผ๋ ์ ๋๋ฉ์ด์
(์์์ , ๋ช
์์ , Hero)์ ๋ค๋ค๋ณด๊ฒ ๋ค.
