클린 아키텍쳐를 읽기 전에
- 클린 아키텍쳐란 무엇이고
- 왜 등장하게 되었는지
- 어떤식으로 Swfit project에 적용할 수 있는지
알아보자.
클린 아키텍쳐
클린 아키텍쳐: 클린코드의 저자 엉클밥이 제시한 아키텍쳐
- 맨 바깥쪽의 원: 하위레벨의 메커니즘
- 안쪽으로 갈 수록 상위레벨의 정책적인 부분
밖에서 안으로 가는 화살표: 의존성은 밖에서 안으로 -> 안쪽원은 바깥쪽에 의존해서는 안된다!
eg) 밖의 디비를 어떤걸 쓰는지는 안쪽에서 몰라야 한다.
초록색 부분 Interface Adapter의 역할
- 하위 레이어와 상위 레이어 사이 데이터 포메팅, 이벤트를 받아서 사용자에게 전달
- 이벤트 발생 -> 컨트롤러가 받음 -> 인터렉터에게 명령을 내림 -> 인터렉터가 비즈니스 로직 처리 -> 결과를 프리젠터에게 전달 -> 프리젠터는 포멧팅 하여 UI에게 전달
인터렉터가 프레젠터를 호출해야하는데 호출 하려면 인터렉터가 프리젠터의 디테일을 알아야 한다 -> 그 말은 인터렉터가 프리젠터에게 의존하게 된다. -> 근데 그러면 상위 레이어가 하위 레이어 의존하면 안되는데? -> 해결하기 위해 DIP 사용 ( <I> 표시 )
결과적으로 의존성은 바깥에서 안쪽으로 향한다.
결국 도메인별 레이어를 분리하고 단방향으로 의존성을 설정해서 변경에 유연하게 대응할 수 있도록 한다는 개념인것 같다.
MVVM 이나 Ribs VIPER과 같은 Swift 아키텍쳐와 비교했을때 더 추상적인 얘기를 하고 있는것 같다.
어쩌면 Clean Architecture은 일종의 principle이고 implementation이 MVVM, Ribs, VIPER과 같은 패턴이 될 수 있을 것 같은데 책에서도 SOLID 원칙과 컴포넌트, 경계에 대한 내용이 있는걸 보면 추구해야 하는 이상적인 아키텍쳐를 설명하고 있고 상황, 장단점에 따라 타협을 통해 패턴을 만든 것들이 지금의 MVP, MVVM, Ribs 등등 일까
원론적으로 유연한 프로그램을 만들기 위해 좋은 아키텍쳐가 필요하고
유연한 프로그램은 수정/변경/유지보수가 쉬워야 하고
변경을 쉽게 하기 위해서는 기능을 수정할때 고쳐야 하는 코드의 영역이 적고 명확해야 한다.
수정되는 코드의 영역이 적으려면 경계를 작게 나누어야 하고
각 컴포넌트들은 서로의 구체적인 implementation을 모른체 소통해야 한다.
수정되는 코드의 영역이 명확하기 위해서는 책임이 명확해야 하고
큰 하나의 책임 대신 여러개의 작은 책임으로 나누어 상호작용 해야한다.
이 생각을 가지고 프로그램을 만들다 보니 여러가지 패턴이 생겨났고
클린 아키텍쳐도 구체적인 구현방식까진 아니지만 좋은 아키텍쳐가 가져야할 조건을 설명하는 것 같다.
왜 클린 아키텍쳐가 만들어짐?
찾아보니 딱히 자료가 없고 이글을 발견했는데
간략하게 내용은 이렇다.
1970년대 소프트웨어는 GUI가 없었다.
80년대 들어서 GUI가 늘어나기 시작했고 새로운 문제점들이 등장했다.
- user input의 처리
- view state를 application 내부와 어떻게 sync 할지
- presentation logic 과 domain logic을 어떻게 분리할지
처음 등장한 클린 아키텍쳐는 MVC 였다.
1980년대 smallTalk (추후에 objective-c의 근간이 되었다)에서 사용되었다.
초기엔 이런 문제에 대해서 좋은 방법이 없었지만 지금은 data binding, Rx, architecture compoenets와 같은 툴들이 등장했고 MVC, MVP, MVVM 과 같은 아키텍쳐 패턴이 늘어났다.
음 그럼 MVP, MVVM 과 같은 것들은 '아키텍쳐 패턴'이라고 부르는 구나
결국 왜 등장했는지는 오랜 선조 프로그래머들의 고민을 통해 점점 진화되어 지금의 클린 아키텍쳐 책의 내용까지 방법론이 성립된 것으로 이해했다.
어떤 식으로 Swift project에 적용 가능할까
실제 clean architecture 를 swift에 적용한 예시 중 star 많이 받은걸 보면 두개 정도 찾을 수 있는데
하나는 ios-Clean-Architecture-MVVM 이고
또 하나는 Clean Swift (aka VIP) 이다.
먼저 Clean Swift를 보면
Clean Archietecture를 iOS에 적용하여 패턴화 시킨게 Clean Swift 이다.
- ViewController는 이벤트를 받고
- Interactor는 비즈니스 로직을 처리하고
- Presenter는 화면에 표시할 데이터를 포메팅 한다.
VIP 사이클이라고 한다.
프로토콜을 레퍼런싱을 하여 여러 타입으로 교체가 가능하다.
메소드를 호출할때 파라미터를 넣는데 여기서는 다른 방식이다.
파라미터를 하나의 struct를 만들어 하나만 넘기고 있다.
- Request
- Response
- ViewModel
장점
1. 메소드 파라미터가 늘어나더라도 메소드 시그니처 수정 불필요
2. 타입 선언을 보면 데이터 쉽게 파악 가능
파란색: class
초록색: 프로토콜
민트색: 데이터 모델
Router: VC가 소유하는데 화면 전환을 담당한다.
1. 새 화면을 띄워주기
2. 새 화면에 필요한 데이터를 주입해주기
worker: Interactor가 소유
- worker 있는 이유: Interactor는 재사용성이 떨어짐 - VIP 사이클에 묶이기 때문에
- 그래서 공통 로직은 worker를 따로 둔다
코드레벨에서 예제 프로젝트인 CleanStore을 보자면
- Models: 코어 데이터 프로퍼티들
- Services: 코어 데이터, 서버 api 에서 데이터 fetch 로직
- Workers: 공통 로직인 order 추가 수정에 관한 function과 DIP를 위한 프로토콜
- Scences에서는 화면 단위로 나누어져 있고 각각은
- ViewController
- Interactor: 비즈니스 로직 담당
- Presenter: UI에 뿌릴 데이터를 포메팅
- Models: use case 별로 enum으로 선언하고 Request, Response, ViewModel 세가지 구조체를 가짐
- Worker: doSomeWork() 메서드만 가지는데 boilderplate 인가
- Router: 화면전환 담당
결국 VIP 사이클의 단방향성으로 state를 관리하고 Router, Interactor, worker 등의 구성요소로 책임을 분리한다.
External Interfaces에 해당되는 코어데이터에서 fetch한 데이터 또는 서버에서 가져오는 데이터 또한 `OrdersStoreProtocol`와 `OrderStoreUtilityProtocol`을 통해 추상화 하여 어떤 곳에서 fetch하던 내부 레이어에서는 모르도록 한다.
특이한 점은 Models인데 Use case 별로 Request, Response, ViewModel을 가지는게 장점이자 단점이 될 듯 하다.
사실 UseCase가 아직 정확하게 뭔지 모르겠다.
Use cases 는 "시스템의 동작을 사용자의 입장에서 표현한 시나리오" 이다.
예를들어 영화앱에서는 Use cases를 생각해볼 때
내가 사용자라면 이 앱으로 영화를 검색한다.
이 출력을 생성하기 위한 처리 단계를 기술하는 곳이 Use cases이고 Use cases Interactor라고도 한다.
(Interactor가 비즈니스 로직을 담당하는건데 비즈니스로직이랑 Use cases랑 차이점이..?)
(출처: https://zeddios.tistory.com/1065)
두번째로 ios-clean-architecture-MVVM 을 보자
Clean Store 보단 Readme 가 친절하다.
iOS Project implemented with Clean Layered Architecture and MVVM. (Can be used as Template project by replacing item name “Movie”).
More information in medium post
:
Medium Post about Clean Architecture + MVVM
설명에 따르면 Clean Layered Architecture 에다가 MVVM을 적용했다고 설명하는데 여기서 Clean Layered에 눈이 간다.
소프트웨어의 레이어를 크게
위 4가지로 나누는게 Clean Architecture의 핵심같다.
아니 근데 Presenters와 UI를 묶어서 MVVM 으로 해버리면 Layer를 나누지 않는 것 같은데
Model-View-ViewModel에서 가장 바깥 Layer가 View이고 Presenter layer가 데이터 포멧팅을 담당하는 ViewModel이 될 듯 하다.
한가지 애매한건 Model이 Entities에 속하지 않는게 의아하다.
여하튼 그래서 프로젝트 구조를 보면
- Domain
- Entities
- UseCases
- Interfaces
- repositories
- Presentation
- VIewModel
- View
- Data
- repositories
- network
- ...
- Infrastructure
- Network
로 Network를 따로 나누고 Presentation쪽에 익숙한 MVVM이 들어가 있다.
일반적으로 비즈니스 로직이 ViewModel에 들어가게 되는데 여기서는 Use Cases가 Interactor의 개념을 가지게 되니까 UseCases에 비즈니스 로직이 있을까?
영화 검색에 대한 리퀘스트가 command pattern 으로 구현되있다.
use cases이긴 하나 영화 검색 동작을 실행하며 Repository pattern으로 검색 데이터를 캐시된 메모리에서 가져올지 아니면 서버에서 들고올지 처리할 수 있을 것 같은데 이 구조가 아니었다면 나는 viewModel에 넣었을 것 같은 로직이다.
그 밖에 Presentation쪽에서 Coordinator pattern을 사용하고 있고
특이한 점이 Entity를 나눠서 그런건지 Network 쪽에서 DTO를 사용하고 있다.
Network의 DataMapping과 Persistent Storage의 Entity mapping에서 extension 함수인 toDomain()을 통해 Entity로 나눠주면서 충실하게 Repository pattern 을 구현하고 있다.
안드로이드 쪽에서 DTO 를 사용하는 걸 많이 봤는데 Swift에서는 DTO를 나누는걸 잘 보지 못했다.
결론은 그래서 어떻게 클린 아키텍쳐 책을 공부해볼 것 인가??
3챕터까지 읽어 본 결과 책에서는 생각보다 추상적이고 바로 적용이 어려울 수 있는 내용들을 얘기하고 있다.
실제로 책에서 말하는 지키고 추구해야하는 원칙들을 프로젝트 레벨에서 지키기 위한 실질적인 방법론 또는 패턴을 생각하면서 탑다운 방향으로 생각해 보는것도 좋은 방법일 것 같다.
예를들어 어떤 Layer를 격리하기 위해 Repository pattern을 사용하고 UI와 Presenters를 격리하기 위해서는 뭐..Clean Swift에서는 Ribs를 채용했고 다른건 MVVM을 차용했고..
지금 형태의 MVVM 패턴에서 굳이 수정해야하는 부분이 있을까
API 단과 CoreData 단이 분리되있는 부분을 합쳐서 하나의 레이어로 추상화 하는 작업을 하면 좋을 것 같고
DTO Entity가 나눠져 있지 않는 부분 정도 생각해 볼 수 있을 것 같다.
서버에서 받는 모델 그대로 viewModel에서 presentation을 위해 사용하고 있는데 Entity로 분리하는것도 좋을 것 같고
Use cases 자체를 정의하고 있지 않는데 정의할 경우 테스트 코드를 작성하기도 좋을 것 같다.
결국 어떤 수정, 리팩토링을 할때 클린 아키텍쳐에 나오는 방향성과 맞는지를 판단하며 적절한 타협점을 찾아 나가는게 좋을 것 같아서 책만 읽기보단 적용할 수 있는 부분을 찾아가며 공부하는게 효과적일 듯 하다.
더 읽을거리
reference
https://medium.com/android-testing-daily/history-of-clean-architecture-9a5e73060ee1
https://github.com/kudoleh/iOS-Clean-Architecture-MVVM
https://github.com/Clean-Swift/CleanStore
https://www.youtube.com/watch?v=3jRxPM3xBnY
'iOS > Architecture' 카테고리의 다른 글
클린 아키텍쳐 4부 정리 (12장~14장) (0) | 2022.04.08 |
---|---|
클린 아키텍쳐 1, 2, 3부 정리 (1장~11장) (0) | 2022.03.31 |
Swift 아키텍쳐 노트 (0) | 2021.03.03 |
iOS: Repository pattern in Swift (0) | 2020.08.07 |
iOS - MVVM Simple look (0) | 2020.04.22 |
댓글