클린 아키텍처란?
오늘날까지 수많은 아키텍처(마이크로 서비스, 서버리스 등)들은 전반적으로 '관심의 분리'(Seperation of concerns)와 '테스트 가능성'(Testability)을 보편적인 요구를 갖고 있습니다.
클린 아키텍처는 이러한 요건을 만족하는, 간단히 말하면 '추상화 개념'(Abstraction principle)으로써 관심사를 분리시키고 의존도를 낮추는 것에 목적을 둔 아키텍처입니다.
의존도를 낮추고 서로에게 주는 영향을 감소함으로써 유지보수의 용이성이 향상됩니다.
덕분에 낮은 비용으로 새로운 기능을 추가할 수 있습니다.
기본적인 원리는 종속성 규칙(Dependency Rule)을 지키는 것입니다. 각 코드의 종속성은 외부에서 내부로 안쪽으로만 가리킬 수 있고, 고수준 정책(High level policy)이 저수준 정책(Low level policy)의 변경에 영향을 받지 않도록 하는 것입니다.
여기서 의미하는 수준(Level)은 입/출력과의 거리를 의미하는 것으로,
고수준 정책은 보통 UI 또는 인터페이스와 거리가 먼 비즈니스 영역 Business Rules, Entities 등을 의미하며,
저수준 정책은 위와 반대로 거리가 가까운 인프라 영역이나 UI 영역 Presentation, Controllers 등을 의미한다.
참고링크:
https://daryeou.tistory.com/280

해당 아키텍쳐를 작동시키는 가장 우선적인 규칙은 Dependency Rule
Dependency Rule
- 소스코드의 종속성은 안쪽으로만 향할 수 있다
- inner circles 안에 있는 요소들은 outer circles에 대해 어떠한 것도 알 수 없다
- 특히, outer circles에 선언된 이름은 inner circles에서 언급해서는 안된다(함수, 클래스 등)
영화 검색앱 프로젝트를 예로 들면,

1. Entities
엔티티는 "Enterprise wide business rules"을 캡슐화한 것
외부 변화가 있을 때 변경될 가능성이 가장 적고, 일반적으로 가장 높은 수준의 규칙을 캡슐화 한다.
영화 검색앱에서는
struct Movie: Equatable, Identifiable {
typealias Identifier = String
enum Genre {
case adventure
case scienceFiction
}
let id: Identifier
let title: String?
let genre: Genre?
let posterPath: String?
let overview: String?
let releaseDate: Date?
}
struct MoviesPage: Equatable {
let page: Int
let totalPages: Int
let movies: [Movie]
}
다음과 같은 "Movie"라는 데이터 구조가 엔티티이다.
다시 말해 엔티티는 데이터 구조 및 함수 집합이라고 생각하면 된다.
영화 검색 앱에서는 다른 요소가 아무리 바뀌어도 어찌됐던 영화에 대한 정보를 보여주는 기능을 가지고 있는 앱이기 때문에
"Movie"라는 데이터 구조가 가장 높은 수준의 규칙이라고 말할 수 있다.
2. Use cases
Use cases는 "시스템의 동작을 사용자의 입장에서 표현한 시나리오"이다.
Use cases는 엔티티와 데이터의 흐름을 조정하고, 해당 엔티티가 Use cases의 목표를 달성하도록 "지시"하는 역할을 한다.
영화 검색 앱은 영화를 검색하는 기능을 가지고 있다.
따라서 영화 검색 앱에서의 Use cases는 사용자에게 보여지는 출력 부분을 위해 해당 출력을 생성하기 위한 처리 단계를 기술하는 곳이라고 보면 된다.
(Use cases를 Use case Interactor라고도 한다.)
Entities는 이 Use cases에 대해 알지 못한다. 그러나 Use cases는 Entities를 알고 있다.
하지만 이 Use cases 계층 변경이 Entities에 영향을 미쳐서는 안된다.
또한 Use cases가 DB, UI 같은 외부 환경의 변경으로 인해 영향을 받지 않아야 한다.
왜냐하면 단순히 Use cases이기 때문에 데이터가 어디에 저장되어 있던, 외부 프레임워크가 바뀌던 상관이 없는 것이다.
하지만 앱 전체의 작동 방식 변경과 같이 사용자의 시나리오가 바뀌게 되면 Use cases에 영향을 미칠 수 있다.
3. Interface Adapters(Controllers + Gateways + Presenters)
Interface Adapter에는 (View)Controllers, Gateways, Presenters가 속한다.
해당 계층을 Interface Adapter 혹은 Presentation Layer라고 부른다.
해당 계층이 하는 역할은 Interface Adapters에 데이터가 들어오면
Entities, Use cases에 가장 편리한 format에서 DB 등과 같은 외부 프레임워크에 가장 편리한 format으로 변환되는 곳이다.
따라서 Interface Adapters는 DB에 대해 알고 있지만, Use cases, Entities에 있는 코드들은 DB에 대해 아는 것이 없어야 한다.
4. Frameworks & Drivers(DB + Devices + External Interfaces + UI + Web)
Dependency Rule의 방향은
DB -> Adapter -> Use Cases -> Entities
원이 지금은 4개밖에 존재하지 않지만 원이 몇개든 상관없이 Dependency Rule이 안쪽으로 향하게끔만 하면 된다.

다이어그램 우측 하단을 보면 원 경계를 교차하는 방법의 예가 있다.
Controller에서 시작해 Use Cases를 통해 Presenter에서 실행된다.
그러나 Use case가 Presenter를 호출한다는 건 Dependency Rule에 어긋나는 것이다.
따라서,
Output Port라는 "인터페이스"를 사용한다.
그래서 Use case는 이 Output port에 있는 인터페이스를 호출한다.
그리고 Presenter는 이 Ouput port 인터페이스를 구현한다.
따라서 직접 Presenter의 구현체에 접근을 안하고 인터페이스를 통해 접근하는 것이다.
참고링크:
'Clean Architecture' 카테고리의 다른 글
[Clean Architecture] iOS Repository Pattern이란? (0) | 2023.10.04 |
---|---|
[Clean Architecture] 만들면서 배우는 클린 아키텍처 9장 (0) | 2023.06.27 |
[Clean Architecture] iOS 클린 아키텍처 실제 프로젝트 3: DI(의존성 주입) (0) | 2023.06.12 |
Coodinater Pattern 정의 및 예제(1) (0) | 2023.05.08 |
클린 아키텍처란?
오늘날까지 수많은 아키텍처(마이크로 서비스, 서버리스 등)들은 전반적으로 '관심의 분리'(Seperation of concerns)와 '테스트 가능성'(Testability)을 보편적인 요구를 갖고 있습니다.
클린 아키텍처는 이러한 요건을 만족하는, 간단히 말하면 '추상화 개념'(Abstraction principle)으로써 관심사를 분리시키고 의존도를 낮추는 것에 목적을 둔 아키텍처입니다.
의존도를 낮추고 서로에게 주는 영향을 감소함으로써 유지보수의 용이성이 향상됩니다.
덕분에 낮은 비용으로 새로운 기능을 추가할 수 있습니다.
기본적인 원리는 종속성 규칙(Dependency Rule)을 지키는 것입니다. 각 코드의 종속성은 외부에서 내부로 안쪽으로만 가리킬 수 있고, 고수준 정책(High level policy)이 저수준 정책(Low level policy)의 변경에 영향을 받지 않도록 하는 것입니다.
여기서 의미하는 수준(Level)은 입/출력과의 거리를 의미하는 것으로,
고수준 정책은 보통 UI 또는 인터페이스와 거리가 먼 비즈니스 영역 Business Rules, Entities 등을 의미하며,
저수준 정책은 위와 반대로 거리가 가까운 인프라 영역이나 UI 영역 Presentation, Controllers 등을 의미한다.
참고링크:
https://daryeou.tistory.com/280

해당 아키텍쳐를 작동시키는 가장 우선적인 규칙은 Dependency Rule
Dependency Rule
- 소스코드의 종속성은 안쪽으로만 향할 수 있다
- inner circles 안에 있는 요소들은 outer circles에 대해 어떠한 것도 알 수 없다
- 특히, outer circles에 선언된 이름은 inner circles에서 언급해서는 안된다(함수, 클래스 등)
영화 검색앱 프로젝트를 예로 들면,

1. Entities
엔티티는 "Enterprise wide business rules"을 캡슐화한 것
외부 변화가 있을 때 변경될 가능성이 가장 적고, 일반적으로 가장 높은 수준의 규칙을 캡슐화 한다.
영화 검색앱에서는
struct Movie: Equatable, Identifiable {
typealias Identifier = String
enum Genre {
case adventure
case scienceFiction
}
let id: Identifier
let title: String?
let genre: Genre?
let posterPath: String?
let overview: String?
let releaseDate: Date?
}
struct MoviesPage: Equatable {
let page: Int
let totalPages: Int
let movies: [Movie]
}
다음과 같은 "Movie"라는 데이터 구조가 엔티티이다.
다시 말해 엔티티는 데이터 구조 및 함수 집합이라고 생각하면 된다.
영화 검색 앱에서는 다른 요소가 아무리 바뀌어도 어찌됐던 영화에 대한 정보를 보여주는 기능을 가지고 있는 앱이기 때문에
"Movie"라는 데이터 구조가 가장 높은 수준의 규칙이라고 말할 수 있다.
2. Use cases
Use cases는 "시스템의 동작을 사용자의 입장에서 표현한 시나리오"이다.
Use cases는 엔티티와 데이터의 흐름을 조정하고, 해당 엔티티가 Use cases의 목표를 달성하도록 "지시"하는 역할을 한다.
영화 검색 앱은 영화를 검색하는 기능을 가지고 있다.
따라서 영화 검색 앱에서의 Use cases는 사용자에게 보여지는 출력 부분을 위해 해당 출력을 생성하기 위한 처리 단계를 기술하는 곳이라고 보면 된다.
(Use cases를 Use case Interactor라고도 한다.)
Entities는 이 Use cases에 대해 알지 못한다. 그러나 Use cases는 Entities를 알고 있다.
하지만 이 Use cases 계층 변경이 Entities에 영향을 미쳐서는 안된다.
또한 Use cases가 DB, UI 같은 외부 환경의 변경으로 인해 영향을 받지 않아야 한다.
왜냐하면 단순히 Use cases이기 때문에 데이터가 어디에 저장되어 있던, 외부 프레임워크가 바뀌던 상관이 없는 것이다.
하지만 앱 전체의 작동 방식 변경과 같이 사용자의 시나리오가 바뀌게 되면 Use cases에 영향을 미칠 수 있다.
3. Interface Adapters(Controllers + Gateways + Presenters)
Interface Adapter에는 (View)Controllers, Gateways, Presenters가 속한다.
해당 계층을 Interface Adapter 혹은 Presentation Layer라고 부른다.
해당 계층이 하는 역할은 Interface Adapters에 데이터가 들어오면
Entities, Use cases에 가장 편리한 format에서 DB 등과 같은 외부 프레임워크에 가장 편리한 format으로 변환되는 곳이다.
따라서 Interface Adapters는 DB에 대해 알고 있지만, Use cases, Entities에 있는 코드들은 DB에 대해 아는 것이 없어야 한다.
4. Frameworks & Drivers(DB + Devices + External Interfaces + UI + Web)
Dependency Rule의 방향은
DB -> Adapter -> Use Cases -> Entities
원이 지금은 4개밖에 존재하지 않지만 원이 몇개든 상관없이 Dependency Rule이 안쪽으로 향하게끔만 하면 된다.

다이어그램 우측 하단을 보면 원 경계를 교차하는 방법의 예가 있다.
Controller에서 시작해 Use Cases를 통해 Presenter에서 실행된다.
그러나 Use case가 Presenter를 호출한다는 건 Dependency Rule에 어긋나는 것이다.
따라서,
Output Port라는 "인터페이스"를 사용한다.
그래서 Use case는 이 Output port에 있는 인터페이스를 호출한다.
그리고 Presenter는 이 Ouput port 인터페이스를 구현한다.
따라서 직접 Presenter의 구현체에 접근을 안하고 인터페이스를 통해 접근하는 것이다.
참고링크:
'Clean Architecture' 카테고리의 다른 글
[Clean Architecture] iOS Repository Pattern이란? (0) | 2023.10.04 |
---|---|
[Clean Architecture] 만들면서 배우는 클린 아키텍처 9장 (0) | 2023.06.27 |
[Clean Architecture] iOS 클린 아키텍처 실제 프로젝트 3: DI(의존성 주입) (0) | 2023.06.12 |
Coodinater Pattern 정의 및 예제(1) (0) | 2023.05.08 |