* Delegate Pattern에 대해 간단하게 이해하기 위한 포스팅
delegate는 위임하다, 맡기다의 의미이다.
누군가에게 일을 대신 시킨다. 즉 대리자가 있다는 뜻!
앱을 만들다 보면 텍스트필드를 자주 활용하게 되고, 뷰컨트롤러에서 관련 코드를 작성한 경험이 있을 것이다.
그런데 텍스트필드도 고유의 객체이고 뷰컨트롤러도 고유의 객체이다.
객체 간 의사소통, 연결을 위해 필요한 것이 바로 Delegate Pattern이라고 이해하면 쉽다.
(* 프로토콜을 사용해서 델리게이트 패턴을 구현한다.
프로토콜은 자격증으로 이해하면 좋다.
프로토콜을 채택하면 (즉 자격증을 취득하면) 관련 능력(속성, 메서드)이 생긴다. )
(* 스위프트는 프로토콜 지향 프로그래밍 언어로도 볼 수 있는데, 프로토콜을 활용하면 확장성이 매우 좋아지는 장점이 있다.
프로토콜도 타입이기 때문!)
일상생활에서 예를 들어 보자면..
퇴근하고 쇼파에 누워 넷플릭스를 보기 위해 리모컨을 집어 들었다.
전원 버튼을 누르면 티비가 켜질 것이다. -> 이것도 델리게이트 패턴으로 이해할 수 있다.
왜? 어떻게?
리모콘의 전원 버튼을 누른다 -> 티비의 전원이 켜진다.
이것을 한단계 더 뜯어서 보면
리모콘의 전원 버튼을 누른다 == 티비에게 전원을 키라고 말한다 -> 티비가 전원을 킨다.
즉 리모콘의 전원 버튼을 누르는 것은 티비가 전원을 키도록 시키는 것이다.
무슨 말인지 복잡하니 코드로 다시 한 번 살펴보자.
import UIKit
//원격 통신을 위한 프로토콜을 만든다.
//보통 ~Delegate로 네이밍을 한다.
//프로토콜 안에는 최소한의 요구사항(프로퍼티,메서드)만 작성하고 상세 구현은 해당 프로토콜을 채택한 객체에서 한다
protocol RemoteControlDelegate {
func turnOn()
func turnOff()
}
자 이렇게 원격 통신을 위한 RemoteControlDelegate 프로토콜을 만들었으니 리모컨을 만들어 보자
class RemoteControl {
// 리모컨 객체의 대리자를 설정하기 위한 변수를 만들어 준다.
// 대리자가 없는 경우를 고려하여 옵셔널로 선언한다.
var delegate: RemoteControlDelegate?
// 자 아래 코드가 중요하다!
// 리모컨에서 turnOn이라는 메서드르 실행하면
// 우선 delegate, 즉 대리자가 있는지 확인하고 (옵셔널로 선언되어 있으니)
// 대리자가 turnOn 메서드를 실행하도록 한다.
// delegate(대리자)는 위에 코드와 같이 RemoteControlDelegate 프로토콜을 채택해야하기에
// 당연히 필수 요구사항인 turnOn과 turnOff함수가 구현되어 있을 것이다.
func turnOn() {
delegate?.turnOn()
}
func turnOff() {
delegate?/turnOff()
}
}
자 이제 리모컨이 있고 해당 리모컨을 어떤 기기(객체)와 연결(객체간 의사소통) 시켜야 한다. (이 녀석이 대리자가 될 것이다.)
TV를 만들어 보자. 그런데 리모컨의 대리자가 되려면 조건이 있었다. 바로 RemoteControlDelegate 프로토콜을 채택해야 한다는 것!
이를 유의하고 코드를 작성해보자.
class TV: RemoteControlDelegate {
//우선 프로토콜의 최소요구 사항만 충족하고
//세부 구현은 간단하게 프린트문으로 해보자.
//이제 TV 인스턴스는 리모컨 인스턴스의 delegate에 할당 될 수 있을 것이다.
func turnOn() {
print("티비의 전원이 켜진다.")
}
func turnOff() {
print("티비의 전원이 꺼진다.")
}
이제 리모컨과 티비를 만들고 전원을 키고 꺼보자~
let remoteController = RemoteControl()
let appleTV = TV()
//리모컨의 대리자로(RemoteControl 클래스 인스턴스의 delegate 속성)
//애플티비(TV 클래스 인스턴스)를 할당해줬다.
remoteController.delegate = appleTV
//그러면 이제! 아래와 같은것이 가능하다.
remoteController.turnOn()
remoteController.turnOff()
//즉 리모컨의 전원버튼을 켜고 끄는 것인데, 이러면 티비의 전원이 켜지고 꺼지는 것이다.
//리모컨이 "티비야 전원 켜(turnOn 메서드 실행해), 티비야 전원 꺼(turnOff 메서드 실행해)
//라고 대신 일을 시키는 것이다 :)
위 코드에서 대리자를 따로 할당해주는 코드를 보였는데, 처음부터 대리자를 지정하고 객체를 찍어내는 방식도 있다.
에어컨을 만들며 알아보자
class AirConditioner: RemoteControlDelegate {
//생성자에서 remoteController 프로토콜을 채택한 객체를 remote에 할당하고
//remote의 delegate 속성에 self, 즉 AirCoditioner의 인스턴스 (airconditioner등)를 할당.
init(remote: remoteControl) {
remote.delegate = self
func turnOn() {
print("에어컨의 전원이 켜진다.")
}
func turnOff() {
print("에어컨의 전원이 꺼진다.")
}
}
//에어컨을 만들고 켜고 꺼보자.
let airconditioner = Airconditioner(remote: remoteController)
remote.turnOn() //에어컨의 전원이 켜진다.
remote.turnOff() //에어컨의 전원이 꺼진다.
위와 같이 작동하는 것을 알 수 있다.
여기서는 리모컨, 티비, 에어컨을 통해 예시를 들었지만
보통은 앱을 만들면서
텍스트필드와 같은 여러 객체들과
뷰컨트롤러 사이에서 델리게이트 패턴을 자주 이용한다.
텍스트필드가 리모컨이라면
뷰컨트롤러가 티비이고 에어컨인 것이다.
즉 텍스트필드에서 어떤 입력이나 행위를 뷰컨트롤러에 전달하고(시키고)
뷰컨트롤러가 해당 일(함수 실행 등)을 하는 형태인 것이다.
-뷰컨트롤러가 텍스트필드에서 발생한 상황에 대한 판단(Bool) 혹은 동작(메서드 실행)을 담당하는 것이다.
그래서 아래와 같은 형태를 자주 발견하게 될 것이다.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
@IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.delegate = self
}
//위에서 알 수 있듯이 뷰컨트롤러 관련 프로토콜을 채택하고
// delegate 속성에 self, 즉 뷰컨트롤러의 인스턴스를 할당하는 것이다.
//다시 쉽게 말하자면 텍스트필드에서 어떤 일이 일어나서
//뷰컨트롤러에 알려주면 뷰컨트롤러가 그 일들을 처리한다는 의미다 :)
사실 델리게이트 패턴은 많이 사용되고 기본적인 개념이지만, 처음에는 생소하고 조금 헷갈리기도 했다.
점차 많이 써보면서 나아지겠지만, 처음 개념을 스스로 복습해보기 위해서 위와 같이 간단한 코드들과 함께 정리해 보았다. 🙋🏻♂️
'UIKit > UIKit' 카테고리의 다른 글
Objective-C 코드 베이스 세팅 (0) | 2024.06.04 |
---|---|
init(frame:) & init(coder:) (0) | 2023.04.11 |
DequeueReusableCell ?? 테이블뷰와 셀 (0) | 2023.02.28 |
Could not insert new outlet connection (.xib) (0) | 2023.02.27 |
ViewController.swift 파일명을 마음대로 바꾼다면.. [Storyboard] Unknown class _xxx in Interface Builder file. (0) | 2023.02.17 |