Menu
Menu
Posts List
  1. 클린 아키텍처 (Clean Architecture):
    1. 개념
      1. 계층 구조
      2. 의존성 방향
    2. 장점
    3. MVC, MVI와의 비교
    4. 예시 (Search)
    5. ReactorKit과 클린 아키텍처를 같이 사용하고싶다면?
    6. Reactor가 ViewModel을 대체할 수 있는 이유
    7. 주의할 점
    8. 예시
    9. ReactorKit 사용 시, 계층구조 예측

Clean Architecture

클린 아키텍처 (Clean Architecture):

클린 아키텍처는 소프트웨어 아키텍처 설계 원칙 중 하나로, 의존성 역전(Dependency Inversion)을 핵심으로 하여 비즈니스 로직과 외부 요소(UI, DB 등)를 철저히 분리한다고 한다.
MVC, MVI와 어떤 부분이 크게 다른지 알아보고 더 공부하기 위해 작성!


개념

클린 아키텍처는 계층별로 관심사(Responsibility)를 나누고, 각 계층은 안쪽 계층(도메인)에만 의존하도록 구성

계층 구조

1
2
3
4
5
6
7
8
9
+------------------------+  
| UI Layer | ← ViewController, View
+------------------------+
| Presentation Layer | ← Presenter, ViewModel
+------------------------+
| Domain Layer | ← UseCase, Entity
+------------------------+
| Data Layer | ← Repository, API, DB
+------------------------+

의존성 방향

  • 모든 의존성은 안쪽(Domain)으로 향한다.
  • 외부(UI, DB, 프레임워크)는 언제든지 교체 가능!

장점

  • 테스트 용이 (도메인 계층이 UI/프레임워크에 독립적)
  • 유지보수성, 확장성 향상
  • 관심사 분리 (SRP - 단일 책임 원칙 적용 가능)

MVC, MVI와의 비교

항목 MVC MVI Clean Architecture
구조 단순성 ✅ 단순함 ❌ 상대적으로 복잡 ❌ 더 많은 계층 구성 필요
관심사 분리 ❌ Controller가 비대해지기 쉬움 ✅ 단방향 흐름으로 명확한 책임 ✅ 계층 기반 분리
테스트 용이성 ❌ UI 의존적 ✅ State 기반 테스트 가능 ✅ 비즈니스 로직 단위로 테스트 용이
프레임워크 의존성 ⛔ UIKit에 강한 결합 ⛔ Rx/MVI 기반 의존 ✅ 도메인 레이어는 프레임워크에 독립적
방향성 양방향 단방향 (Intent → Model → View) 의존성 역전 (Data → Domain ← UI)

  • UI Layer: SearchViewController
  • Presentation Layer: SearchViewModel, SearchPresenter
  • Domain Layer: SearchUseCase, SearchEntity
  • Data Layer: SearchRepository, SearchAPI

ReactorKit과 클린 아키텍처를 같이 사용하고싶다면?

Reactor클린 아키텍처의 Presentation Layer(ViewModel 자리)를 충분히 대체 가능할 것 같다!
View ↔ Reactor ↔ UseCase 흐름으로 단방향 데이터 흐름을 구현 가능 할 것 같다.

Reactor가 ViewModel을 대체할 수 있는 이유

역할 설명
입력 처리 Action으로 사용자 이벤트 전달
상태 변환 Mutate → Reduce 패턴으로 상태 변화 처리
상태 제공 State로 UI 업데이트 상태 제공
의존성 주입 UseCase, Service 등 외부 주입 가능

→ Reactor는 ViewModel처럼 동작하면서도 단방향 흐름을 명확히 구현할 수 있지 않을까?


주의할 점

  • Reactor가 비대해지지 않도록, 복잡한 비즈니스 로직은 UseCase로 위임하는 것이 좋다고 한다.
  • Reactor는 단순히 액션을 받아서 상태를 변경하고, 상태를 View로 전달하는 역할에 집중하는 것이 이상적!

예시

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
32
33
34
35
36
37
final class MyReactor: Reactor {
enum Action {
case fetchTitle
}
enum Mutation {
case setTitle(String)
}

struct State {
var title: String = ""
}

let useCase: MyUseCase

init(useCase: MyUseCase) {
self.useCase = useCase
}

func mutate(action: Action) -> Observable<Mutation> {
switch action {
case .fetchTitle:
let setTitle: Observable<Mutation> =
useCase.fetchTitle().map { Mutation.setTitle($0) }

return .concat([setTitle])
}
}

func reduce(state: State, mutation: Mutation) -> State {
switch mutation {
case let .setTitle(title):
var newState = state
newState.title = title
return newState
}
}
}

ReactorKit 사용 시, 계층구조 예측

1
2
3
4
5
6
7
8
9
10
11
12
13
+------------------------+  
| UI Layer | ← ViewController, View
| 사용자 인터페이스, UI 렌더링, 유저 액션 전달
+------------------------+
| Presentation Layer | ← Presenter, Reactor
| 상태/액션 처리, 유저 액션을 도메인 계층에 전달
+------------------------+
| Domain Layer | ← UseCase, Entity
| 핵심 비즈니스 로직, 애플리케이션 규칙
+------------------------+
| Data Layer | ← Repository, API, DB
| 외부 리소스 (네트워크, DB) 처리
+------------------------+