2023년 1월 1일
08:00 AM
Buffering ...

최근 글 👑

[Swift-Study] iOS 앱 개발 심화 1주차 - 아키텍처

2024. 5. 1. 00:56ㆍIOS/Swift-Study
SMALL

아키텍처란?

소프트웨어 시스템 전체의 구조와 조직을 결정하는 프로세스로,

시스템의 기본적인 구조를 설계하고 이를 컴포넌트로 분할하며,

컴포넌트 간의 상호 작용 및 데이터 흐름을 정의하는 것을 포함

소프트웨어 아키텍처는 시스템의 전체적인 모습을 정의하며,

시스템의 기능, 성능, 보안, 확장성, 유지보수성 등과 같은

'비기능적인 요구사항'에 대한 '해결책' 제공

 

Apple MVC - Architecture

지금까지의 실습 단계에서 사용된 패턴과 동일

MVC(Model-View-Controller) 아키텍처는 애플리케이션의 구조를 구성하는 패턴 중 하나

아래에서 설명하는 MVC 패턴은, Apple의 MVC패턴을 기반으로 설명

Model

데이터와 데이터를 처리하는 로직을 담당

애플리케이션의 상태와 동작을 나타내는 데이터를 관리,

이 데이터를 조작 및 업데이트하는데 필요한 비즈니스 로직을 포함

모델은 주로 데이터 구조체나 클래스로 표현,

데이터베이스나 네트워크와 같은 외부 데이터 소스와의 상호 작용을 담당 가능

import Foundation

struct Todo {
    var text: String
    var completed: Bool
}

View

사용자 인터페이스 (화면에 표시되는 요소들을 담당)

뷰는 사용자가 볼 수 있는 것들로 구성되어 있고,

주로 UI 컴포넌트들(버튼, 레이블, 이미지 등)을 포함

뷰는 사용자의 입력을 받아 컨트롤러에 전달,

모델의 데이터를 표시할 때 사용

Controller

모델과 뷰 간의 상호 작용을 관리

사용자의 입력을 받아 해당하는 모델을 업데이트하고,

모델의 변경 사항을 감지하여 뷰를 업데이트

즉, 컨트롤러는 뷰와 모델 사이의 중간자 역할을 수행,

사용자 입력을 받아 처리하고 그 결과를 화면에 반영

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var textField: UITextField!
    
		var todos: [Todo] = []
    
    @IBAction func addButtonTapped(_ sender: UIButton) {
        guard let text = textField.text, !text.isEmpty else { return }
        self.addTodo(text: text)
        tableView.reloadData()
        textField.text = ""
    }

	  func addTodo(text: String) {
		let newTodo = Todo(text: text, completed: false)
		self.todos.append(newTodo)
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.todos.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TodoCell", for: indexPath)
        let todo = self.todos[indexPath.row]
        cell.textLabel?.text = todo.text
        cell.accessoryType = todo.completed ? .checkmark : .none
        return cell
    }
}

 

장점

간단한 구조

MVC는 소프트웨어를 세 가지 주요 부분으로 분할하여 구조화하기 때문에

개발자들에게 직관적이고 이해하기 쉬운 구조를 제공

모델(Model), 뷰(View), 컨트롤러(Controller)로 구분하여

각 부분이 서로 독립적으로 작동하면서 전체적인 애플리케이션 로직을 구성

 

Apple의 Cocoa 프레임워크와 호환성

Apple은 MVC를 기본적으로 지원하고 Cocoa 프레임워크에서도 널리 사용

iOS 및 macOS 애플리케이션 개발에서 Cocoa 프레임워크는 MVC를 따르며,

이는 Apple의 공식적인 지침을 따르며 애플리케이션을 구축하는 데 도움이 됨

단점

Massive View Controller(MVC)

Apple의 MVC 구현에서는 종종 뷰 컨트롤러가 매우 비대해지는 경향이 있음

이는 하나의 뷰 컨트롤러에 많은 역할과 책임이 집중되어 복잡성을 증가시키고 유지보수를 어렵게 만듦

테스트 용이성

Cocoa MVC에서는 테스트하기 어려운 코드가 뷰 컨트롤러에 집중될 수 있음

사용자 인터페이스와 비즈니스 로직이 뒤섞여 있어 테스트를 분리하기 어려울 수 있음

 

MVVM - Architecture

사용자 인터페이스와 로직을 분리하여 유지보수성을 향상,

확장 가능한 애플리케이션을 만들기 위해

모델(데이터), 뷰(UI), 뷰 모델(비즈니스 로직)의 세 가지 구성 요소로 분리하는 것이 "아키텍처"

Model

데이터와 비즈니스 로직을 담당하는 부분

import Foundation

struct Todo {
    var text: String
    var completed: Bool

		mutating func toggle() {
				self.completed.toggle()
		}
}

ViewModel

'Model'과 'View' 간의 중간 매개체로, 'View'에 표시할 데이터를 가지고 있고,

사용자 입력을 받아 'Model'에 전달

import Foundation

class TodoViewModel {
  var todos: [Todo] = []
  
  func addTodo(text: String) {
    let newTodo = Todo(text: text, completed: false)
    todos.append(newTodo)
  }
  
  func toggleTodoCompletion(for index: Int) {
    todos[index].toggle()
  }
}

View

이곳에서의 View는,

Apple MVC와 달리

Storyboard + Controller를 모두 총칭하는 개념으로 사용

 

import UIKit

class ViewController: UIViewController {
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var textField: UITextField!
    
    var viewModel = TodoViewModel()
    
    @IBAction func addButtonTapped(_ sender: UIButton) {
        guard let text = textField.text, !text.isEmpty else { return }
        viewModel.addTodo(text: text)
        tableView.reloadData()
        textField.text = ""
    }
}

extension ViewController: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.todos.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "TodoCell", for: indexPath)
        let todo = viewModel.todos[indexPath.row]
        cell.textLabel?.text = todo.text
        cell.accessoryType = todo.completed ? .checkmark : .none
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        viewModel.toggleTodoCompletion(for: indexPath.row)
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
}

 

장점

뷰와 로직의 분리

MVVM은 뷰와 비즈니스 로직을 분리시켜주어 각각의 역할을 명확히 함

이를 통해 코드의 가독성이 향상되고 유지보수가 쉬워짐

 

테스트 용이성

뷰모델은 일반적으로 순수한 비즈니스 로직을 담고 있으며,

이로 인해 테스트하기 쉬운 코드가 됨

뷰모델은 UI와 독립적으로 테스트할 수 있어서 테스트 코드 작성이 간편

단점

Learning Curve MVVM은 초기에 이해하기 어려울 수 있음

특히 처음 사용하는 개발자들에게는 데이터 바인딩,

뷰모델 작성 등의 개념을 이해하는 데 시간이 걸릴 수 있음

 

보일러 플레이트

작은 규모의 프로젝트/화면에서는 오히려 'ViewModel'의 적용이 코드의 복잡성 증가,

반복적으로 비슷한 형태의 코드가 사용되는 보일러 플레이트의 단점이 드러날 수 있음

728x90