|
| 1 | +## 1. **프로토콜 (Protocol)** |
| 2 | +| `프로토콜`은 **클래스, 구조체, 열거형이 따라야 하는 "규칙"**을 정의하는 타입 |
| 3 | +어떤 타입이 특정 프로토콜을 채택하면 그 프로토콜이 정의한 메서드나 프로퍼티를 반드시 구현해야 함 |
| 4 | +```swift |
| 5 | +protocol Drivable { |
| 6 | + func startEngine() |
| 7 | + func drive() |
| 8 | +} |
| 9 | +``` |
| 10 | +이 프로토콜을 채택하면 아래처럼 메서드를 구현해야 함! |
| 11 | +```swift |
| 12 | +struct Car: Drivable { |
| 13 | + func startEngine() { |
| 14 | + print("Engine started") |
| 15 | + } |
| 16 | + |
| 17 | + func drive() { |
| 18 | + print("Car is driving") |
| 19 | + } |
| 20 | +} |
| 21 | +``` |
| 22 | + |
| 23 | +### 왜 필요할까? |
| 24 | +- **일관된 인터페이스 보장**: 여러 타입이 동일한 동작을 할 수 있도록 강제함 |
| 25 | +- **의존성 역전 (DIP)**: 구체적인 타입이 아닌 "기능"에 의존하게 함 |
| 26 | +- **다형성**: 다양한 객체를 같은 방식으로 처리할 수 있음 |
| 27 | + |
| 28 | +## 2. **델리게이트 패턴 (Delegate Pattern)** |
| 29 | +> 델리게이트(Delegate) 패턴은 **어떤 객체가 해야 할 일을 다른 객체에게 위임(delegate)** 하는 디자인 패턴 |
| 30 | +보통 프로토콜을 기반으로 구성되며, 어떤 객체가 프로토콜을 통해 "일을 대신할 대상"을 등록하고, 그 대상이 프로토콜을 채택해 필요한 기능을 구현함 |
| 31 | + |
| 32 | +왜 필요할까? |
| 33 | +- **역방향 데이터 전달**: 자식 → 부모 뷰로 이벤트 전달 |
| 34 | +- **책임 분리**: 하나의 객체에 너무 많은 역할을 몰아주지 않음 |
| 35 | +- **유연성**: 동작을 외부에서 커스터마이즈 가능 |
| 36 | + |
| 37 | +### 델리게이트 패턴 구성 요약 |
| 38 | + |
| 39 | +|프로토콜 정의|`MyViewDelegate`|어떤 메서드를 구현해야 할지 명시| |
| 40 | +|---|---|---| |
| 41 | +|델리게이트 변수|`weak var delegate: MyViewDelegate?`|대리자(역할 수행자) 연결| |
| 42 | +|호출|`delegate?.somethingDidHappen()`|대리자에게 이벤트 전달| |
| 43 | +|프로토콜 채택 및 구현|보통 ViewController|이벤트에 반응하는 코드 작성| |
| 44 | + |
| 45 | +### 예시) 버튼이 눌렸을 때 ViewController에게 알리는 커스텀 뷰 |
| 46 | +```swift |
| 47 | +// 1. 프로토콜 정의 |
| 48 | +protocol CustomButtonDelegate: AnyObject { |
| 49 | + func buttonWasTapped() |
| 50 | +} |
| 51 | + |
| 52 | +// 2. 커스텀 뷰 정의 |
| 53 | +class CustomButtonView: UIView { |
| 54 | + weak var delegate: CustomButtonDelegate? |
| 55 | + |
| 56 | + func buttonTapped() { |
| 57 | + // 3. 델리게이트에게 알림 |
| 58 | + delegate?.buttonWasTapped() |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | +```swift |
| 63 | +// 4. ViewController에서 델리게이트 채택 |
| 64 | +class ViewController: UIViewController, CustomButtonDelegate { |
| 65 | + func buttonWasTapped() { |
| 66 | + print("Button was tapped in the custom view!") |
| 67 | + } |
| 68 | + |
| 69 | + func setup() { |
| 70 | + let customView = CustomButtonView() |
| 71 | + customView.delegate = self |
| 72 | + } |
| 73 | +} |
| 74 | +``` |
| 75 | + |
| 76 | +## ** 정리 |
| 77 | +||프로토콜|델리게이트 패턴| |
| 78 | +|---|---|---| |
| 79 | +|개념|기능의 "약속"|"위임"을 통해 일을 분리함| |
| 80 | +|목적|인터페이스 통일|이벤트 전달 & 역할 분산| |
| 81 | +|연관성|프로토콜 기반으로 동작|항상 프로토콜과 함께 사용| |
| 82 | +|주요 용도|기능 설계|자식 → 부모 이벤트 전달 등| |
| 83 | + |
| 84 | + |
| 85 | +## Q) 언제 쓰면 좋을까? |
| 86 | +- View ↔ ViewController 간 통신 (ex. 버튼 누름, 스크롤 이벤트 등) |
| 87 | +- TableView, CollectionView처럼 iOS 시스템이 제공하는 델리게이트 패턴 활용 시 |
| 88 | +- 복잡한 동작을 모듈로 분리하고 싶을 때 |
| 89 | + |
| 90 | +# !! 근데 SwiftUI에서는 delegate가 사라짐 !! |
| 91 | +> SwiftUI의 설계 철학과 아키텍처가 기존 UIKit과 크게 다름 UIKit은 **객체지향(OOP)** 기반으로 동작함 |
| 92 | +```swift |
| 93 | +class MyViewController: UIViewController, UITableViewDelegate { |
| 94 | + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { |
| 95 | + // 셀 선택됨 |
| 96 | + } |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +- delegate = UIKit 컴포넌트의 이벤트를 외부 객체로 전달하기 위한 방법!! |
| 101 | +- UIKit은 명령형(imperative) UI 모델로 구성돼 있어서, 이벤트가 발생할 때마다 개발자가 수동으로 상태를 업데이트해줘야함 |
| 102 | + |
| 103 | +→ 다양한 이벤트 처리를 위해 delegate 패턴이 필수…였는데요 |
| 104 | +## SwiftUI에서는 왜 delegate가 사라졌을까? |
| 105 | +> SwiftUI는 UIKit과 달리 선언형(declarative) UI 모델을 사용한다 |
| 106 | +```swift |
| 107 | +List(items) { item in |
| 108 | + Text(item.name) |
| 109 | + .onTapGesture { |
| 110 | + // 이 아이템이 탭됐을 때 처리 |
| 111 | + } |
| 112 | +} |
| 113 | +``` |
| 114 | +SwiftUI는 **뷰의 상태(state)**와 UI를 선언적으로 연결해 놓음 |
| 115 | + |
| 116 | +### 1. **상태 기반 데이터 흐름 (State-Driven UI)** |
| 117 | +SwiftUI는 `@State`, `@Binding`, `@ObservedObject`, `@Environment` 등 다양한 상태 속성 래퍼를 사용해서 데이터의 변경이 곧 UI의 변경으로 직결되는 구조 |
| 118 | +```swift |
| 119 | +@State private var isOn = false |
| 120 | + |
| 121 | +Toggle("Switch", isOn: $isOn) |
| 122 | +``` |
| 123 | +- `delegate` 없이도 `@State`가 바뀌면 UI가 자동으로 다시 렌더링됨 |
| 124 | +- 반응형 프로그래밍 → 별도로 이벤트 처리 객체(delegate)를 둘 필요가 없음! |
| 125 | + |
| 126 | +### 2. **View는 구조체 Struct → 소유관계가 사라짐** |
| 127 | +UIKit은 클래스 기반이기 때문에 `delegate`처럼 다른 객체를 참조해서 처리해야 했고, |
| 128 | +SwiftUI의 뷰는 대부분 `struct`라서 참조보다 데이터 전달과 바인딩에 집중함 |
| 129 | +```swift |
| 130 | +struct CustomView: View { |
| 131 | + var onComplete: () -> Void // delegate 대신 클로저를 받음 |
| 132 | +} |
| 133 | +``` |
| 134 | +- delegate 프로퍼티를 가질 수 없음 (구조체는 참조를 보관하지 않음) |
| 135 | +- 대신 클로저로 필요한 로직을 주입 (event handler로 대체) |
| 136 | + |
| 137 | +### 3. **단방향 데이터 흐름** |
| 138 | +SwiftUI는 모든 데이터가 위에서 아래로 흐르도록 설계되어 있음 |
| 139 | +```swift |
| 140 | +ParentView |
| 141 | + └── ChildView(data: ..., onTap: { ... }) |
| 142 | + |
| 143 | +``` |
| 144 | +- 뷰는 데이터를 직접 수정하지 않고, 상위 뷰의 바인딩이나 클로저를 통해 동작만 위임 |
| 145 | +- delegate 같은 양방향 흐름은 SwiftUI에서 지양함 |
| 146 | + |
| 147 | +## Q) 그럼 delegate는 완전히 사라졌을까? |
| 148 | +아니요!!!!!!! UIKit을 사용할 때는 여전히 필요함!!! |
| 149 | +`UIViewRepresentable` 같은 SwiftUI <-> UIKit 브리징 컴포넌트에서는 여전히 delegate를 사용함 |
| 150 | + |
| 151 | +ex) |
| 152 | +```swift |
| 153 | +class Coordinator: NSObject, UITextFieldDelegate { |
| 154 | + func textFieldShouldReturn(_ textField: UITextField) -> Bool { |
| 155 | + ... |
| 156 | + } |
| 157 | +} |
| 158 | +``` |
| 159 | + |
| 160 | +## ** 결론 |
| 161 | +> SwiftUI는 상태 중심 + 선언형 UI + 함수형 |
| 162 | +> UIKit에서 쓰던 delegate 패턴은 필요 없어졌고, 대신 클로저, 바인딩, ObservableObject 같은 기능이 대신하고 있음 |
0 commit comments