본문 바로가기

UIKIT

ViewModel을 사용하는 목적

https://explorer89.tistory.com/294

 

ViewModel

https://explorer89.tistory.com/82 ObservableObject와 @Published개체가 변경되기 전에 내보내는 게시자가 있는 개체 형식이다.?  @Published와 ObservableObject는 스위프트의 Combine 프레임워크에서 사용되는 속성 래

explorer89.tistory.com

 

왜 ViewModel을 사용하는가?

ViewModel의 주요 목적은 UI와 비즈니스 로직을 분리하는 것입니다. 이 패턴은 MVVM(Model-View-ViewModel) 구조에서 사용되며, 다음과 같은 이유로 선택됩니다:

  1. UI와 로직 분리
    • ViewController(UIKit)나 View(SwiftUI)에 로직이 포함되면 코드가 복잡하고 유지보수가 어려워집니다.
    • ViewModel을 사용하면 UI 업데이트 로직과 데이터 처리 로직이 분리되므로, 코드를 더 이해하기 쉽고 테스트 가능하게 만듭니다.
  2. 데이터 바인딩
    • ViewModel은 데이터를 가공하거나 변경 사항을 UI에 전달하는 중간 계층 역할을 합니다.
    • UIKit에서 Combine, KVO, 또는 NotificationCenter 등을 사용해 UI와 데이터 상태를 동기화할 수 있습니다.
    • SwiftUI에서는 ObservableObject와 @Published를 사용해 뷰를 자동으로 업데이트합니다.
  3. 테스트 가능성
    • ViewModel은 UIKit에 의존하지 않기 때문에, 독립적으로 단위 테스트를 작성할 수 있습니다.
    • 반대로, ViewController나 다른 UIKit 구성 요소와 강하게 결합된 로직은 테스트가 어렵습니다.

 

struct RegisterUserRequest {
    
    let username: String
    let email: String
    let password: String
    
}

func registerUser(request: RegisterUserRequest) {
    // 1. Firebase Auth로 사용자 생성
    Auth.auth().createUser(withEmail: request.email, password: request.password)
}

 

RegisterUserRequest 구조체만 사용한다면?

RegisterUserRequest 구조체를 정의하고, 별도의 registerUser(request:) 메서드에서 FirebaseAuth를 호출하는 접근은 충분히 유효합니다. 하지만 이 방법은 다음과 같은 한계를 가질 수 있습니다:

  • 데이터 상태 관리
    • 단순히 구조체와 함수만 사용하면, 현재 유효성 검증 상태(예: 이메일이 올바른지, 비밀번호 길이가 적절한지)와 같은 상태를 관리하기 어렵습니다.
    • ViewModel에서는 이런 상태를 @Published 프로퍼티로 관리해 UI에서 쉽게 참조하고 업데이트할 수 있습니다.
     
  • 중복된 로직
    • 여러 화면에서 사용자 등록 로직을 사용한다면, 데이터 검증 및 Firebase 호출과 같은 로직이 중복될 가능성이 큽니다.
    • ViewModel은 이 로직을 중앙화하여 중복을 줄이는 역할을 합니다.

 

  • 비즈니스 로직과 UI의 결합
    • ViewController에서 직접 Firebase를 호출하면, 비즈니스 로직과 UI 코드가 강하게 결합됩니다.
    • 결과적으로 코드 재사용성과 테스트 가능성이 낮아지고, UI 변경 시 로직에 영향을 미칠 가능성이 커집니다.

 

간단한 구조라면 이렇게 구현 가능

ViewModel 없이 단순히 구조체와 함수를 활용하는 방법도 가능합니다.

struct RegisterUserRequest {
    let username: String
    let email: String
    let password: String
}

class AuthManager {
    static let shared = AuthManager()

    func registerUser(request: RegisterUserRequest, completion: @escaping (Result<User, Error>) -> Void) {
        Auth.auth().createUser(withEmail: request.email, password: request.password) { authResult, error in
            if let error = error {
                completion(.failure(error))
            } else if let user = authResult?.user {
                completion(.success(user))
            }
        }
    }
}
let request = RegisterUserRequest(username: "JohnDoe", email: "john@example.com", password: "password123")

AuthManager.shared.registerUser(request: request) { result in
    switch result {
    case .success(let user):
        print("User registered: \(user.email ?? "")")
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}

 

언제 ViewModel을 사용해야 할까?

ViewModel은 다음과 같은 경우에 특히 유용합니다:

  • UI와 데이터를 더 잘 분리하고 싶을 때.
  • 상태 관리가 필요한 경우 (예: 폼 검증).
  • 앱이 복잡해지고, 코드 재사용성과 테스트 가능성이 중요해질 때.

 

직접 구현한 경우 (RegisterUserRequest 사용)

특징

  • 모든 로직이 ViewController에 포함되어 있습니다.
  • Validator, AuthService, AlertManager를 직접 호출하고, 사용자 입력 데이터를 바로 처리합니다.

장점

  • 간단하고 빠르게 구현 가능:
    • 로직이 모두 한곳에 있어서 작성 속도가 빠르고, 코드가 단순합니다.
  • 작은 규모의 프로젝트에 적합:
    • UI와 데이터 로직이 섞여 있어도 관리가 어렵지 않은 경우 사용해도 문제없습니다.

단점

  1. 결합도가 높음:
    • UI (ViewController)가 로직 (Validator, AuthService)과 강하게 연결되어 있어, 이 로직을 다른 화면에서 재사용하려면 코드 복사가 필요합니다.
    • 예를 들어, 같은 registerUser 로직을 재사용하려면 또다시 ViewController에 비슷한 코드가 추가될 가능성이 높습니다.
  2. 테스트가 어려움:
    • ViewController는 UIKit 의존성이 강하므로, 단위 테스트를 작성하기 어렵습니다.
    • 이메일, 비밀번호 검증 로직과 Firebase 호출이 UI 코드와 얽혀 있어 독립적인 테스트가 힘듭니다.

 

 

ViewModel을 사용한 경우

특징

  • UI와 로직이 분리되어 있습니다.
  • 사용자 입력 데이터는 ViewModel에서 관리되고, UI는 ViewModel의 상태를 관찰(sink)하여 반응합니다.

장점

  1. 결합도 감소:
    • UI는 단순히 ViewModel의 상태를 관찰하고 업데이트합니다.
    • 로직은 ViewModel에 집중되어 있어, 다른 화면에서도 동일한 로직을 재사용하기 쉽습니다.
    예: 회원가입 로직을 다른 화면에 추가하고 싶을 때, 동일한 ViewModel을 재사용하면 됩니다.
  2. 상태 관리가 용이:
    • isRegistrationFormValid와 같은 상태를 @Published로 관리하면, UI와 데이터가 동기화됩니다.
    • 예를 들어, 입력값이 바뀌면 자동으로 버튼의 활성화 상태가 변경됩니다.
  3. 테스트 가능성 증가:
    • ViewModel은 UIKit과 분리되어 있으므로, 단위 테스트를 작성하기 쉽습니다.
    • 예: 이메일과 비밀번호 검증 로직을 테스트하거나, Firebase 호출 결과를 테스트할 수 있습니다.

단점

  1. 초기 구현 비용:
    • ViewModel을 정의하고 바인딩 로직을 작성해야 하므로 초기 작업이 다소 복잡합니다.
  2. 작은 규모에서는 과할 수 있음:
    • 간단한 앱에서는 ViewModel을 사용하지 않아도 충분히 관리 가능한 경우가 많습니다.

'UIKIT' 카테고리의 다른 글

Combine을 활용한 함수  (0) 2025.01.08
viewModel.$user에서 user 앞에 $를 붙이는 이유  (0) 2025.01.08
UITabBarAppearance  (0) 2025.01.05
UICollectionReusableView와 UIView의 차이 + headerSection  (0) 2025.01.05
UIFontMetrics  (0) 2025.01.05