[Swift Apple ] Swift 디자인 패턴 심화 과정

Swift 디자인 패턴 심화 과정을 통해 단순히 패턴을 “암기”하는 수준이 아닌, 왜 이 패턴을 쓰는지, 언제 적용해야 하는지그리고 실전 프로젝트에서 어떻게 활용하는지까지 이해할 수 있도록 정리해드리겠습니다. 이제는 자주 쓰는 패턴을 넘어, 아키텍처 설계, 유지보수성 향상, 확장성 확보를 위한 디자인 패턴의 진짜 가치를 배워볼 시간입니다!


[Swift Apple ] 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 앱 구현 (콘솔 버전)
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 패턴으로 로그인 → 메인 화면 전환
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를 활용한 네트워크 모듈 테스트 코드 작성
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 기능이 있는 텍스트 편집기
 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 패턴으로 캐릭터 상태 관리 시스템
State 패턴으로 캐릭터 상태 관리 시스템




🏁 마무리 요약

과제

핵심 패턴

주요 학습 포인트

1. MVVM

MVVM

ViewModel에서 로직 분리

2. Coordinator

화면 전환 책임 분리

뷰컨의 역할 단순화

3. DI + Test

의존성 주입

테스트 용이성 향상

4. Command

실행 취소 처리

실행/되돌리기 구현

5. State

상태 전이

객체 상태에 따라 동작 변경






🎯 마무리하며


Swift 디자인 패턴 심화 과정 핵심 요약:

  • 단순히 패턴을 “적용”하는 것을 넘어,

    언제, 왜 사용하는지를 이해하는 것이 중요

  • 아키텍처 설계 단계에서 패턴을 활용하면

    코드 품질, 유지보수성, 확장성이 획기적으로 향상

  • Swift + 디자인 패턴 = 클린 코드 + 실전 대응력


댓글 쓰기