|
| 1 | +>[!question] |
| 2 | +>GQ1. Delegate란 무엇인가 |
| 3 | +>GQ2. 어떻게 쓰는 것인가 |
| 4 | +>GQ3. 어디에 쓰이는 건가 |
| 5 | +
|
| 6 | +## Description |
| 7 | + |
| 8 | +- 객체 간의 1:1 위임(위탁) 관계를 만들어 특정 작업이나 이벤트 처리를 다른 객체에 '위임'하는 방식 |
| 9 | + -> 한 객체가 해야 할 일부 작업을 대신 **다른 객체에게 맡기는** 디자인 패턴 |
| 10 | +- 메서드 자체를 인자로 넘겨주는 '형식' |
| 11 | +- 델리게이트를 통해 메서드를 매개변수로 전달 할 수 있다 |
| 12 | +- **함수가 아니라 "형식" 임**!! |
| 13 | + |
| 14 | +> A 객체가 특정 상황이 발생하면 B 객체에게 '이거 좀 처리해줘'라고 부탁하는 방식 |
| 15 | +
|
| 16 | ++ 언제 쓰일까? |
| 17 | + + UI 이벤트 처리 |
| 18 | + + 비동기 작업 처리 |
| 19 | + |
| 20 | ++ 목적 |
| 21 | + + 객체 간 결합도를 낮추고(loosely coupled), |
| 22 | + + 역할을 분리하여 유지보수를 쉽게 하기 위함 |
| 23 | + |
| 24 | + 1. 역할 분리 - 이벤트 발생하는 객체와 처리하는 객체를 분리할 수 있음 |
| 25 | + 2. 재사용성 증가 - 동일한 클래스를 다양한 상황에서 활용 가능 |
| 26 | + 3. 결합도 낮춤 - 직접적인 의존 관게가 줄어 유연성이 높아짐 |
| 27 | + |
| 28 | +## 주요 기능 |
| 29 | + |
| 30 | ++ 동작 흐름 |
| 31 | + 1. 프로토콜 정의 |
| 32 | + : "이런 함수들을 구현해야 함"라는 약속을 정의 |
| 33 | + ```swift |
| 34 | + protocol MyButtonDelegate: AnyObject { |
| 35 | + func buttonDidTap() // 위임할 함수 |
| 36 | + } |
| 37 | + ``` |
| 38 | + |
| 39 | + 2. 델리게이트 변수 선언 |
| 40 | + : 이벤트를 위임할 객체를 담을 변수(weak var delegate) 선언 |
| 41 | + ```swift |
| 42 | + lass MyButton { |
| 43 | + weak var delegate: MyButtonDelegate? // 반드시 weak(순환 참조 방지) |
| 44 | + func tap() { |
| 45 | + print("버튼이 눌렸습니다.") |
| 46 | + delegate?.buttonDidTap() // 델리게이트에 위임 |
| 47 | + } |
| 48 | + } |
| 49 | + ``` |
| 50 | + + ~~weak에 대해 알아봐야 될 것 같다.. (순환 참조도..)~~ |
| 51 | + |
| 52 | + 3. 프로토콜 채택 및 구현 |
| 53 | + : 다른 클래스에서 해당 프로토콜을 채택하고, 실제 동작을 구현 |
| 54 | + ```swift |
| 55 | + class ViewController: MyButtonDelegate { |
| 56 | + func buttonDidTap() { |
| 57 | + print("ViewController에서 버튼 눌림 이벤트 처리!") |
| 58 | + } |
| 59 | + } |
| 60 | + ``` |
| 61 | + |
| 62 | + 4. 이벤트 발생 시 delegate 호츨 |
| 63 | + : 주체 객체는 이벤트가 발생하면 delegate의 함수를 호출 |
| 64 | + ```swift |
| 65 | + let button = MyButton() |
| 66 | + let vc = ViewController() |
| 67 | + button.delegate = vc |
| 68 | + button.tap() |
| 69 | + // 출력: |
| 70 | + // 버튼이 눌렸습니다. |
| 71 | + // ViewController에서 버튼 눌림 이벤트 처리! |
| 72 | + ``` |
| 73 | + |
| 74 | +## 코드 예시 |
| 75 | + |
| 76 | ++ MPC(Multipeer Connectivity)에서의 Delegate 사용 |
| 77 | + + 흐름 |
| 78 | + MultipeerConnectivity 내부 이벤트 |
| 79 | + ↓ |
| 80 | + MPCManager(MCSessionDelegate로 콜백 받음) |
| 81 | + ↓ (자체 프로토콜 델리게이트 호출) |
| 82 | + View/ViewModel (UI 업데이트 or 데이터 처리) |
| 83 | + |
| 84 | + 1. 프로토콜 정의 |
| 85 | + ```swift |
| 86 | + protocol MPCManagerDelegate: AnyObject { |
| 87 | + func connectedPeersChanged(_ peers: [MCPeerID]) |
| 88 | + func didReceive(message: String, from peer: MCPeerID) |
| 89 | + } |
| 90 | + ``` |
| 91 | + |
| 92 | + 2. MPCManager 클래스 |
| 93 | + - MCSessionDelegate 등 MPC 기본 델리게이트를 채택해 **이벤트를 수신** |
| 94 | + - 내부에서 **자신이 만든 delegate에게 이벤트를 다시 전달** |
| 95 | + ```swift |
| 96 | + import MultipeerConnectivity |
| 97 | + |
| 98 | + class MPCManager: NSObject, ObservableObject { |
| 99 | + weak var delegate: MPCManagerDelegate? |
| 100 | + |
| 101 | + private let myPeerID = MCPeerID(displayName: UIDevice.current.name) |
| 102 | + private var session: MCSession! |
| 103 | + |
| 104 | + override init() { |
| 105 | + super.init() |
| 106 | + session = MCSession(peer: myPeerID, securityIdentity: nil, encryptionPreference: .required) |
| 107 | + session.delegate = self |
| 108 | + } |
| 109 | + } |
| 110 | + |
| 111 | + // MARK: - MCSessionDelegate |
| 112 | + extension MPCManager: MCSessionDelegate { |
| 113 | + func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { |
| 114 | + print("Peer 상태 변경: \(peerID.displayName), state: \(state.rawValue)") |
| 115 | + DispatchQueue.main.async { |
| 116 | + self.delegate?.connectedPeersChanged(session.connectedPeers) |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { |
| 121 | + if let message = String(data: data, encoding: .utf8) { |
| 122 | + DispatchQueue.main.async { |
| 123 | + self.delegate?.didReceive(message: message, from: peerID) |
| 124 | + } |
| 125 | + } |
| 126 | + } |
| 127 | + |
| 128 | + // 나머지는 기본 구현 |
| 129 | + func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {} |
| 130 | + func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {} |
| 131 | + func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {} |
| 132 | + } |
| 133 | + ``` |
| 134 | + |
| 135 | + 3. ViewModel 또는 ViewController에서 델리게이트 채택 |
| 136 | + ```swift |
| 137 | + class WaitingRoomViewModel: ObservableObject, MPCManagerDelegate { |
| 138 | + @Published var connectedPeers: [MCPeerID] = [] |
| 139 | + @Published var receivedMessages: [String] = [] |
| 140 | + |
| 141 | + private var mpcManager = MPCManager() |
| 142 | + |
| 143 | + init() { |
| 144 | + mpcManager.delegate = self |
| 145 | + } |
| 146 | + |
| 147 | + func connectedPeersChanged(_ peers: [MCPeerID]) { |
| 148 | + connectedPeers = peers |
| 149 | + } |
| 150 | + |
| 151 | + func didReceive(message: String, from peer: MCPeerID) { |
| 152 | + receivedMessages.append("\(peer.displayName): \(message)") |
| 153 | + } |
| 154 | + } |
| 155 | + ``` |
| 156 | + |
| 157 | + 4. SwiftUI View에서 ViewModel와 바인딩 |
| 158 | + ```swift |
| 159 | + struct WaitingRoomView: View { |
| 160 | + @StateObject private var viewModel = WaitingRoomViewModel() |
| 161 | + |
| 162 | + var body: some View { |
| 163 | + VStack { |
| 164 | + Text("참여자: \(viewModel.connectedPeers.map { $0.displayName }.joined(separator: ", "))") |
| 165 | + List(viewModel.receivedMessages, id: \.self) { msg in |
| 166 | + Text(msg) |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | + } |
| 171 | + ``` |
| 172 | + |
| 173 | +-> MPCManager가 혼자 모든 일을 하지 않고, "이런 이벤트가 일어나면 너가(ViewModel) 대신 처리해줘" 라고 **부탁**하는 메커니즘 (이벤트 전달자 역할) |
| 174 | ++ 비유 |
| 175 | + + MPCManager = 비서 |
| 176 | + + Delegate = 상사 |
| 177 | + -> 비서가 알려주고 상사가 직접 처리 |
| 178 | + |
| 179 | + |
| 180 | +>[!Answer] |
| 181 | +>델리게이트 = '대신 처리해주는 대리인' |
| 182 | +
|
| 183 | + |
| 184 | +## Keywords |
| 185 | ++ 파생된 키워드들을 작성 |
| 186 | + |
| 187 | +## References |
| 188 | +- 참고한 레퍼런스를 작성 (예 : Apple의 공식 문서) |
0 commit comments