써니쿠키의 IOS 개발일기
iOS 설계에서의 클린아키텍쳐 (Clean Architecture for iOS) 본문
안녕하세요 써니쿠키입니다 🍪
지난 포스팅에서 Uncle Bob의 클린아키텍쳐를 정리해보았는데요. >> [ 여기! ]
오늘은 이어서 이 클린아키텍쳐를 모바일 설계에는 어떻게 적용할 수 있는지와,
예시 프로젝트로 많이 언급되는 깃헙을 참고해서 정리해보겠습니다.
수정할 부분이 있다면 댓글로 남겨주시면 감사하겠습니다 🙇🏻♀️
모바일에서의 클린아키텍처
엉클밥의 클린아키텍처의 컨셉을 보면 계층이 아래와 같이 4개의 계층(layer)이었죠.
모바일 설계에서 이 클린 아키텍쳐를 적용하기 위한 방법 중 하나로,
3Layer형태로 사용합니다.
바로 아래 그림 처럼 말이죠!
이 3 Layer를 하나씩 알아보겠습니다.
1. Domain Layer (도메인 계층)
Entity와 UseCase 계층을 묶어 Domain계층이라 합니다.
- Entity
앱의 핵심 데이터구조입니다.
- UseCase
비즈니스 로직입니다
2. Presentation Layer (프리젠테이션 계층)
UI표시, 애니메이션, 이벤트 핸들링 같은 UI관련 모든 처리를 담당하는 계층입니다
- View
UI화면 표시와 사용자의 터치 이벤트같은 이벤트들을 수신합니다.
화면에 그리는게 어떤 의미인지는 모르고 어떤 모양으로, 어떤 색으로 그리는지 같은 View적인 요소만 결정합니다.
그저 Presenter에서 받은 데이터나 상태에 따라 뷰의 표시를 전환합니다.
- Presenters
MVP/VIPER라면 Presenter역할,
MVVM이라면 ViewModels의 역할과도 같습니다.
사용자의 이벤트에 대한 판단하고 대응합니다.
뷰에 그려지는게 어떤 의미인지, 무엇을 그려야하는 알고있습니다.
3. Data Repository Layer (데이타 계층) = Data Layer
통신 및 데이터관리 로직은 이 계층이 담당합니다.
- DataBase(DB), API
실제 데이터의 입출력을 담당합니다.
- Repository
데이터를 조정하는데 UseCase가 필요로하는 데이터를 저장하거나,
UseCase가 필요로 하는 데이터로 수정하는 기능을 합니다.
( 데이터소스(ex DB)를 Protocol같은 인터페이스 형태로 참조하기 때문에
데이터소스 객체를 갈아끼울 수 있는 형태로,
외부 API를 호출하거나 로컬 DB에 접근하거나 테스트를위한 MockObject로 전환할 수 있습니다.)
의존성 규칙
그리고 클린아키텍쳐에서 꼭 지켜야 할 의존성 규칙!
안쪽 원을 향하는 방향이 규칙이므로
Present 계층과 Data 계층이 Domain 을 바라보는 방향으로 의존성 방향이 지켜져야합니다.
3 Layer
모바일에 적용한 클린아키텍쳐의 형태인 3Layer의 그림을 다시 정리해서 그리면 아래와 같은 그림으로 볼 수 있습니다.
( 원본 그림에서는 Entity를 Model이라 되어있고, 네트워크나 로컬DB에서 받아오는 DTO를 Entity라 작성되어 있는데
헷갈림 방지를 위해 수정해서 그렸습니다.)
이 그림에서 DataSource는 위의 과녁에서봤던 DataBase(DB), API에 해당합니다.
그리고 위에서 설명한 것 외에
Data 계층에 DTO랑
Domain계층에 Translater가 새로 생겼죠?
- DTO
DTO는 데이터소스에서 사용되는 데이터를 정의한 모델로
REST API의 요청/응답을 위한 JSON이나 로컬 DB에 저장하기 위한 타입들이 그 예시입니다.
그럼 DTO타입을 도메인계층에서 사용하는 Entity(Model)타입으로 변환해서 사용해야 할텐데,
이 역할을 Translater가 하는 것 입니다.
- Translater
즉, Translater는 DTO를 Entity모델로 변환하는 Mapper역할을 담당합니다.
의존성 역전
의존성 규칙에 의해 의존방향이 Domain 계층에서 Data계층 방향을 향하면 ❌안되는데❌
그림을 보면 흐름이, Data계층에서 데이터를 가져와서,
Domain계층을 거쳐 Presentation 계층으로 이동해 뷰에 보여지는 흐름이잖아요?
그럼 Domain계층에서 UseCase인 비즈니스 로직을 사용하려면
Data 계층의 데이터를 가져와야하는건데..! 참조할 수 없다는 모순이 있습니다.
바로 이럴 때 의존성 역전 방법을 사용합니다.
쉽게말하면 Repository를 인터페이스로 만들고 Domain 계층이 이 인터페이스를 참조하게 하는 것 입니다.
예제 프로젝트로 이해하기
제일 좋은건 실제 코드를 보는 것이겠죠?
예시 프로젝트로 많이 언급되는 영화검색 프로젝트 깃헙을 참고해보겠습니다.
이 프로젝트는 사진과 같이 영화검색이 주 서비스입니다.
(지금은 API문제인지 작동이 안되어서 zedd님의 블로그에서 작동 이미지를 캡쳐해왔습니다)
README와 파일분류를 먼저 살펴보면 아래와같이 3Layer를 적용한 것을 알 수 있습니다.
Domain 계층에서 Repositories interfaces를 갖고있는게 눈에 띕니다.
위에서 말했듯이 의존성 방향을 지키기 위해 interface를 만들어 Domain계층이 Data 계층을 참조하지않고
인터페이스를 바라보게하는 '의존성역전방법'을 사용 한 것 입니다.
의존성 방향이 Domain 을 향한 방향이고,
Domain계층은 다른 계층을 전혀 포함하지 않는다고 다시 한 번 강조하셨네요.
그럼 코드를 굵직굵직하게 살펴보겠습니다.
👉 Domain - Entity / UseCase / Interface
여기선 영화를 검색해서 리스트로 보여주는게 주요 서비스기 때문에
핵심 데이터는 '영화'입니다.
그래서 Entity에는 Movie와
영화를 리스트로 보여주기 위한 MoviesPage가 담겨있습니다.
UseCase는 비즈니스로직인데, 영화 '검색'이 비지니스 로직이겠죠?
이 '검색'을 할 때는 Data 계층에서 데이터를 가져와야하는데,
Domain계층은 다른 계층을 알아서는 안되기 때문에 직접 참조할 수 없어서 의존성 역전 법칙을 사용합니다.
'MoviesRepository'라는 Repository 인터페이스(프로토콜)를 만들었고,
이 프로토콜을 참조해서 사용함으로서 Domain계층이 Data계층을 직접 알지 못하게 했습니다.
Translater를 찾아봤는데 이 프로젝트에서는 Data계층에 있네요..!
Translater의 역할은 Data계층의 DTO를 Domain계층의 Entity로 변환하는 역할인데
DTO에 메서드로 구현해 놓으셨네요... 밑에서 따로 보여드릴게요
👉Data - DataSource, Repository
DataSource로는 네트워크(REST API)와 영구저장소로 CoreData를 사용하셨네요.
네트워크 데이터소스에서는 서버에서 가져온 데이터의 디코딩을 위한 DTO와
위에서 찾아다니던 Translater인 Mapping메서드가 있습니다.
그 외에도 네트워크 주소와 같은 관련 필요 메서드들이 모두 여기에 있습니다.
영구저장소로 사용한 코어데이에는 REST API로 가져온 데이터를 저장하고 있네요.
CRUD 관련 메서드와, 코어데이터 타입과 Mapping할 DTO가 구현되어있습니다.
Repository는 실제 데이터의 입출력을 담당하기 때문에 'fetchMoviesList' 같은 메서드들이 담겨있습니다.
Repository는 "UseCase에서 필요로하는 데이터를 저장"하거나 "UseCase에 필요로하는 데이터로 수정"하는 역할을 한다했죠??
이 메서드의 Completion 매개변수를 보면 성공시에 MoviePage(Entity)를 가져옵니다.
(내부 구현은 코어데이터에서 먼저 확인 후 찾는 게 코어데이터에 있으면 여기서 데이터를 꺼내오고, 없으면 API로 데이터를 가져옵니다.)
UseCase는 Entity(MoviePage)를 다루는 비지니스 로직이기 때문에,
"UseCase에서 필요로하는 데이터를 저장"한다는 역할에 알맞는 메서드라 할 수 있습니다.
👉Data - Presentation - UI, Presenter
Presenter역한일 ViewModel 들과
UI역할인 view들이 있습니다.
하나만 보자면,
영화들을 리스트로 보여주는 화면의 뷰모델은
UseCase를 사용해 검색해온 페이지를 현재 페이지에서 보여주는 메서드를 갖고있습니다.
View는 화면을 구성하는 요소들을 갖고있고, bind메서드로 ViewModel이 바뀔 때마다 뷰가 바뀌도록 구성해놓았습니다.
이렇게 프로젝트 둘러보기 끝..!
뜬금없지만.. 이 프로젝트에서는 의존성 역전을 위한 프로토콜 타입구현 외에도
대부분의 곳에서 추상화를 사용하셔서 추상화 공부에도 좋은 예제인 것 같습니다.
오늘은 이렇게 iOS에 적용한 클린아키텍처를 알아보았습니다..!
클린 아키텍쳐를 공부하고 예제 프로젝트를 보니
파일이름들만 보아도 클린 아키텍쳐가 적용된 프로젝트구나! 하고 알 수 있었고
원하는 메서드나 코드를 어느 파일에서 찾을 수 있겠구나 하는 감이 생긴 것 같습니다. 🤔
저는 이제 제 프로젝트를 직접 리팩토링 해보려고합니다.
이론에서 끝내지 마시고 코드로 적용해보시는 시간을 가져보시길 바라겠습니다🐣
참고
Template iOS App using Clean Architecture and MVVM
まだMVC,MVP,MVVMで消耗してるの? iOS Clean Architectureについて
Clean Architecture는 모바일 개발을 어떻게 도와주는가? - (1) 경계선: 계층 나누기
Clean Architecture Guide (with tested examples): Data Flow != Dependency Rule
'swift, Ios' 카테고리의 다른 글
[Swift] DiffableDataSource에 한 개 이상 CellRegistration 하기 (0) | 2023.03.22 |
---|---|
[swift] Copy On Write (COW) (0) | 2023.03.06 |
클린아키텍쳐(Clean Architecture) (0) | 2023.03.02 |
야곰아카데미 커리어 스타터 후기 (7기) (0) | 2023.02.26 |
[swift] components 에서 enum의 rawValue를 이용해 문자열 댕강 쪼개버리기⚔️ (0) | 2022.09.22 |