티스토리 뷰

"코드를 기능별로 잘 나누는 것만으로 정말 좋은 아키텍처가 될까?"

아키텍처 패턴을 고민할 때 가장 중요한 것은 코드를 단순히 레이어별로 나누는 것이 아니라, 각 레이어가 올바르게 역할을 수행하고, 의존성이 잘 정리되어 있는지를 따지는 것이다.

Clean Architecture는 Layer Separation(레이어 분리)Dependency Rule(의존성 규칙) 을 통해 확장성 높은 소프트웨어를 만드는 것을 목표로 한다.


1️⃣ Clean Architecture란?

Clean Architecture는 로버트 C. 마틴(Uncle Bob) 이 제안한 소프트웨어 설계 원칙으로,
애플리케이션을 책임에 따라 계층(Layer)으로 분리하고, 의존성을 내부 도메인 중심으로 흐르게 하는 구조를 말한다.

✅ 핵심 목표:

  • 비즈니스 로직과 UI를 분리하여 유지보수성을 높인다.
  • 의존성을 도메인 중심으로 설계하여, 외부 요소(Database, UI, Frameworks)에 영향을 덜 받도록 한다.
  • 테스트가 용이한 구조를 만들어 단위 테스트(Unit Test)가 쉽도록 한다.

Clean Architecture는 계층 구조의존성 규칙(Dependency Rule) 을 기반으로 설계된다.


2️⃣ Clean Architecture의 레이어 구조

Clean Architecture는 아래와 같은 4개의 계층(Layer)으로 나뉜다.

[ UI Layer ]        → View, ViewModel (iOS에서는 SwiftUI, UIKit 등)
[ Interface Layer ] → Use Case (Interactor)
[ Domain Layer ]    → Entities (비즈니스 로직)
[ Data Layer ]      → Repository, Data Sources (DB, API)

 

📌 레이어별 역할 정리

레이어  역할  예시
UI Layer (프레젠테이션 계층) 사용자 입력을 받고, 화면을 구성 ViewController, SwiftUI View
Interface Layer (Use Case 계층) UI와 도메인 사이의 연결 역할, 비즈니스 규칙 실행 UseCase, Interactor
Domain Layer (도메인 계층) 애플리케이션의 핵심 로직, 엔티티 정의 UserEntity, ProductEntity
Data Layer (데이터 계층) 데이터 관리 (DB, API, Local Storage) Repository, NetworkService, Database

 


3️⃣ Dependency Rule(의존성 규칙)

Clean Architecture에서 가장 중요한 개념은 의존성이 바깥쪽에서 안쪽으로만 흐른다는 점이다.

UI → Interface(Use Case) → Domain(Entity) → Data

의존성이 내부 도메인 중심으로 흐르는 이유

  • 비즈니스 로직을 UI와 분리하여 UI 변경이 비즈니스 로직에 영향을 주지 않도록 한다.
  • 외부 요소(DB, API, 프레임워크 등)에 대한 의존성을 줄여 확장성을 높인다.
  • 단위 테스트가 용이하도록 구조화하여 유지보수성을 높인다.

💡 즉, 핵심 비즈니스 로직은 어떤 프레임워크나 라이브러리에도 의존하지 않도록 한다!


4️⃣ Clean Architecture를 iOS 프로젝트에 적용하기

이제 Clean Architecture를 iOS 프로젝트에서 어떻게 구현할 수 있는지 살펴보자.

✅ 1. Entity (Domain Layer)

도메인 계층의 핵심 비즈니스 로직을 담당하는 엔티티(Entity)이다.

struct User {
    let id: Int
    let name: String
}

✅ 2. Use Case (Interface Layer)

비즈니스 로직을 실행하는 역할을 한다.
UI와 도메인 사이의 연결고리 역할을 하며, Repository를 사용해 데이터를 가져온다.

protocol FetchUserUseCase {
    func execute(userId: Int) async throws -> User
}

class FetchUserInteractor: FetchUserUseCase {
    private let repository: UserRepository

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

    func execute(userId: Int) async throws -> User {
        return try await repository.getUser(by: userId)
    }
}

 

✅ 3. Repository (Data Layer)

Repository는 데이터를 가져오는 인터페이스 역할을 한다.

protocol UserRepository {
    func getUser(by id: Int) async throws -> User
}

class UserRepositoryImpl: UserRepository {
    private let apiClient: APIClient

    init(apiClient: APIClient) {
        self.apiClient = apiClient
    }

    func getUser(by id: Int) async throws -> User {
        let response = try await apiClient.request(endpoint: "/users/\(id)")
        return User(id: response["id"] as! Int, name: response["name"] as! String)
    }
}

✅ 4. ViewModel (UI Layer)

ViewModel은 UI에서 데이터를 받아오고 상태를 관리하는 역할을 한다.

class UserViewModel: ObservableObject {
    private let fetchUserUseCase: FetchUserUseCase
    @Published var user: User?

    init(fetchUserUseCase: FetchUserUseCase) {
        self.fetchUserUseCase = fetchUserUseCase
    }

    func loadUser(userId: Int) async {
        do {
            let user = try await fetchUserUseCase.execute(userId: userId)
            DispatchQueue.main.async {
                self.user = user
            }
        } catch {
            print("Error loading user: \(error)")
        }
    }
}

 

✅ 5. View (UI Layer)

SwiftUI에서 ViewModel을 사용해 UI를 업데이트한다.

struct UserView: View {
    @StateObject private var viewModel = UserViewModel(fetchUserUseCase: FetchUserInteractor(repository: UserRepositoryImpl(apiClient: APIClient())))

    var body: some View {
        VStack {
            if let user = viewModel.user {
                Text(user.name)
            } else {
                ProgressView()
            }
        }
        .onAppear {
            Task {
                await viewModel.loadUser(userId: 1)
            }
        }
    }
}

 


5️⃣ Clean Architecture 적용 시 고려할 점

✅ 언제 적용하면 좋을까?

  • 대규모 프로젝트에서 코드의 확장성과 유지보수성을 높이고 싶을 때
  • 테스트가 중요한 프로젝트에서 UI와 비즈니스 로직을 분리하고 싶을 때
  • 팀 협업이 필요한 경우 (각 레이어별로 역할이 나뉘므로 개발이 분리됨)

✅ 언제 과할 수 있을까?

  • 작은 프로젝트에서는 계층이 많아져서 오히려 복잡성이 증가할 수도 있다.
  • 모든 프로젝트에서 반드시 Clean Architecture를 적용할 필요는 없으며, 프로젝트의 규모와 요구사항을 고려해야 한다.

정리

✅ Clean Architecture는 비즈니스 로직과 UI를 분리하여 유지보수를 쉽게 만드는 설계 패턴이다.
✅ 핵심 개념은 Layer Separation(레이어 분리)과 Dependency Rule(의존성 규칙) 을 따르는 것!
도메인 계층이 외부 요소(UI, DB, API 등)에 의존하지 않도록 설계해야 한다.
iOS에서는 Entity, UseCase, Repository, ViewModel, View로 분리하여 적용할 수 있다.
✅ 실무에서 적용할 때는 프로젝트 규모와 필요에 따라 적절히 활용하는 것이 중요하다.

 

Clean Architecture는 처음에는 조금 복잡해 보일 수 있지만, 한 번 익숙해지면 유지보수성과 확장성이 뛰어난 코드를 작성할 수 있다.
프로젝트에서 직접 적용해보면서 어떤 부분이 도움이 되는지 경험해보는 것이 가장 중요하다.

이제, Clean Architecture를 적용해 더 깔끔한 iOS 앱을 만들어보자!

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/04   »
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
글 보관함