일정
- 알고리즘 스터디 & 데일리 스크럼(09:00 ~ 11:00)
- UIKit 개인 과제 도전
Delegate 패턴이란?
Delegate 패턴은 iOS에서 객체 간의 1:1 통신을 구현하기 위한 디자인 패턴입니다. 한 객체가 자신이 처리해야 할 작업이나 이벤트를 다른 객체에게 위임(delegate)하여 처리하도록 하는 방식으로 동작합니다. 주로 이벤트 처리나 콜백(callback)을 구현할 때 사용되며, 대표적인 예로는 UITableView에서 셀 선택 이벤트를 처리하는 경우가 있습니다.
- 핵심 아이디어: 객체 A가 특정 작업을 직접 처리하지 않고, 객체 B에게 "이 일을 대신 처리해줘"라고 요청하는 구조입니다.
- 특징: 객체 간의 의존성을 줄이고, 책임을 명확히 분리합니다.
Delegate 패턴은 어떤 상황에서 사용되나요?
Delegate 패턴은 아래와 같은 상황에서 유용하게 활용됩니다:
- 객체 간 느슨한 결합(Loose Coupling)이 필요할 때
- Delegate를 사용하면 객체가 서로 직접 의존하지 않고, 중간에 프로토콜을 통해 통신하므로 결합도가 낮아집니다.
- 특정 이벤트에 대한 응답이 필요할 때
- 예: 버튼 클릭, 스크롤 이벤트, 텍스트 입력 완료 등 사용자의 상호작용에 반응해야 할 때.
- 재사용 가능한 컴포넌트를 만들 때
- 컴포넌트(예: 커스텀 버튼)의 동작을 외부 객체가 정의하도록 위임하여, 다양한 상황에서 유연하게 사용할 수 있습니다.
Delegate 패턴과 Notification, KVO의 차이점
Delegate 패턴 외에도 iOS에서는 Notification과 KVO(Key-Value Observing)라는 통신 방식이 있습니다. 이들의 차이점을 아래 표로 정리했습니다.
| 특성 | Delegate 패턴 | Notification | KVO |
|---|
| 통신 방식 | 1:1 (한 객체가 한 객체에 위임) | 1:多 (한 객체가 여러 객체에 알림) | 프로퍼티 변화 감지 |
| 결합도 | 프로토콜로 정의, 중간 정도 | 매우 느슨함 (발신자-수신자 독립) | 객체 간 강한 의존 가능성 |
| 타입 안정성 | 강함 (Protocol로 명확히 정의) | 약함 (문자열 기반) | 약함 (프로퍼티 이름 기반) |
| 실행 시점 | 동기적 (즉시 호출) | 비동기적 가능 | 프로퍼티 변경 시 자동 알림 |
| 사용 예시 | UITableViewDelegate | 앱 전역 이벤트 (예: 화면 회전) | 데이터 모델 값 변경 감지 |
1. Delegate 패턴
- 특징: 한 객체가 다른 객체에게 작업을 위임하며, 통신이 명확하고 타입 안전합니다.
- 장점: 코드 가독성이 높고, 디버깅이 쉬움.
2. Notification
- 특징: NotificationCenter를 통해 이벤트를 브로드캐스트하며, 여러 객체가 이를 수신할 수 있습니다.
- 장점: 발신자와 수신자가 서로 알 필요 없어 유연함.
- 단점: 문자열 기반으로 타입 안정성이 낮음.
3. KVO (Key-Value Observing)
- 특징: 객체의 특정 프로퍼티 값 변화를 감지하고 알림을 받습니다.
- 장점: 프로퍼티 변화에 자동 반응 가능.
- 단점: 설정이 복잡하고, 메모리 누수 위험이 있음.
프로토콜을 활용한 Delegate 패턴 구현 방법
Delegate 패턴은 주로 Protocol을 사용해 구현됩니다. 아래는 구현 과정과 예제 코드입니다.
구현 단계
- Protocol 정의: Delegate가 수행해야 할 메서드를 정의합니다.
- Delegate 프로퍼티 추가: 작업을 위임할 객체에 delegate 프로퍼티를 선언합니다 (보통 weak으로 메모리 순환 참조 방지).
- Delegate 메서드 호출: 이벤트 발생 시 delegate의 메서드를 호출합니다.
- Protocol 채택: Delegate 역할을 할 객체가 프로토콜을 채택하고 메서드를 구현합니다.
예시 코드
1. Protocol 정의
protocol ButtonDelegate: AnyObject {
func buttonDidTap(_ button: CustomButton)
}
- AnyObject를 추가해 클래스만 프로토콜을 채택하도록 제한합니다.
2. Delegate를 사용하는 클래스
class CustomButton {
weak var delegate: ButtonDelegate?
func tap() {
*
delegate?.buttonDidTap(self)
}
}
- weak 키워드로 순환 참조를 방지합니다.
- ?로 옵셔널 체이닝을 사용해 delegate가 nil일 경우 안전하게 처리합니다.
3. Delegate를 채택하는 클래스
class ViewController: UIViewController, ButtonDelegate {
let button = CustomButton()
override func viewDidLoad() {
super.viewDidLoad()
button.delegate = self *
}
func buttonDidTap(_ button: CustomButton) {
print("Button tapped!")
}
}
- ViewController가 ButtonDelegate를 채택하고, 버튼 탭 이벤트를 처리합니다.
동작 흐름
- CustomButton의 tap() 메서드가 호출되면,
- delegate?.buttonDidTap(self)를 통해 ViewController의 buttonDidTap 메서드가 실행됩니다.
- 콘솔에 "Button tapped!"가 출력됩니다.