-
Ribs 에 대해서...Swift/아키텍쳐 2021. 9. 30. 15:32
Ribs
Uber 에서 만든 아키텍쳐 패턴으로, MVC (Massive ViewController 라고 하는...ㅠ) 패턴에 한계 에서 VIPER 패턴을 바탕으로 만든 패턴입니다.
깃헙 : https://github.com/uber/RIBs
짧게 우버에서 얘기하는 시작의 이유는 아래와 같습니다.
1. 신뢰성
최소한의 장애를 위해 핵심 코드에 대한 엄격한 검토 프로세스, 그 외는 핵심 비즈니스를 중단하지 않는지 확인하고
이러한 코드 분리로 새로운 기능을 시험하고 제대로 작동하는지 테스트한다.
2. iOS와 Android 가 통합된 기반에서 작업할 수 있는 호환성
동일한 패턴으로 더 쉽게 협업하고 동시에 작업할 수 있도록 작업.
아래가 두 플랫폼이 공유하는 내용입니다.
- Core architecture
- Class names
- Inheritance relationships between business logic units
- How business logic is divided
- Plugin points (names, existence, structure, etc.)
- Reactive programming chains
- Unified platform components
(핵심 아키텍쳐, 클래스 이름, 비즈니스로직을 어떤식으로 쪼개는지, 플러그인 포인트, 통합 컴포넌트 등등)
해당 아키텍쳐를 구성하면서...
우버가 해당 아키텍쳐를 구성하면서 아래와 같은 고민을 거쳤다고 설명하고 있습니다.
MVC 의 1. massive ViewController / 너무 많은 책임과 3000라인이 넘어 읽이 어려워진 문제
2. 테스트가 취약한 업데이트 힘든 프로세스 / 테스트 코드 작성에 If else문으로 코드 작성
VIPER가 Clean Architecture 의 방식으로 사용될 수 잇다고 생각
[이점]
1. 더 많은 추상화 제공
2. VIPER 접근 방식에서 Presenter와 Interactor는 평범하고 오래된 객체로 간단한 단위 테스트 사용 가능
VIPER [단점]
1. iOS 전용 구조로 인해 Android에 대해 절충안이 필요.
2. View-driven application logic 으로 비즈니스 로직만 있는 Viper 구현이 어렵다.
2. 애플리케이션의 상태롤 조작해야하는 Interactor가 수행하는 비즈니스 로직이 항상 Presenter를 거쳐야 하므로 비즈니스 로직 이 누출된다.
3. 뷰와 비즈니스 가 밀접하게 결합되어 있어 비즈니스 로직만 포함하거나 / 뷰로직만 포함하기가 어렵다!
Ribelts
그래서 탄생한 단일 책임 원칙에 따른 단일 목적을 가진 작고 독립적인 테스트 조각으로 나누는 Ribelts 입니다.
6개의 구성요소에 각각 책임을 부여했습니다.
모든 기능을 만들때 Rib 을 만든다고 생각하면 된다.
Builder
모든 기본 Riblet단위를 인스턴스화 하고 종속성을 정의힌다. 즉, Router, Interactor, View, Component를 모두 생성하고 DI를 정의합니다.
Factory 패턴으로 각 클래스 생성 로직이 들어간다고 보면 됨. 어떤 DI 프레임워크를 쓰던지
Component
부모 RIB Builder가 Component 통해 자식 RIB Builder로 의존성을 주입시켜준다.
여기서 Data Stream 을 포함하기도 하고, 이를 적절한 네트워크 이벤트에 연결해서 interactor에 주입한다.
Router
자식 RIB을 attach, detach 해서 Ribs 트리 구조를 형성한다. (이동 화살표)
Helper for routing between RIBs
Interactor
Business logic, 위의 그림을 참고해보면 Router로 Routing calls 하고, Presenter로 Data Model을 전달한다.
아래 두가지는 Optional한 Component이다. -> 이 뜻은 View 없이 비즈니스 로직만의 Viewless RIB가 가능하다는 말이다.
Presenter
Interactor와 View 간의 통신을 담당하여 Business model을 View model로 변환하는 역할로 상태를 가지고 있지 않는 클래스이다.
즉 주로 Translation을 담당한다고 보면 되는데, view가 이해할 수 있도록 model 로직을 만들어서 주거나, View에 일어난걸 interactor에 알릴때도 필요한 가공을 해서 보낼 수 있다.
Presenter가 생략되는 경우에는 ViewModel 변환은 VIew또는 Interactor가!
View
알다시피, UI event, View Model을 받으면 UI를 업데이트
아래가 프로토콜 기준으로 나눠진 기능이다.
부모 RIB와 자식 RIB 통신은 각 Interactor가 담당하고 있음을 알 수 있다.
부모에서 자식은 Observable stream을 넘겨 데이터를 전달하고
자식에서 부모는 부모 interacotor의 Listener 인터페이스로 접근하게 된다.
또다른 참고 그림
Ribs 아키텍쳐에 대해서 공부하면서 아래를 참고/정리했습니다.
https://eng.uber.com/new-rider-app-architecture/: 우버 블로그
https://www.youtube.com/watch?v=FmnshYCDeW8 : 타다를 개발하면서 Ribs 를 적용했던 경험을 설명해주시면서 해당 아키텍쳐에 대한 정리를 해주심
https://blog.mathpresso.com/1-ios-ribs-architecture-af9834956daf : 메스프레스(콴다) 의 iOS 개발자 분의 설명
https://www.youtube.com/watch?v=3XS6xLzKRjc : 카카오뱅크 개발자 분이 소개하나는 Ribs
* RIBS의 장점은 state 관리를 아주 잘 할 수 있다는 점이다.
Application 에서 모든 state를 트리로 만들어 보는 것이다. (위는 Uber의 예시)
=> Ribs가 흔히, 의존성 트리. 의존성 그래프라 불리는 것을 강제적으로 만들어주는 셈이 된다. 부모에 자식들(RIB)를 넣어주게 됨.
Ribs를 사용해보면서 내가 느끼는 점....
장점
은 위에 언급한 대로다. 특히, 프로토콜 사용이 정말 잘 되어있다는 점과 명확한 클래스 분리가 장점인것 같다.
view없이도 가능한거, 위처럼 의존성 트리가 강제되는점. <- 미리 설계를 같이 하게 되기 때문에도 이점이 되는것 같다.
rib트리를 만들고 각 rib을 만드는 식으로 구성하면 일을 나누는거에도 좀 좋다.
단점
하나의 상태를 표현하기 위해도 적어도 세개의 클래스가 생김으로 파일이 정말 많다. (코드는 짧지만)
좀 프로토콜이 많은데 각 역할을 모두가 잘 인지하고 상태를 잘 관리할 수 있어야 한다.
내 기준 러닝커브가 좀 있다.
알아야되는 점
비즈니스 로직에 따라서 rib가 결정되고 state가 관리된다고 이해하면 조금 쉽다. 하나의 화면 하나의 뷰 컨트롤러로 이루어지지 않고 (뷰를 기준으로 트리가 작성되지 않으니까) 어떤 기능으로 묶고 그 아래 rib들이 존재하는 식의 구조를 이해해야한다.
그리고 또다른 참고 그림이었던 밑에 그림이 큰 틀로 꼭 이해해두고 시작하면 좋다고 생각해 한번 더 첨부한다.
아직 나지 않은 결론...
clean architecture에서 data layer , domain layer, UI layer 나누어서 use case를 활용한 방식과 고민이 된다. 해당 아키텍쳐는 템플릿으로 강제되는 등의 이점은 없지만 강제성과 자유도 중 유연하게 작성하는것이 좋다고 생각한다면 ribs보다는 clean architecture가 맞는거 같기도...
728x90'Swift > 아키텍쳐' 카테고리의 다른 글
Ribs 예제 도입기 - Root편 (0) 2021.10.15 Ribs 예제 도입기 - Ribs 설치 편 (0) 2021.10.15 Clean Architecture (0) 2021.06.18 ReactorKit (0) 2021.06.18 Clean Swift (0) 2021.03.14