[Swift] iOS Clean Architecture, MVVM 기초 개념

팔랑이·2025년 1월 7일
0

iOS/Swift

목록 보기
65/71

💻 인프런 - iOS Clean Architecture & MVVM: RxSwift 완전 정복 강의를 수강하고 학습한 내용입니다.


SOLID원칙

원칙은 지키기 어렵지만, 시간이 지날수록 원칙을 지키면 큰 영향을 가져온다
SOLID 원칙을 지키면, 프로젝트의 규모가 커질수록 큰 영향을 가져온다.
유지보수와 확장이 쉬운 환경을 만들 수 있다.

1. 단일 책임 원칙 (Single Responsibility Principle)

  • 클래스는 단 하나의 책임만 가져야 한다.
  • 책임 = 변경의 이유
  • ex) ViewController의 역할은 UI를 그리는 하나의 책임만을 가져야 한다.

2. 개방-폐쇄 원칙 (Open-Closed Principle)

  • 확장에는 열려 있고 수정에는 닫혀 있어야 한다.
  • ex) ViewController가 Protocol에 의존하면 코드를 수정하지 않고도 기능 확장이 가능하다.

3. 인터페이스 분리 원칙 (Interface Segregation Principle)

  • 사용하지 않는 인터페이스는 포함하지 않아야 한다.

4. 의존성 역전 원칙 (Dependency Inversion Principle)

리스코프 치환 원칙은 큰 관련이 없어 이번 강의에서는 별도로 다루지 않음.


MVVM

구성 요소

  • ViewController: UI 이벤트를 ViewModel에 전달
  • ViewModel: View와 관련된 로직을 처리
  • Model: 엔티티뿐 아니라 네트워크 호출, 내부 데이터 접근 등 데이터를 담당

의존성

MVVM은 단방향적 의존성을 가지는데, View는 ViewModel을 의존하고 Viewmodel은 Model을 의존한다.

  • ViewModel 변경시 ViewController는 영향을 받지만, ViewController가 바뀌면 ViewModel은 영향을 받지 않음
  • Model 변경있으면 ViewModel에 영향. 반대는 아무런 영향이 없음

추상화와 인터페이스

추상화의 필요성

  • ViewModel 내부의 세부 구현을 알 필요 없이 필요한 인터페이스만 제공
  • 추상화와 캡슐화로 세부 내용을 감추고 필요한 인터페이스만 노출
  • 결과적으로 ViewController는 ViewModel에 약하게 의존하게 된다

예를 들어,

그림 1그림 2

그림 2와 같이, ViewController에서는 뷰모델의 여러 메서드 안의 내용을 알 필요가 없다.
이렇게 필요한 인터페이스만 추출해내는 작업을 추상화라고 하며, 세부 내용을 감추는 작업을 캡슐화라고 하고 그 결과물을 인터페이스라고 한다.

Swift는 이 기능을 Protocol로 제공하며, 의존성을 최소화하여 변경에 유연하게 대응할 수 있게 설계해야 한다.


Clean Architecture

클린 아키텍처를 검색하면 엄청 복잡한 그림이 나오지만,
모바일에서는 다음과 같이 크게 3계층으로 나눌 수 있다.

MVVM을 확장하여 클린아키텍처 설계 ->
M을 세분화하여 Domain Layer, Data Layer가 되었고
VVM 부분이 Presentation Layer라고 할 수 있다.

1. Domain Layer

  • 프로그램의 코어하고 핵심적인 부분을 다룬다.
  • Entity, Use Cases(비즈니스 로직), Repositories

비즈니스 로직이란?
유저 리스트 불러오기, 유저 상세 데이터 불러오기, 유저 저장, 유저 검색 등 핵심적인 기능

2. Data Layer

  • 데이터 소스(DB, API)와 관련된 모든 작업
  • Repository 자체는 Data Layer, Repository Protocol은 Domain Layer

3. Presentation Layer

  • ViewModel, ViewController 부분

의존성의 방향

화살표는 의존성의 방향을 뜻하는데,

  • 안으로 갈수록 고수준 모듈/레이어: 비교적 변경이 잘 일어나지 않음
  • 밖으로 갈수록 저수준 모듈/레이어: 비교적 변경이 자주 일어남

저수준은 고수준을 의존해야 한다. 고수준이 저수준을 의존하면, 화살표의 방향이 바뀌므로 클린 아키텍처를 위반.

예시 코드 1: 고수준에서 저수준을 의존

class ViewModel() { // 저수준
	private let usecase = UseCase() // 저수준 -> 고수준 의존
    
    func getUsers() {
    	usecase.getUsers()
    }
}

class UseCase() { // 고수준
	private let repository = Repository() // 고수준 -> 저수준 의존 (원칙 위배)
    
    func getUsers() {
    	repository.getUsers()
    }
}

두 번째 예시에서, 고수준 모듈인 UseCase가 저수준 모듈인 Repository()를 의존하고 있기 때문에 의존성 방향이 위배된다.

그렇다면 UseCase에서는 어떻게 데이터를 불러와야 할까?

예시 코드 2: 인터페이스(프로토콜) 사용하여 의존성 역전

고수준 모듈인 UseCase가 저수준 모듈인 Repository를 직접 의존하지 않도록, Repository Protocol을 사용하여 의존성을 역전시킬 수 있다.

인터페이스(프로토콜)는 추상화한 개념으로, 이러나 저러나 고수준으로 분류

class UseCase() { 
	private let repository: RepositoryProtocol
    init(repository: RepositoryProtocol) {
    	self.repository = repository
    }
    
    func getUsers() {
    	repository.getUsers()
    }
}

protocol RepositoryProtocol() {
	func getUsers()
}

이렇게 하면 클린아키텍처의 원칙을 위배하지 않고 데이터에 접근할 수 있다.


다음 강의부터는 실습으로!

profile
정체되지 않는 성장

3개의 댓글

comment-user-thumbnail
2025년 1월 15일

와 아직도 열심히 공부 하시고 계시네요!! 멋집니다...
제 근황은 게임 회사에 취업하여 서버 프로그래머로 다니고 있습니다 ~~!!

2개의 답글