Architecture

MVI (Model - View - Intent)

최지철 2024. 6. 7. 14:24
728x90
반응형

MVI란? 

MVI는 Model-View-Intent의 약자로, 사용자의 의도(Intent)가 애플리케이션 상태(State)에 영향을 미치고, 이 상태가 뷰(View)에 반영되는 아키텍처이다.

  • Model: 애플리케이션의 상태를 나타냅니다. 모든 UI 데이터와 비즈니스 로직을 포함
  • View: 사용자 인터페이스를 나타내며, Model의 상태 변화를 반영
  • Intent: 사용자의 액션이나 이벤트를 나타내며, Model의 상태를 변경하는 데 사용

 

그렇다면 상태(State)는 뭘까?

"상태(State)"는 애플리케이션의 현재 데이터를 의미한다.

상태는 즉 앱의 모든 중요한 데이터를 포함해, UI를 비롯한 다양한 부분에서 공유된다.

  • UI 상태: 현재 화면에 표시되는 내용, 사용자 입력, 로딩 상태 등.
  • 도메인 데이터: 애플리케이션이 관리하는 핵심 데이터, 예를 들어 Todo 리스트의 항목들, 사용자 프로필 정보, 쇼핑 카트의 항목 등.
  • 네트워크 상태: API 요청의 상태 (성공, 실패, 로딩 중 등).

 

MVI는 단방향 아키텍처?

단방향 아키텍처는 애플리케이션의 데이터 흐름을 한 방향으로만 흐르도록 설계하는 패턴이다. 이는 상태 관리의 복잡성을 줄이고, 코드의 예측 가능성과 유지보수성을 높이는 데 도움을 준다.

MVI (Model-View-Intent) 아키텍처는 단방향 아키텍처의 형태이다.

MVI는 상태를 한 방향으로 흐르게 하여 예측 가능성과 유지보수성을 높이는 것을 목표로 한다.

 

단방향 데이터 흐름

  1. Intent: 사용자가 View와 상호작용하여 Intent를 생성. (Intent는 사용자의 행동이나 이벤트)
  2. Model: Intent는 Model을 통해 상태 변경을 요청 ->  Model은 새로운 상태를 생성
  3. View: Model의 상태 변화가 View에 반영 -> View는 새로운 상태를 기반으로 UI를 업데이트

 

그렇다면, MVI는 왜 등장하였는가?

복잡한 상태 관리 문제

기존의 MVC 와 MVVM 같은 아키텍처는 상태 관리에 대한 문제점을 갖고있다.

  • 상태 일관성 유지의 어려움: 여러 컴포넌트가 서로 상호작용하면서 상태 일관성을 유지하기 어려워짐.
  • 비동기 작업 관리의 복잡성: 네트워크 요청, 데이터베이스 액세스 등 비동기 작업이 많아지면서 상태 관리가 더욱 복잡해짐.
  • 테스트 어려움: 상태와 로직이 명확히 분리되지 않아 테스트하기 어려움.

 

MVVM은 왜 상태관리에 대해 어려움을 겪는가?

양방향 데이터 바인딩

MVVM의 핵심은 View와 ViewModel 간의 양방향 데이터 바인딩이다. 이는 상태 변화가 자동으로 UI에 반영되고, UI에서의 변경이 다시 ViewModel로 전달됨을 의미한다. 하지만 이로 인해 다음과 같은 문제가 발생할 수 있다.

  • 변경의 출처 추적 어려움: 상태 변화가 양방향으로 이루어지므로, 상태 변화의 원인을 추적하기 어려울 수 있다.
  • 비의도적 상태 변화: 잘못된 바인딩 설정이나 예기치 않은 사용자 입력으로 인해 상태가 비의도적으로 변경될 수 있다.

잠깐, MVVM에서 ViewModel은 View를 모르니, 단방향 아니야?

  • 실제로 이부분이 나를 가장 헷갈리게 하였다.
  • MVVM에서 양방향 데이터 바인딩이란, View와 ViewModel간의 자동으로 동기화 되는 메커니즘을 의미한다.
  • 즉, 방향성이란, 직접적인 상호 참조를 의미하는 것은 아니다!

 

MVI의 장점

  • 유지보수성: 상태 관리 로직이 중앙 집중화되어 있어, 유지보수가 용이합니다.
  • 테스트 용이성: Intent와 Model이 명확히 분리되어 있어, 각각의 구성 요소를 쉽게 테스트할 수 있습니다.
  • 예측 가능한 상태 관리: 모든 상태 변화가 Intent를 통해 이루어지므로, 상태 변화를 쉽게 예측하고 추적할 수 있습니다.
  • 모델이 불변하기 때문에, 큰 앱에서 멀티 스레드 안전성이 높음

 

예시: MVI 아키텍처로 Todo 앱 구현하기

Model

import Combine

struct Todo: Identifiable {
    let id: UUID
    let title: String
    let isCompleted: Bool
}

class TodoModel: ObservableObject {
    @Published var todos: [Todo] = []
    
    func addTodo(title: String) {
        let newTodo = Todo(id: UUID(), title: title, isCompleted: false)
        todos.append(newTodo)
    }
    
    func toggleTodoCompletion(id: UUID) {
        if let index = todos.firstIndex(where: { $0.id == id }) {
            todos[index].isCompleted.toggle()
        }
    }
}


Intent

enum TodoIntent {
    case addTodo(String)
    case toggleTodoCompletion(UUID)
}

class TodoIntentHandler {
    private let model: TodoModel
    
    init(model: TodoModel) {
        self.model = model
    }
    
    func handle(intent: TodoIntent) {
        switch intent {
        case .addTodo (let title):
            model.addTodo(title: title)
        case .toggleTodoCompletion(let id):
            model.toggleTodoCompletion(id: id)
        }
    }
}

 

View

import SwiftUI

struct TodoView: View {
    @ObservedObject var model: TodoModel
    private let intentHandler: TodoIntentHandler
    
    init(model: TodoModel) {
        self.model = model
        self.intentHandler = TodoIntentHandler(model: model)
    }
    
    var body: some View {
        VStack {
            List {
                ForEach(model.todos) { todo in
                    HStack {
                        Text(todo.title)
                        Spacer()
                        if todo.isCompleted {
                            Image(systemName: "checkmark")
                        }
                    }
                    .onTapGesture {
                        intentHandler.handle(intent: .toggleTodoCompletion(todo.id))
                    }
                }
            }
            HStack {
                TextField("New Todo", text: $newTodoTitle)
                Button(action: {
                    intentHandler.handle(intent: .addTodo(newTodoTitle))
                    newTodoTitle = ""
                }) {
                    Text("Add")
                }
            }
            .padding()
        }
    }
    
    @State private var newTodoTitle: String = ""
}

 

요약

MVI 아키텍처는 단방향 데이터 흐름을 통해 상태를 관리하는 아키텍처 패턴이다.
Intent, Model, View 간의 명확한 역할 분담과 데이터 흐름을 통해 상태 관리의 일관성과 예측 가능성을 높일 수 있으며,
MVI 아키텍처를 통해 복잡한 애플리케이션에서도 안정적이고 유지보수하기 쉬운 코드를 작성할 수 있다.

 

728x90
반응형