SlideShare a Scribd company logo
[Flutter] Dependency
Injection과 Service Locator
임태규
• 정보관리 기술사
• 11년차 모바일 엔지니어
(안드로이드, 플러터)
• 전) 삼성전자
• 전) 쿠팡
• 현) Presto Labs
• https://github.com/dualcoder-pe
• https://www.linkedin.com/in/taekyu-lim-b3b629187
Presto Labs
• Global Top Tier High Frequency Trading Firm
• Singapore, Shanghai, Seoul + Remote (Global)
• https://aqx.com
• Mobile, BE, FE 채용 (링크)
목차
Dependency Injection과 Service Locator
Flutter에서 Dependency Injection과 Service Locator
1
2
Dependency Injection과
Service Locator
Part 1
논의의 소재
객체지향 프로그래밍
SOLID
객체
클래스
캡슐화
추상화
다형성
Dependency Injection
Service Locator
IoC
객체지향 프로그래밍
• 객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로
보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 객체들의
모임으로 파악하고자 하는 것이다.
객체지향 프로그래밍
애플리케이션
객체 객체 객체
객체들을 잘 모으려면
어떻게 해야 할까?
응집도는 높게, 결합도는 낮게
SOLID
IoC
Dependency Injection
Service Locator
예제 프로젝트 구조
예제 프로젝트 구조
App Bloc UseCase Repository
Main View DataSource
응집도와 결합도
• 응집도(cohesion)
• 모듈 내부 구성요소 간 연관 정도
• 응집도가 높다 ➔ 모듈 내 모든 기능이 단일 목적을 위해 수행됨
• 결합도(coupling)
• 모듈과 외부 모듈의 연관 정도
• 결합도가 낮다 ➔ 모듈이 기능 수행을 위해 다른 모듈에 의존하지 않음
class SendOrderUsecase {
final OrderRepository _orderRepository = OrderRepository();
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
높은 결합도
예제 프로젝트 구조
App Bloc UseCase Repository
Main View DataSource
class OrderRepository {
final LocalDatasource _localDatasource = LocalDatasource();
final RemoteDatesource _remoteDatesource = RemoteDatesource();
Future<OrderResult> sendOrder(Order order) async {
final localRes = await _localDatasource.sendOrder(order);
final remoteRes = await _remoteDatesource.sendOrder(order);
return OrderResult((localRes && remoteRes) ? "SUCCESS" : "Fail");
}
}
높은 결합도
DIP 원칙 적용
SOLID
• Single Responsibility Principle: 단일 책임 원칙
• Open Closed Principle: 개방 폐쇄 원칙
• Liskov Substitution Principle: 리스코프 치환 원칙
• Interface Segregation Principle: 인터페이스 분리 원칙
• Dependency Inversion Principle: 의존성 역전 원칙
➔ 구체적인 대상이 아니라, 추상적인 대상에 의존해야 함
DIP 적용
UseCase Repository DataSource UseCase
Repository
DataSource
Repository
Impl
class OrderRepository {
final LocalDatasource _localDatasource = LocalDatasource();
final RemoteDatesource _remoteDatesource = RemoteDatesource();
Future<OrderResult> sendOrder(Order order) async {
}
}
abstract class OrderRepository {
Future<OrderResult> sendOrder(Order order);
}
DIP 적용
DIP 적용
class OrderRepositoryImpl implements OrderRepository {
final LocalDatasource _localDatasource = LocalDatasource();
final RemoteDatesource _remoteDatesource = RemoteDatesource();
Future<OrderResult> sendOrder(Order order) async {
final localRes = await _localDatasource.sendOrder(order);
final remoteRes = await _remoteDatesource.sendOrder(order);
return OrderResult((localRes && remoteRes) ? "SUCCESS" : "Fail");
}
}
class SendOrderUsecase {
final OrderRepository _orderRepository = OrderRepositoryImpl();
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
DIP 적용
아직 구체적인 대상에 대한
의존성을 제거하지 못함
DIP 적용
UseCase Repository DataSource UseCase
Repository
DataSource
Repository
Impl
IoC 원칙 적용
IoC
• 애플리케이션의 제어권을 개발자가 갖지 않고 외부에 위임
• Ex) 안드로이드에서 Lifecycle 별로 프레임워크에서 callback 함수를
호출
IoC 적용을 위한 패턴
• Template Method
• Strategy
• Factory Method
• Abstract Factory
• Dependency Injection
• Service Locator
➔ Interface 구현체와, Interface를 사용하는 App Code를 분리
Dependency Injection
• 의존성 주입
• 객체가 의존성을 외부에서 전달받는 패턴
Dependency Injection
UseCase
Repository
DataSource
Repository
Impl
누군가
• 의존성 주입 유형
• 생성자 주입
• Setter 주입
• 인터페이스 주입
Dependency Injection
• 객체의 생성자를 통해 외부에서 의존성을 주입
생성자 주입
class SendOrderUsecase {
final OrderRepository _orderRepository;
SendOrderUsecase(this._orderRepository);
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
class SendOrderUsecase {
late OrderRepository _orderRepository;
set orderRepository(OrderRepository orderRepository) =>
_orderRepository = orderRepository;
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
• 객체를 생성한 뒤, 인스턴스의 setter를 통해 의존성을 주입
Setter 주입
class SendOrderUsecase {
OrderRepository? _orderRepository;
set orderRepository(OrderRepository orderRepository) =>
_orderRepository = orderRepository;
Future<OrderResult> sendOrder(Order order) =>
_orderRepository?.sendOrder(order) ?? Future.value(OrderResult("Fail"));
}
• 객체를 생성한 뒤, 인스턴스의 setter를 통해 의존성을 주입
Setter 주입
누군가가 누군가?
(주입은 누가 해주나?)
생성자 주입 – Pure DI
void main() {
runApp(
OrderApp(orderBloc:
OrderBloc(SendOrderUsecase(OrderRepositoryImpl()))));
}
Setter 주입 - Pure DI
void main() {
runApp(OrderApp(
orderBloc: OrderBloc(
SendOrderUsecase()..orderRepository = OrderRepositoryImpl())));
}
• 원인
• Composition Root가 Entry Point와 가까워야 함
• 한계
• 의존관계 tree의 depth가 깊을 때, 안쪽까지 객체를 전달하기 불편함
Pure DI의 한계
App Bloc UseCase
Main View DataSource
Repository
Repository
Impl
누군가
가까이
Composition Root
Entry Point
Depth
Service Locator
• 애플리케이션에 필요한 모든 객체를 알고 있는 객체를 만들고, 그
객체를 통해 의존성을 획득하는 방법
Service Locator
App Bloc UseCase
Main View DataSource
Repository
Repository
Impl
누군가
Composition Root
Entry Point
어딘가
Service Locator
• Service Locator의 유형
• Static Service Locator
• Dynamic Service Locator
Service Locator
class ServiceLocator {
static ServiceLocator? _instance;
final OrderRepository _orderRepository;
ServiceLocator(this._orderRepository);
static void load(ServiceLocator serviceLocator) => _instance = serviceLocator;
static OrderRepository getOrderRepository() => _instance!._orderRepository;
}
Service Locator 구현
Service Locator 객체 생성
void main() {
ServiceLocator.load(ServiceLocator(OrderRepositoryImpl()));
runApp(OrderApp(orderBloc: OrderBloc(SendOrderUsecase())));
}
class SendOrderUsecase {
final OrderRepository _orderRepository = ServiceLocator.getOrderRepository();
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
Service Locator 의존성 획득
Dynamic Service Locator 구현
class DynamicServiceLocator {
static DynamicServiceLocator? _instance;
final HashMap<String, dynamic> _services = HashMap();
static void load(DynamicServiceLocator serviceLocator) =>
_instance = serviceLocator;
void loadService(String key, dynamic object) => _services[key] = object;
static dynamic getService(String key) => _instance!._services[key];
}
void main() {
DynamicServiceLocator locator = DynamicServiceLocator();
locator.loadService("OrderRepository", OrderRepositoryImpl());
DynamicServiceLocator.load(locator);
runApp(OrderApp(orderBloc: OrderBloc(SendOrderUsecase())));
}
Dynamic Service Locator 객체 생성
class SendOrderUsecase {
final OrderRepository _orderRepository =
DynamicServiceLocator.getService("OrderRepository");
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
Dynamic Service Locator 의존성 획득
Service Locator는
안티 패턴이다
- mark seemann -
• 종속성을 숨김
• 단위테스트 어려움
• mocking이 가능하더라도, 전역 메모리 사용하므로, 테스트 후 clear 필요
• 전역 싱글톤 패턴
• 구현 상 전역 싱글톤을 안쓰더라도, 내부적으로 공유 메모리 사용을 피할 수 없음)
• 모든 클래스가 로케이터와 커플링
• Runtime Error
• Locator의 존재를 모르고 생성을 안하고 사용하면 런타임 에러
• 의존 객체를 찾기 어려움
• 내부에 의존성 추가되면 컴파일러가 알 수 없음
• 캡슐화 위반
• 객체 사용을 위한 전제 조건을 알 수 없음
Service Locator의 단점
Flutter의 DI 라이브러리
• GetX
• GetIt
• InheritedWidget
• Provider/Riverpod
➔ InheritedWidget, Riverpod은 상태관리
Flutter DI 라이브러리
void main() {
Get.lazyPut<OrderRepository>(() => OrderRepositoryImpl());
runApp(OrderApp(orderBloc: OrderBloc(SendOrderUsecase())));
}
GetX의 Injection 방식
GetX의 Injection 방식
class SendOrderUsecase {
final OrderRepository _orderRepository = Get.find();
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
DI vs GetX
DI와 GetX의 비교 – DI
class OrderApp extends StatelessWidget {
const OrderApp({Key? key, required this.orderBloc}) : super(key: key);
final OrderBloc orderBloc;
class OrderBloc {
final SendOrderUsecase _sendOrderUsecase;
OrderBloc(this._sendOrderUsecase);
DI와 GetX의 비교 – DI
class SendOrderUsecase {
final OrderRepository _orderRepository;
SendOrderUsecase(this._orderRepository);
DI와 GetX의 비교 – DI
class OrderRepositoryImpl implements OrderRepository {
final LocalDatasource _localDatasource;
final RemoteDatasource _remoteDatesource;
OrderRepositoryImpl(this._localDatasource, this._remoteDatesource);
DI와 GetX의 비교 – DI
void main() {
runApp(OrderApp(
orderBloc: OrderBloc(SendOrderUsecase(
OrderRepositoryImpl(LocalDatasource(), RemoteDatasource())))));
}
DI와 GetX의 비교 – DI
class OrderApp extends StatelessWidget {
OrderApp({Key? key}) : super(key: key);
final OrderBloc orderBloc = Get.find();
DI와 GetX의 비교 – GetX
DI와 GetX의 비교 – GetX
class OrderBloc {
final SendOrderUsecase _sendOrderUsecase = Get.find();
class SendOrderUsecase {
final OrderRepository _orderRepository = Get.find();
class OrderRepositoryImpl implements OrderRepository {
final LocalDatasource _localDatasource = Get.find();
final RemoteDatasource _remoteDatesource = Get.find();
void main() {
Get.lazyPut(() => OrderBloc());
Get.lazyPut(() => SendOrderUsecase());
Get.lazyPut<OrderRepository>(() => OrderRepositoryImpl());
Get.lazyPut(() => LocalDatasource());
Get.lazyPut(() => RemoteDatasource());
runApp(OrderApp());
}
DI와 GetX의 비교 – GetX
DI와 GetX의 비교
App Bloc UseCase
Main View DataSource
Repository
Repository
Impl
누군가
Composition Root
Entry Point
Service Locator
누군가 어딘가
Composition Root
DI
GetX
GetX는 Service Locator
패턴
GetX를 Service Locator로만
쓸 수 있을까?
GetX를 DI Container로 활용하기
App Bloc UseCase
Main View DataSource
Repository
Repository
Impl
누군가
Composition Root
Entry Point
Service Locator
누군가 어딘가
Composition Root
DI with GetX
GetX
어딘가
Get
• DI는 원칙과 패턴
• 지금까지 본 DI는 Pure DI
• 외부 라이브러리나 객체 사용 없이 의존성 주입
• DI Container
• 의존성 주입할 모든 객체를 관리하는 모듈
• 역할 (RRR)
• Register: 인스턴스 등록
• Resolve: 인스턴스 제공
• Release: 인스턴스 해제
Pure DI와 DI Container
class Injector {
static void register() {
Get.lazyPut(() => LocalDatasource());
Get.lazyPut(() => RemoteDatasource());
Get.lazyPut<OrderRepository>(
() => OrderRepositoryImpl(Get.find(), Get.find()));
Get.lazyPut(() => SendOrderUsecase(Get.find()));
Get.lazyPut(() => OrderBloc(Get.find()));
}
}
GetX를 DI Container로 활용하기
GetX를 DI Container로 활용하기
void main() {
Injector.register();
runApp(OrderApp(orderBloc: Get.find()));
}
GetX를 DI Container로 활용하기
class OrderApp extends StatelessWidget {
const OrderApp({Key? key, required this.orderBloc}) : super(key: key);
final OrderBloc orderBloc;
class OrderBloc {
final SendOrderUsecase _sendOrderUsecase;
OrderBloc(this._sendOrderUsecase);
GetX를 DI Container로 활용하기
class SendOrderUsecase {
final OrderRepository _orderRepository;
SendOrderUsecase(this._orderRepository);
GetX를 DI Container로 활용하기
class OrderRepositoryImpl implements OrderRepository {
final LocalDatasource _localDatasource;
final RemoteDatasource _remoteDatesource;
OrderRepositoryImpl(this._localDatasource, this._remoteDatesource);
GetX를 DI Container로 활용하기
• 장점
• GetX의 Memory Management 기능 활용 가능
• Entry Point에서 가까운 Composition Root에서 의존관계 관리
• 의존관계 tree의 depth가 깊어도, 안쪽까지 객체를 쉽게 전달
• 단점
• GetX에 등록을 잊는다면, 여전히 Runtime Error 발생 가능
GetX를 DI Container로 활용하기
예외적으로 Composition Root가
아닌 곳에서 주입하고 싶다면?
class SendOrderUsecase {
final OrderRepository _orderRepository;
SendOrderUsecase([OrderRepository? orderRepository])
: _orderRepository = orderRepository ?? Get.find();
Future<OrderResult> sendOrder(Order order) =>
_orderRepository.sendOrder(order);
}
GetX를 DI Container로 활용하기
• 예외적으로 생성자에서 객체 생성해 주입 허용
• 외부에 의존관계 알림
• 테스트 용이
• 객체 주입 용이
• 단, 모든 객체가 GetX 의존성 가짐
GetX를 DI Container로 활용하기
• 원인
• 객체를 생성 및 조립한 후 변경이 불가능함
• 한계
• 객체 생성에 Runtime 요소가 필요할 때 대응이 어려움
• 보완
• 추상 팩토리 메서드 패턴 활용
한계 및 보완
• 의존성 있는 객체간 결합도를 낮추는 방법
• DI: 생성자 주입, Setter 주입
• Service Locator: 의존성 필요한 객체가 컨테이너에 객체 요청
• Service Locator는 안티패턴이라는 주장 (Mark Seemann)
• Flutter의 의존성 주입 (GetX, GetIT) ➔ Service Locator 방식
• GetX, GetIt을 DI Container로 활용할 수 있음
• 객체 생성에 Runtime 요소가 필요할 경우, 추상 팩토리 패턴으로 보완
정리
Q & A

More Related Content

What's hot

우아한 객체지향
우아한 객체지향우아한 객체지향
우아한 객체지향
Young-Ho Cho
 
이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정
Arawn Park
 
Git in 10 minutes
Git in 10 minutesGit in 10 minutes
Git in 10 minutes
Safique Ahmed Faruque
 
Introduction to git flow
Introduction to git flowIntroduction to git flow
Introduction to git flow
Knoldus Inc.
 
우아한 모노리스
우아한 모노리스우아한 모노리스
우아한 모노리스
Arawn Park
 
Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Reitit - Clojure/North 2019
Reitit - Clojure/North 2019
Metosin Oy
 
Spring Security 5
Spring Security 5Spring Security 5
Spring Security 5
Jesus Perez Franco
 
Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...
Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...
Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...
Edureka!
 
Angular interview questions
Angular interview questionsAngular interview questions
Angular interview questions
Goa App
 
Springboot Overview
Springboot  OverviewSpringboot  Overview
Springboot Overview
Jose Patricio Bovet Derpich
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
Ignacio Martín
 
REST API 설계
REST API 설계REST API 설계
REST API 설계
Terry Cho
 
Spring boot
Spring bootSpring boot
Spring boot
sdeeg
 
Introduction to git
Introduction to gitIntroduction to git
Introduction to git
Randal Schwartz
 
[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입
[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입
[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입
choi sungwook
 
Certificate 와 Provisioning Profile
Certificate 와 Provisioning ProfileCertificate 와 Provisioning Profile
Certificate 와 Provisioning Profile
ssuser9054541
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency Injection
Theo Jungeblut
 
Jenkins multi configuration (matrix)
Jenkins multi configuration (matrix)Jenkins multi configuration (matrix)
Jenkins multi configuration (matrix)
Muhammad Zbeedat
 
Angular Advanced Routing
Angular Advanced RoutingAngular Advanced Routing
Angular Advanced Routing
Laurent Duveau
 
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
YoungjikYoon
 

What's hot (20)

우아한 객체지향
우아한 객체지향우아한 객체지향
우아한 객체지향
 
이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정
 
Git in 10 minutes
Git in 10 minutesGit in 10 minutes
Git in 10 minutes
 
Introduction to git flow
Introduction to git flowIntroduction to git flow
Introduction to git flow
 
우아한 모노리스
우아한 모노리스우아한 모노리스
우아한 모노리스
 
Reitit - Clojure/North 2019
Reitit - Clojure/North 2019Reitit - Clojure/North 2019
Reitit - Clojure/North 2019
 
Spring Security 5
Spring Security 5Spring Security 5
Spring Security 5
 
Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...
Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...
Git Tutorial | Git Basics - Branching, Merging, Rebasing | Learn Git | DevOps...
 
Angular interview questions
Angular interview questionsAngular interview questions
Angular interview questions
 
Springboot Overview
Springboot  OverviewSpringboot  Overview
Springboot Overview
 
Introduction to Redux
Introduction to ReduxIntroduction to Redux
Introduction to Redux
 
REST API 설계
REST API 설계REST API 설계
REST API 설계
 
Spring boot
Spring bootSpring boot
Spring boot
 
Introduction to git
Introduction to gitIntroduction to git
Introduction to git
 
[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입
[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입
[개인 프로젝트] 쿠버네티스를 이용한 개발환경 자동화 구축시스템 - 프로토타입
 
Certificate 와 Provisioning Profile
Certificate 와 Provisioning ProfileCertificate 와 Provisioning Profile
Certificate 와 Provisioning Profile
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency Injection
 
Jenkins multi configuration (matrix)
Jenkins multi configuration (matrix)Jenkins multi configuration (matrix)
Jenkins multi configuration (matrix)
 
Angular Advanced Routing
Angular Advanced RoutingAngular Advanced Routing
Angular Advanced Routing
 
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
 

Similar to [2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf

반복적인 작업이 싫은 안드로이드 개발자에게
반복적인 작업이 싫은 안드로이드 개발자에게반복적인 작업이 싫은 안드로이드 개발자에게
반복적인 작업이 싫은 안드로이드 개발자에게
Sungju Jin
 
[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초
양재동 코드랩
 
Domain Specific Languages With Groovy
Domain Specific Languages With GroovyDomain Specific Languages With Groovy
Domain Specific Languages With Groovy
Tommy C. Kang
 
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GDG Korea
 
20201121 코드 삼분지계
20201121 코드 삼분지계20201121 코드 삼분지계
20201121 코드 삼분지계
Chiwon Song
 
Spring Framework - Inversion of Control Container
Spring Framework - Inversion of Control ContainerSpring Framework - Inversion of Control Container
Spring Framework - Inversion of Control Container
Kyung Koo Yoon
 
Sql 중심 코드 탈피
Sql 중심 코드 탈피Sql 중심 코드 탈피
Sql 중심 코드 탈피
ssuser776e2d
 
Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)
Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)
Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)
LanarkSeung
 
20140122 techdays mini 앱 개발 세미나(3) - 센서활용 앱 개발
20140122 techdays mini  앱 개발 세미나(3) - 센서활용 앱 개발20140122 techdays mini  앱 개발 세미나(3) - 센서활용 앱 개발
20140122 techdays mini 앱 개발 세미나(3) - 센서활용 앱 개발
영욱 김
 
#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
탑크리에듀(구로디지털단지역3번출구 2분거리)
 
[Study]HeadFirst JSP&servlet chapter5
[Study]HeadFirst JSP&servlet chapter5[Study]HeadFirst JSP&servlet chapter5
[Study]HeadFirst JSP&servlet chapter5
Hyeonseok Yang
 
[1A5]효율적인안드로이드앱개발
[1A5]효율적인안드로이드앱개발[1A5]효율적인안드로이드앱개발
[1A5]효율적인안드로이드앱개발
NAVER D2
 
Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료
ssuser776e2d
 
MVP 패턴 소개
MVP 패턴 소개MVP 패턴 소개
MVP 패턴 소개
beom kyun choi
 
Node.js and react
Node.js and reactNode.js and react
Node.js and react
HyungKuIm
 
아해2019 SpringAOP 문겸
아해2019 SpringAOP 문겸아해2019 SpringAOP 문겸
아해2019 SpringAOP 문겸
MoonGyeom1
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
NAVER Engineering
 
Python server-101
Python server-101Python server-101
Python server-101
Huey Park
 
[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱
NAVER D2
 
MyBatis에서 JPA로
MyBatis에서 JPA로MyBatis에서 JPA로
MyBatis에서 JPA로
Dongmin Shin
 

Similar to [2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf (20)

반복적인 작업이 싫은 안드로이드 개발자에게
반복적인 작업이 싫은 안드로이드 개발자에게반복적인 작업이 싫은 안드로이드 개발자에게
반복적인 작업이 싫은 안드로이드 개발자에게
 
[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초[Codelab 2017] ReactJS 기초
[Codelab 2017] ReactJS 기초
 
Domain Specific Languages With Groovy
Domain Specific Languages With GroovyDomain Specific Languages With Groovy
Domain Specific Languages With Groovy
 
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
GKAC 2015 Apr. - Battery, 안드로이드를 위한 쉬운 웹 API 호출
 
20201121 코드 삼분지계
20201121 코드 삼분지계20201121 코드 삼분지계
20201121 코드 삼분지계
 
Spring Framework - Inversion of Control Container
Spring Framework - Inversion of Control ContainerSpring Framework - Inversion of Control Container
Spring Framework - Inversion of Control Container
 
Sql 중심 코드 탈피
Sql 중심 코드 탈피Sql 중심 코드 탈피
Sql 중심 코드 탈피
 
Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)
Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)
Create-React-App으로 SSR을 구현하며 배운 점 (feat. TypeScript)
 
20140122 techdays mini 앱 개발 세미나(3) - 센서활용 앱 개발
20140122 techdays mini  앱 개발 세미나(3) - 센서활용 앱 개발20140122 techdays mini  앱 개발 세미나(3) - 센서활용 앱 개발
20140122 techdays mini 앱 개발 세미나(3) - 센서활용 앱 개발
 
#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
#19.스프링프레임워크 & 마이바티스 (Spring Framework, MyBatis)_국비지원IT학원/실업자/재직자환급교육/자바/스프링/...
 
[Study]HeadFirst JSP&servlet chapter5
[Study]HeadFirst JSP&servlet chapter5[Study]HeadFirst JSP&servlet chapter5
[Study]HeadFirst JSP&servlet chapter5
 
[1A5]효율적인안드로이드앱개발
[1A5]효율적인안드로이드앱개발[1A5]효율적인안드로이드앱개발
[1A5]효율적인안드로이드앱개발
 
Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료Sql 중심 코드 탈피 발표자료
Sql 중심 코드 탈피 발표자료
 
MVP 패턴 소개
MVP 패턴 소개MVP 패턴 소개
MVP 패턴 소개
 
Node.js and react
Node.js and reactNode.js and react
Node.js and react
 
아해2019 SpringAOP 문겸
아해2019 SpringAOP 문겸아해2019 SpringAOP 문겸
아해2019 SpringAOP 문겸
 
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
[TECHCON 2019: MOBILE - Android]2.예제에서는 알려주지 않는 Model 이야기
 
Python server-101
Python server-101Python server-101
Python server-101
 
[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱[114]angularvs react 김훈민손찬욱
[114]angularvs react 김훈민손찬욱
 
MyBatis에서 JPA로
MyBatis에서 JPA로MyBatis에서 JPA로
MyBatis에서 JPA로
 

[2022]NaverMeetup_[Flutter] Dependency Injection과 Service Locator_임태규.pdf

  • 2. 임태규 • 정보관리 기술사 • 11년차 모바일 엔지니어 (안드로이드, 플러터) • 전) 삼성전자 • 전) 쿠팡 • 현) Presto Labs • https://github.com/dualcoder-pe • https://www.linkedin.com/in/taekyu-lim-b3b629187
  • 3. Presto Labs • Global Top Tier High Frequency Trading Firm • Singapore, Shanghai, Seoul + Remote (Global) • https://aqx.com • Mobile, BE, FE 채용 (링크)
  • 4. 목차 Dependency Injection과 Service Locator Flutter에서 Dependency Injection과 Service Locator 1 2
  • 7. 객체지향 프로그래밍 • 객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러 개의 독립된 단위, 즉 객체들의 모임으로 파악하고자 하는 것이다.
  • 10. 응집도는 높게, 결합도는 낮게 SOLID IoC Dependency Injection Service Locator
  • 12. 예제 프로젝트 구조 App Bloc UseCase Repository Main View DataSource
  • 13. 응집도와 결합도 • 응집도(cohesion) • 모듈 내부 구성요소 간 연관 정도 • 응집도가 높다 ➔ 모듈 내 모든 기능이 단일 목적을 위해 수행됨 • 결합도(coupling) • 모듈과 외부 모듈의 연관 정도 • 결합도가 낮다 ➔ 모듈이 기능 수행을 위해 다른 모듈에 의존하지 않음
  • 14. class SendOrderUsecase { final OrderRepository _orderRepository = OrderRepository(); Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); } 높은 결합도
  • 15. 예제 프로젝트 구조 App Bloc UseCase Repository Main View DataSource
  • 16. class OrderRepository { final LocalDatasource _localDatasource = LocalDatasource(); final RemoteDatesource _remoteDatesource = RemoteDatesource(); Future<OrderResult> sendOrder(Order order) async { final localRes = await _localDatasource.sendOrder(order); final remoteRes = await _remoteDatesource.sendOrder(order); return OrderResult((localRes && remoteRes) ? "SUCCESS" : "Fail"); } } 높은 결합도
  • 18. SOLID • Single Responsibility Principle: 단일 책임 원칙 • Open Closed Principle: 개방 폐쇄 원칙 • Liskov Substitution Principle: 리스코프 치환 원칙 • Interface Segregation Principle: 인터페이스 분리 원칙 • Dependency Inversion Principle: 의존성 역전 원칙 ➔ 구체적인 대상이 아니라, 추상적인 대상에 의존해야 함
  • 19. DIP 적용 UseCase Repository DataSource UseCase Repository DataSource Repository Impl
  • 20. class OrderRepository { final LocalDatasource _localDatasource = LocalDatasource(); final RemoteDatesource _remoteDatesource = RemoteDatesource(); Future<OrderResult> sendOrder(Order order) async { } } abstract class OrderRepository { Future<OrderResult> sendOrder(Order order); } DIP 적용
  • 21. DIP 적용 class OrderRepositoryImpl implements OrderRepository { final LocalDatasource _localDatasource = LocalDatasource(); final RemoteDatesource _remoteDatesource = RemoteDatesource(); Future<OrderResult> sendOrder(Order order) async { final localRes = await _localDatasource.sendOrder(order); final remoteRes = await _remoteDatesource.sendOrder(order); return OrderResult((localRes && remoteRes) ? "SUCCESS" : "Fail"); } }
  • 22. class SendOrderUsecase { final OrderRepository _orderRepository = OrderRepositoryImpl(); Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); } DIP 적용 아직 구체적인 대상에 대한 의존성을 제거하지 못함
  • 23. DIP 적용 UseCase Repository DataSource UseCase Repository DataSource Repository Impl
  • 25. IoC • 애플리케이션의 제어권을 개발자가 갖지 않고 외부에 위임 • Ex) 안드로이드에서 Lifecycle 별로 프레임워크에서 callback 함수를 호출
  • 26. IoC 적용을 위한 패턴 • Template Method • Strategy • Factory Method • Abstract Factory • Dependency Injection • Service Locator ➔ Interface 구현체와, Interface를 사용하는 App Code를 분리
  • 28. • 의존성 주입 • 객체가 의존성을 외부에서 전달받는 패턴 Dependency Injection UseCase Repository DataSource Repository Impl 누군가
  • 29. • 의존성 주입 유형 • 생성자 주입 • Setter 주입 • 인터페이스 주입 Dependency Injection
  • 30. • 객체의 생성자를 통해 외부에서 의존성을 주입 생성자 주입 class SendOrderUsecase { final OrderRepository _orderRepository; SendOrderUsecase(this._orderRepository); Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); }
  • 31. class SendOrderUsecase { late OrderRepository _orderRepository; set orderRepository(OrderRepository orderRepository) => _orderRepository = orderRepository; Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); } • 객체를 생성한 뒤, 인스턴스의 setter를 통해 의존성을 주입 Setter 주입
  • 32. class SendOrderUsecase { OrderRepository? _orderRepository; set orderRepository(OrderRepository orderRepository) => _orderRepository = orderRepository; Future<OrderResult> sendOrder(Order order) => _orderRepository?.sendOrder(order) ?? Future.value(OrderResult("Fail")); } • 객체를 생성한 뒤, 인스턴스의 setter를 통해 의존성을 주입 Setter 주입
  • 34. 생성자 주입 – Pure DI void main() { runApp( OrderApp(orderBloc: OrderBloc(SendOrderUsecase(OrderRepositoryImpl())))); }
  • 35. Setter 주입 - Pure DI void main() { runApp(OrderApp( orderBloc: OrderBloc( SendOrderUsecase()..orderRepository = OrderRepositoryImpl()))); }
  • 36. • 원인 • Composition Root가 Entry Point와 가까워야 함 • 한계 • 의존관계 tree의 depth가 깊을 때, 안쪽까지 객체를 전달하기 불편함 Pure DI의 한계 App Bloc UseCase Main View DataSource Repository Repository Impl 누군가 가까이 Composition Root Entry Point Depth
  • 38. • 애플리케이션에 필요한 모든 객체를 알고 있는 객체를 만들고, 그 객체를 통해 의존성을 획득하는 방법 Service Locator App Bloc UseCase Main View DataSource Repository Repository Impl 누군가 Composition Root Entry Point 어딘가 Service Locator
  • 39. • Service Locator의 유형 • Static Service Locator • Dynamic Service Locator Service Locator
  • 40. class ServiceLocator { static ServiceLocator? _instance; final OrderRepository _orderRepository; ServiceLocator(this._orderRepository); static void load(ServiceLocator serviceLocator) => _instance = serviceLocator; static OrderRepository getOrderRepository() => _instance!._orderRepository; } Service Locator 구현
  • 41. Service Locator 객체 생성 void main() { ServiceLocator.load(ServiceLocator(OrderRepositoryImpl())); runApp(OrderApp(orderBloc: OrderBloc(SendOrderUsecase()))); }
  • 42. class SendOrderUsecase { final OrderRepository _orderRepository = ServiceLocator.getOrderRepository(); Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); } Service Locator 의존성 획득
  • 43. Dynamic Service Locator 구현 class DynamicServiceLocator { static DynamicServiceLocator? _instance; final HashMap<String, dynamic> _services = HashMap(); static void load(DynamicServiceLocator serviceLocator) => _instance = serviceLocator; void loadService(String key, dynamic object) => _services[key] = object; static dynamic getService(String key) => _instance!._services[key]; }
  • 44. void main() { DynamicServiceLocator locator = DynamicServiceLocator(); locator.loadService("OrderRepository", OrderRepositoryImpl()); DynamicServiceLocator.load(locator); runApp(OrderApp(orderBloc: OrderBloc(SendOrderUsecase()))); } Dynamic Service Locator 객체 생성
  • 45. class SendOrderUsecase { final OrderRepository _orderRepository = DynamicServiceLocator.getService("OrderRepository"); Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); } Dynamic Service Locator 의존성 획득
  • 47. • 종속성을 숨김 • 단위테스트 어려움 • mocking이 가능하더라도, 전역 메모리 사용하므로, 테스트 후 clear 필요 • 전역 싱글톤 패턴 • 구현 상 전역 싱글톤을 안쓰더라도, 내부적으로 공유 메모리 사용을 피할 수 없음) • 모든 클래스가 로케이터와 커플링 • Runtime Error • Locator의 존재를 모르고 생성을 안하고 사용하면 런타임 에러 • 의존 객체를 찾기 어려움 • 내부에 의존성 추가되면 컴파일러가 알 수 없음 • 캡슐화 위반 • 객체 사용을 위한 전제 조건을 알 수 없음 Service Locator의 단점
  • 49. • GetX • GetIt • InheritedWidget • Provider/Riverpod ➔ InheritedWidget, Riverpod은 상태관리 Flutter DI 라이브러리
  • 50. void main() { Get.lazyPut<OrderRepository>(() => OrderRepositoryImpl()); runApp(OrderApp(orderBloc: OrderBloc(SendOrderUsecase()))); } GetX의 Injection 방식
  • 51. GetX의 Injection 방식 class SendOrderUsecase { final OrderRepository _orderRepository = Get.find(); Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); }
  • 53. DI와 GetX의 비교 – DI class OrderApp extends StatelessWidget { const OrderApp({Key? key, required this.orderBloc}) : super(key: key); final OrderBloc orderBloc;
  • 54. class OrderBloc { final SendOrderUsecase _sendOrderUsecase; OrderBloc(this._sendOrderUsecase); DI와 GetX의 비교 – DI
  • 55. class SendOrderUsecase { final OrderRepository _orderRepository; SendOrderUsecase(this._orderRepository); DI와 GetX의 비교 – DI
  • 56. class OrderRepositoryImpl implements OrderRepository { final LocalDatasource _localDatasource; final RemoteDatasource _remoteDatesource; OrderRepositoryImpl(this._localDatasource, this._remoteDatesource); DI와 GetX의 비교 – DI
  • 57. void main() { runApp(OrderApp( orderBloc: OrderBloc(SendOrderUsecase( OrderRepositoryImpl(LocalDatasource(), RemoteDatasource()))))); } DI와 GetX의 비교 – DI
  • 58. class OrderApp extends StatelessWidget { OrderApp({Key? key}) : super(key: key); final OrderBloc orderBloc = Get.find(); DI와 GetX의 비교 – GetX
  • 59. DI와 GetX의 비교 – GetX class OrderBloc { final SendOrderUsecase _sendOrderUsecase = Get.find(); class SendOrderUsecase { final OrderRepository _orderRepository = Get.find(); class OrderRepositoryImpl implements OrderRepository { final LocalDatasource _localDatasource = Get.find(); final RemoteDatasource _remoteDatesource = Get.find();
  • 60. void main() { Get.lazyPut(() => OrderBloc()); Get.lazyPut(() => SendOrderUsecase()); Get.lazyPut<OrderRepository>(() => OrderRepositoryImpl()); Get.lazyPut(() => LocalDatasource()); Get.lazyPut(() => RemoteDatasource()); runApp(OrderApp()); } DI와 GetX의 비교 – GetX
  • 61. DI와 GetX의 비교 App Bloc UseCase Main View DataSource Repository Repository Impl 누군가 Composition Root Entry Point Service Locator 누군가 어딘가 Composition Root DI GetX
  • 64. GetX를 DI Container로 활용하기 App Bloc UseCase Main View DataSource Repository Repository Impl 누군가 Composition Root Entry Point Service Locator 누군가 어딘가 Composition Root DI with GetX GetX 어딘가 Get
  • 65. • DI는 원칙과 패턴 • 지금까지 본 DI는 Pure DI • 외부 라이브러리나 객체 사용 없이 의존성 주입 • DI Container • 의존성 주입할 모든 객체를 관리하는 모듈 • 역할 (RRR) • Register: 인스턴스 등록 • Resolve: 인스턴스 제공 • Release: 인스턴스 해제 Pure DI와 DI Container
  • 66. class Injector { static void register() { Get.lazyPut(() => LocalDatasource()); Get.lazyPut(() => RemoteDatasource()); Get.lazyPut<OrderRepository>( () => OrderRepositoryImpl(Get.find(), Get.find())); Get.lazyPut(() => SendOrderUsecase(Get.find())); Get.lazyPut(() => OrderBloc(Get.find())); } } GetX를 DI Container로 활용하기
  • 67. GetX를 DI Container로 활용하기 void main() { Injector.register(); runApp(OrderApp(orderBloc: Get.find())); }
  • 68. GetX를 DI Container로 활용하기 class OrderApp extends StatelessWidget { const OrderApp({Key? key, required this.orderBloc}) : super(key: key); final OrderBloc orderBloc;
  • 69. class OrderBloc { final SendOrderUsecase _sendOrderUsecase; OrderBloc(this._sendOrderUsecase); GetX를 DI Container로 활용하기
  • 70. class SendOrderUsecase { final OrderRepository _orderRepository; SendOrderUsecase(this._orderRepository); GetX를 DI Container로 활용하기
  • 71. class OrderRepositoryImpl implements OrderRepository { final LocalDatasource _localDatasource; final RemoteDatasource _remoteDatesource; OrderRepositoryImpl(this._localDatasource, this._remoteDatesource); GetX를 DI Container로 활용하기
  • 72. • 장점 • GetX의 Memory Management 기능 활용 가능 • Entry Point에서 가까운 Composition Root에서 의존관계 관리 • 의존관계 tree의 depth가 깊어도, 안쪽까지 객체를 쉽게 전달 • 단점 • GetX에 등록을 잊는다면, 여전히 Runtime Error 발생 가능 GetX를 DI Container로 활용하기
  • 73. 예외적으로 Composition Root가 아닌 곳에서 주입하고 싶다면?
  • 74. class SendOrderUsecase { final OrderRepository _orderRepository; SendOrderUsecase([OrderRepository? orderRepository]) : _orderRepository = orderRepository ?? Get.find(); Future<OrderResult> sendOrder(Order order) => _orderRepository.sendOrder(order); } GetX를 DI Container로 활용하기
  • 75. • 예외적으로 생성자에서 객체 생성해 주입 허용 • 외부에 의존관계 알림 • 테스트 용이 • 객체 주입 용이 • 단, 모든 객체가 GetX 의존성 가짐 GetX를 DI Container로 활용하기
  • 76. • 원인 • 객체를 생성 및 조립한 후 변경이 불가능함 • 한계 • 객체 생성에 Runtime 요소가 필요할 때 대응이 어려움 • 보완 • 추상 팩토리 메서드 패턴 활용 한계 및 보완
  • 77. • 의존성 있는 객체간 결합도를 낮추는 방법 • DI: 생성자 주입, Setter 주입 • Service Locator: 의존성 필요한 객체가 컨테이너에 객체 요청 • Service Locator는 안티패턴이라는 주장 (Mark Seemann) • Flutter의 의존성 주입 (GetX, GetIT) ➔ Service Locator 방식 • GetX, GetIt을 DI Container로 활용할 수 있음 • 객체 생성에 Runtime 요소가 필요할 경우, 추상 팩토리 패턴으로 보완 정리
  • 78. Q & A