티스토리 뷰
[DI] DI Tree, DI Graph (의존성 트리, 의존성 그래프)를 만드는 방법
eungding 2020. 1. 17. 21:58수열님은 의존성 그래프, 우버(RIBs)에서는 의존성 트리 라는 용어를 주로 쓰시던데 같은 용어인 것 같습니다.
의존성 트리란 이렇게 의존 관계이지만 흩어져있는 앱 내의 상태들 혹은 클래스들을
이렇게 예쁜 트리형태로 만들어주는 것을 의미합니다.
이렇게 모든 클래스가 의존성 트리에 속하게 되면 Unit Test도 편하고(Stub을 주입해주기 편하니까)
Layering도 확실하게 되어서 각 클래스는 자신이 알아야할 데이터들만 딱 알게 되는 아름다운 그림이 펼쳐집니다.
iOS에서 이런 의존성 그래프를 만들어주는 방법(또는 라이브러리)를 정리해보고자 합니다.
[1] RIBs
천재 우버가 만든 RIBs....!!
(강제) 프로토콜 지향 RIBs을 도입해서 컴파일 에러를 쭉쭉 해결하면서 코딩해가면 아름다운 RIB 트리가 펼쳐집니다.
제가 진행한 예제프로젝트를 기반으로 설명하자면,
1.1 RIB들 간의 의존관계(부모, 자식 관계)는 Builder와 Router로 만들어집니다.
1.2 RIB들간의 데이터 의존 관계(RIB에서 RIB으로 데이터를 주입해주기)는 Componet와 Dependency로 만들어집니다.
RIBs에서는 부모 RIB의 Componet를 자식 RIB의 Dependency로 주입해줍니다.
이 관계가 어떻게 만들어지냐면...!!
부모 RIB의 Component (LoggedInComponent)는 자식 RIB의 Dependency 프로토콜을 따라야합니다.
그래야지 build함수에서 자식 Builder의 dependency로 component를 넣어줄 수 있거든요!! (회색줄)
MemosBuilder의 dependency는 MemosDependency 타입인데, LoggedInComponent가 해당 프로토콜을 따르므로 넣어줄 수 있는 겁니다.
그래서 부모입장에서는 Component에 자식들이 접근해도 되는 & 내가 자식들에게 노출시켜도 되는 프로퍼티를 internal 타입으로 선언합니다. (노출시키면 안되는 것은 fileprivate으로 선언.)
자식입장에서는 Dependency에 부모에게 받아와야하는 것을 선언합니다. 그러면 해당 Dependecy 프로토콜을 따르고 있는 부모 Component는 요청한 것들을 구현해야되겠죠?+?
예제를 살펴봅시다.
아래 그림과 같이 LoggedInRIB이 MemosRIB에게 email을, MemosRIB이 AddMemoRIB에게 email을 주입해줘야한다고 가정해보겠습니다.
1) LoggedInRIB > MemosRIB
🙆♀️ 부모(LoggedInRIB): 나는 email이라는 데이터를 확실하게 가지고 있는데, 자식들에게 노출시킬래. 가져다 쓰려면 써도 된다.
🙋♀️ 자식(MemosRIB): 부모로부터 email이 필요하다!!
2) MemosRIB > AddMemoRIB
🙆♀️ 부모(MemosRIB): 나는 email이라는 데이터를 확실하게 가지고 있다. 'MemosDependency프로토콜을 따르는 나의 부모 RIB'이 dependency로 주입되었는데, 거기서 부터 받은 데이터라고 할 수 있지. 자식들에게 노출시킬테니 가져다 써도 된다.
🙋♀️ 자식(AddMemoRIB): 부모로부터 email이 필요하다!!
더 자세한 내용을 보고 싶다면 9. email을 MemosRIB에게 까지 주입해주기 👈 여기를 봐주세요 : )
[2] Pure DI
앞서 RIBs에서는 의존관계를 각각 RIB에서 설정 해줬습니다.
- 부모 자식 의존 관계 설정은 두 RIB의 Builder와 Router 를 통해,
- 데이터 의존 관계 설정은 두 RIB의 Componet와 Dependency를 통해.
그래서 여러 RIB의 Builder 코드를 보고 전체적인 부모-자식 의존관계를 파악했고
여러 RIB의 Componet와 Dependency 코드를 보고 전체적인 데이터 흐름 관계를 파악했었죠.
하지만 Pure DI는 의존관계를 한방에(?) 진입점에서(?) 설정해주는 방법입니다.
예를 들어 이런 의존성 트리라면
이렇게 Constructor Injection을 이용하여 한방에 트리를 다 만들어버리는(의존관계를 다 설정해버리는) 방법입니다.
우선 Pure DI는 앱에서만 쓰이는 개념은 아닙니다.
웹에서도 쓰입니다.
https://www.kenneth-truyers.net/2014/11/19/how-to-use-pure-di/
그럼 이제 Pure DI 개념을 iOS 앱에 적용해봅시다.
Composition Root(앱의 의존성 그래프가 만들어지는 곳)은 어디일까요?!?
바로바로 프로그램의 진입점, iOS에서는 AppDelegate 라고 할 수 있습니다.
Entire object dependency graph를 Composition Root 에서 다 그려버리는 것이 굉장히 신기합니다. 😯
그럼 어떤 식으로 사용할 수 있을까요?!?
AppDelegate는 AppDependency라는 것을 주입받습니다.
앱 환경일때는 resolve라는 함수를 이용해서 알아서 주입해주고, 테스트환경 일때는 생성자에 주입해주도록 만들었습니다.
AppDependency는 기본적으로 이렇게 생겼습니다.
이것저것 필요한 것을 추가하다보면 이런식으로 점점 코드가 늘어날 것입니다.
예를들어 ListViewController가 있는데, 사용자가 이 뷰컨안의 item을 클릭했을 때, DetailViewController가 만들어져야한다고 생각해봅시다. ListViewController에 DetailViewController의 factory 클로져를 넘겨줘야할 것입니다.
하지만 많은 의존관계가 생긴다면 factory 클로져가 굉장히 복잡해지게 됩니다.
그리고 Cell configure에 관련된 configurator 클로져까지 더 하면 복잡해집니다.
그래서 Pure 라는 라이브러리가 나오게 되었다고 하네요 :- )
Pure makes factories and configurators neat.
(위의 PureDI - iOS 관련 설명은 여기서 첨부한 것입니다. 이 레포지토리는 Pure에 대한 소개뿐만아니라 iOS 앱에서 Pure DI를 적용하는 방법에 대해서도 나와있어요...!!!)
https://github.com/devxoul/Pure#problem
Pure DI에 대해 설명해주는 피피티도 좋아요 :-)
https://speakerdeck.com/devxoul/regeosi-peurojegteueseo-yijonseong-juibhagi?slide=100
[3] Weaver
너무너무 신기한 Weaver 라는 라이브러리를 발견했다.
충격의 1초영상....!!
https://www.youtube.com/watch?v=Rxhc9VJBoOI&feature=youtu.be
Annotation을 이용한 라이브러리인데, AppDelegate에 dependencies를 등록하고, scope를 정하고 등등을 해서
의존성 그래프를 만들어간다.
Dependency Graph를 export하는 기능도 제공한다 (예제) 🤭 🤭
'🍏 > Architecture, DesignPattern' 카테고리의 다른 글
[Design Pattern] Repository패턴이란 (0) | 2020.07.17 |
---|---|
[CleanSwift] 클린스위프트(Clean Swift)에 대해 알아보자 (1) | 2020.04.29 |
[DI] 의존성 주입(Dependency Injection) 을 해주는 세가지 방법 (0) | 2020.01.17 |
[Architecture] iOS 앱 아키텍쳐 2탄 (ReactorKit/RIBs/Clean Architecture/CleanSwift) (0) | 2020.01.16 |
[ReactorKit] ReactorKit 예제 따라하기 (0) | 2019.11.05 |
- Total
- Today
- Yesterday
- 장고 Custom Management Command
- flutter 앱 출시
- ribs
- Flutter getter setter
- flutter dynamic link
- Django FCM
- Django Heroku Scheduler
- 플러터 얼럿
- Sketch 누끼
- ipad multitasking
- Flutter Clipboard
- SerializerMethodField
- Flutter Text Gradient
- 장고 URL querystring
- Watch App for iOS App vs Watch App
- 구글 Geocoding API
- DRF APIException
- drf custom error
- github actions
- PencilKit
- 플러터 싱글톤
- Flutter Spacer
- Python Type Hint
- METAL
- Dart Factory
- flutter deep link
- Django Firebase Cloud Messaging
- flutter build mode
- cocoapod
- Flutter 로딩
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |