Swift 디자인 패턴 심화 과정을 통해 단순히 패턴을 “암기”하는 수준이 아닌, 왜 이 패턴을 쓰는지, 언제 적용해야 하는지, 그리고 실전 프로젝트에서 어떻게 활용하는지까지 이해할 수 있도록 정리해드리겠습니다. 이제는 자주 쓰는 패턴을 넘어, 아키텍처 설계, 유지보수성 향상, 확장성 확보를 위한 디자인 패턴의 진짜 가치를 배워볼 시간입니다!
[Swift Apple ] Swift 디자인 패턴 심화 과정 |
🎯 Swift 디자인 패턴 심화 과정
– 실전 중심! 유지보수성과 확장성을 위한 고급 설계 전략
✅ 디자인 패턴, 언제 그리고 왜 사용할까?
디자인 패턴은 “무조건 따라 쓰는 규칙”이 아닙니다. 반복적으로 발생하는 문제를 더 나은 구조로 해결하기 위한 지침이에요.
디자인 패턴 적용이 필요한 순간
-
프로젝트가 커지면서 코드가 복잡해질 때
-
기능 추가 시 기존 코드 수정이 위험할 때
-
여러 명이 개발하는 상황에서 일관된 구조가 필요할 때
-
재사용 가능한 모듈을 만들고 싶을 때
⚡ 중요한 건 상황에 맞는 패턴 선택과 적용 시점!
🚀 심화 과정에서 다룰 주요 디자인 패턴
패턴명 |
활용 포인트 |
---|---|
MVVM 패턴 |
SwiftUI & iOS 아키텍처 설계 |
Coordinator 패턴 |
복잡한 화면 전환(네비게이션) 관리 |
Dependency Injection |
의존성 주입을 통한 테스트 가능성과 유연성 확보 |
Composite 패턴 |
계층적 UI 구조 설계 |
Command 패턴 |
요청을 객체로 캡슐화 (Undo/Redo 시스템 등) |
State 패턴 |
객체 상태 변화에 따른 행동 변경 |
Mediator 패턴 |
객체 간 복잡한 의존 관계를 중재 |
1️⃣ MVVM 패턴 (Model-View-ViewModel)
현대 iOS & SwiftUI 앱의 핵심 아키텍처
-
✅ View: UI (SwiftUI View / UIKit ViewController)
-
✅ ViewModel: 비즈니스 로직 처리, 데이터 가공
-
✅ Model: 순수 데이터 구조
MVVM 흐름도
Model ⟷ ViewModel ⟷ View
SwiftUI 예시:
class CounterViewModel: ObservableObject {
@Published var count = 0
func increment() {
count += 1
}
}
MVVM을 사용하면 테스트 가능성, 유지보수성이 크게 향상됩니다.
2️⃣ Coordinator 패턴 – 화면 전환 관리 마스터하기
복잡한 앱일수록 화면 전환(Navigation)이 엉키기 쉽습니다.
이를 깔끔하게 관리해주는 것이 Coordinator 패턴이에요.
-
ViewController에서 화면 전환 로직 제거
-
Coordinator가 모든 흐름을 담당
✅ 네비게이션이 복잡한 프로젝트에서 필수적인 패턴!
3️⃣ Dependency Injection (DI) – 의존성 주입
객체가 직접 다른 객체를 생성하지 않고,
외부에서 필요한 의존성을 주입받아 유연성과 테스트 효율을 높이는 패턴.
예시:
class NetworkManager {
func fetchData() { ... }
}
class ViewModel {
let network: NetworkManager
init(network: NetworkManager) {
self.network = network
}
}
-
DI를 적용하면 Mock 객체를 활용한 테스트도 쉬워짐!
4️⃣ Composite 패턴 – 계층적 구조 설계
UI 요소나 데이터 트리 구조를 설계할 때 유용.
예시: 폴더-파일 구조
protocol Component {
func display()
}
class File: Component {
func display() { print("파일") }
}
class Folder: Component {
var items: [Component] = []
func display() {
print("폴더 열기")
items.forEach { $0.display() }
}
}
5️⃣ Command 패턴 – 명령 캡슐화
모든 요청을 객체화해서 나중에 실행하거나, 취소, 재실행이 가능하게 하는 패턴.
주로 Undo/Redo 시스템, 버튼 클릭 처리 등에 활용.
6️⃣ State 패턴 – 상태에 따른 행동 변경
객체의 상태가 바뀔 때, 그에 맞는 행동을 유연하게 변경할 수 있는 패턴.
예시: 게임 캐릭터의 상태 (공격, 방어, 대기 등)에 따라 다른 동작 수행
7️⃣ Mediator 패턴 – 복잡한 의존성 중재자
여러 객체가 서로 직접 소통하지 않고,
중앙 관리자를 통해 통신하도록 설계해 복잡도를 줄이는 패턴.
✏️ 심화 실습 과제
MVVM 아키텍처로 간단한 Todo 앱 구현
-
Coordinator 패턴으로 로그인 → 메인 화면 전환 설계
-
DI를 활용한 네트워크 모듈 테스트 코드 작성
-
Command 패턴으로 Undo 기능이 있는 텍스트 편집기 구현
-
State 패턴으로 캐릭터 상태 관리 시스템 개발
✅ 1. MVVM 아키텍처로 간단한 Todo 앱 구현 (콘솔 버전)
// Model
struct Todo {
var title: String
var isCompleted: Bool
}
// ViewModel
class TodoViewModel {
private var todos: [Todo] = []
func addTodo(title: String) {
todos.append(Todo(title: title, isCompleted: false))
}
func completeTodo(at index: Int) {
guard todos.indices.contains(index) else { return }
todos[index].isCompleted = true
}
func getTodoList() -> [String] {
todos.enumerated().map {
"\($0.offset + 1). \($0.element.title) [\($0.element.isCompleted ? "완료" : "미완료")]"
}
}
}
// View (Console 출력용)
class TodoView {
private let viewModel = TodoViewModel()
func simulate() {
viewModel.addTodo(title: "Swift 공부하기")
viewModel.addTodo(title: "운동하기")
viewModel.completeTodo(at: 0)
print(viewModel.getTodoList().joined(separator: "\n"))
}
}
// 실행
TodoView().simulate()
MVVM 아키텍처로 간단한 Todo 앱 구현 (콘솔 버전) |
✅ 2. Coordinator 패턴으로 로그인 → 메인 화면 전환
protocol Coordinator {
func start()
}
class AppCoordinator: Coordinator {
func start() {
showLogin()
}
private func showLogin() {
let loginVC = LoginViewController()
loginVC.coordinator = self
loginVC.login()
}
func showMain() {
let mainVC = MainViewController()
mainVC.loadMain()
}
}
class LoginViewController {
var coordinator: AppCoordinator?
func login() {
print("🔐 로그인 성공!")
coordinator?.showMain()
}
}
class MainViewController {
func loadMain() {
print("🏠 메인 화면 로드 완료!")
}
}
// 실행
let app = AppCoordinator()
app.start()
Coordinator 패턴으로 로그인 → 메인 화면 전환 |
✅ 3. DI를 활용한 네트워크 모듈 테스트 코드 작성
protocol NetworkService {
func fetchData(endpoint: String, completion: @escaping (String) -> Void)
}
class RealNetworkService: NetworkService {
func fetchData(endpoint: String, completion: @escaping (String) -> Void) {
print("🌐 실 데이터 요청 중... [\(endpoint)]")
completion("서버 데이터")
}
}
class MockNetworkService: NetworkService {
func fetchData(endpoint: String, completion: @escaping (String) -> Void) {
print("🧪 테스트용 데이터 반환")
completion("테스트 데이터")
}
}
class ViewModel {
private let network: NetworkService
init(network: NetworkService) {
self.network = network
}
func load(completion: @escaping (String) -> Void) {
network.fetchData(endpoint: "/user", completion: completion)
}
}
// 테스트
let testVM = ViewModel(network: MockNetworkService())
testVM.load { data in
print("결과: \(data)")
}
DI를 활용한 네트워크 모듈 테스트 코드 작성 |
✅ 4. Command 패턴으로 Undo 기능이 있는 텍스트 편집기
protocol Command {
func execute()
func undo()
}
class AddTextCommand: Command {
private var editor: TextEditor
private var text: String
init(editor: TextEditor, text: String) {
self.editor = editor
self.text = text
}
func execute() {
editor.addText(text)
}
func undo() {
editor.removeLastText()
}
}
class TextEditor {
private(set) var content: [String] = []
func addText(_ text: String) {
content.append(text)
print("✏️ 추가됨: \(text)")
}
func removeLastText() {
guard !content.isEmpty else { return }
let removed = content.removeLast()
print("↩️ 되돌리기: \(removed)")
}
func show() {
print("📄 현재 내용: \(content.joined(separator: " "))")
}
}
// 사용 예시
let editor = TextEditor()
let command1 = AddTextCommand(editor: editor, text: "Hello")
let command2 = AddTextCommand(editor: editor, text: "World")
command1.execute()
command2.execute()
editor.show()
command2.undo()
editor.show()
Command 패턴으로 Undo 기능이 있는 텍스트 편집기 |
✅ 5. State 패턴으로 캐릭터 상태 관리 시스템
protocol CharacterState {
func action()
}
class IdleState: CharacterState {
func action() {
print("😐 캐릭터가 가만히 있습니다.")
}
}
class AttackState: CharacterState {
func action() {
print("⚔️ 캐릭터가 공격합니다!")
}
}
class DeadState: CharacterState {
func action() {
print("💀 캐릭터가 사망했습니다.")
}
}
class GameCharacter {
private var state: CharacterState = IdleState()
func setState(_ newState: CharacterState) {
self.state = newState
}
func performAction() {
state.action()
}
}
// 사용 예시
let character = GameCharacter()
character.performAction()
character.setState(AttackState())
character.performAction()
character.setState(DeadState())
character.performAction()
State 패턴으로 캐릭터 상태 관리 시스템 |
🏁 마무리 요약
과제 |
핵심 패턴 |
주요 학습 포인트 |
---|---|---|
1. MVVM |
MVVM |
ViewModel에서 로직 분리 |
2. Coordinator |
화면 전환 책임 분리 |
뷰컨의 역할 단순화 |
3. DI + Test |
의존성 주입 |
테스트 용이성 향상 |
4. Command |
실행 취소 처리 |
실행/되돌리기 구현 |
5. State |
상태 전이 |
객체 상태에 따라 동작 변경 |
🎯 마무리하며
Swift 디자인 패턴 심화 과정 핵심 요약:
-
단순히 패턴을 “적용”하는 것을 넘어,
언제, 왜 사용하는지를 이해하는 것이 중요
-
아키텍처 설계 단계에서 패턴을 활용하면
코드 품질, 유지보수성, 확장성이 획기적으로 향상
-
Swift + 디자인 패턴 = 클린 코드 + 실전 대응력
댓글 쓰기