[작성중..] Clean Architecture

hyun·2025년 7월 28일
0

iOS

목록 보기
34/54

레이어 구성

Entity (도메인 모델)

순수한 Swift 구조체/클래스, 비즈니스 규칙과 핵심 로직을 담음

외부 프레임워크나 Cocoa Touch에 대한 의존이 전혀 없음

Use Case (유스케이스 / 인터랙터)

Entity를 활용해 구체적인 비즈니스 동작을 수행

입력(요청) → 비즈니스 로직 → 출력(응답) 형태로 구성

인터페이스(프로토콜)를 통해 외부(Repository 등)와 통신

Interface Adapters (인터페이스 어댑터)

데이터베이스, 네트워크, UI 등 외부 시스템과 Use Case/Entity 사이의 변환 책임

예 : Repository 구현체, Presenter, ViewModel

Frameworks & Drivers (프레임워크와 드라이버)

실질적인 iOS SDK(UI, network, DB 라이브러리 등)

앱 진입점(AppDelegate, SceneDelegate), ViewController, Kingfisher, Alamofire 등

핵심 원칙

의존성 규칙 (Dependency Rule)

코드 의존성 방향은 바깥 레이어 → 안쪽 레이어로만 향해야 한다.

인터페이스 의존

상위 레이어는 하위 레이어의 구체 구현체가 아니라, 프로토콜(추상화)에만 의존한다.

SOLID 원칙

SRP (단일 책임 원칙)

OCP (개방-폐쇄 원칙)

LSP, ISP, DIP 등

장단점

장점

  • 모듈화로 유지보수·테스트 용이
  • 비즈니스 로직과 UI/인프라 분리
  • 의존성 관리가 명확

단점

  • 초기 설계가 복잡
  • 간단한 프로젝트에는 오버헤드
  • 레이어 간 인터페이스 코드 증가

간단한 예시

// 1) Entity
struct Movie {
    let id: Int
    let title: String
}

// 2) Use Case Protocol
protocol FetchMoviesUseCase {
    func execute(completion: @escaping (Result<[Movie], Error>) -> Void)
}

// 3) Use Case 구현체
final class FetchMoviesInteractor: FetchMoviesUseCase {
    private let repository: MovieRepository

    init(repository: MovieRepository) {
        self.repository = repository
    }

    func execute(completion: @escaping (Result<[Movie], Error>) -> Void) {
        repository.fetchAll { result in
            switch result {
            case .success(let data):
                // 추가적인 비즈니스 로직 가능
                completion(.success(data.map { Movie(id: $0.id, title: $0.title) }))
            case .failure(let error):
                completion(.failure(error))
            }
        }
    }
}

// 4) Repository Protocol (Interface Adapter)
protocol MovieRepository {
    func fetchAll(completion: @escaping (Result<[MovieDTO], Error>) -> Void)
}

// 5) Repository 구현체 (infrastructure)
final class MovieAPIRepository: MovieRepository {
    func fetchAll(completion: @escaping (Result<[MovieDTO], Error>) -> Void) {
        // Alamofire/URLSession을 통해 네트워크 호출
    }

}

레거시 프로젝트에 도입할 땐 점진적으로 Use Case → Repository → UI 순으로 분리

Dependency Injection을 활용해 테스트 용이성 확보

작게 시작해서, 프로젝트 규모가 커질수록 레이어를 엄격히 분리

0개의 댓글