[참고: 이 글은 iOS 개발에 대한 내용이며, Swift 언어를 기반으로 작성되었습니다.]
iOS 애플리케이션을 개발할 때, Delegate 패턴과 Protocol은 정말 자주 사용되는 매우 중요한 디자인 패턴입니다.
iOS 개발을 하다 보면, 예를 들어 UICollectionView를 만들 때, 한 번쯤 collectionView.delegate = self와 같은 코드를 써보셨을 겁니다. 여기서 delegate가 뭘까요?
사전적 의미는 '위임/대리'이라는데, 대체 무엇을 위임하는 걸까요?
결론부터 말하자면, 실제 구현을 위임합니다. 설계할 때는 구현해야 하는 것들만 알려주고, 실제 구현은 사용하는 쪽에서 하게 만드는 패턴입니다.
이번 글에서는 Delegate 패턴과 Protocol에 대해 자세히 알아보도록 하겠습니다.
Delegate 패턴
Delegate 패턴은 일종의 협력 관계를 만들어주는 디자인 패턴입니다. 여기에는 두 가지 핵심 역할이 있습니다.
- Delegate (대리자): 메인 객체가 일부 작업을 다른 객체에게 "대리(delegate)"하도록 합니다. 메인 객체는 대리 객체의 메서드를 호출하거나 이벤트를 전달하여 작업을 수행합니다.
- Delegate 객체: Delegate 객체는 Delegate Protocol(또는 인터페이스)을 채택하여 필요한 메서드를 구현합니다. 이렇게 구현된 메서드는 메인 객체가 요청할 때 실행됩니다.
이해를 돕기 위해, 일상 생활에서 쉽게 볼 수 있는 상황을 예시로 들어 설명해보겠습니다.
상상해보세요, 당신이 학교 축제를 준비하고 있습니다.
학생들은 다양한 부스를 운영하며, 각 부스에는 책임자가 있습니다.
축제의 주최자인 당신은 각 부스에서 이벤트가 발생했을 때 적절한 조치를 취해야 합니다.
이때 Delegate 패턴을 사용하여 상호 작용을 구현할 수 있습니다.
1. 당신(축제 주최자)은 "BoothDelegate"라는 프로토콜을 정의합니다. 이 프로토콜은 부스에서 발생한 이벤트에 대응하는 메서드를 요구할 수 있습니다. (프로토콜이 무엇인지는 아래에서 자세히 설명하겠습니다. 간단히 설명서라고 이해해주세요.)
protocol BoothDelegate {
func boothDidReceiveVisitor(_ booth: Booth)
func boothDidRunOutOfSupplies(_ booth: Booth)
// ...
}
2. 각 부스는 BoothDelegate 프로토콜을 채택하여 메서드를 구현합니다:
class Booth {
var delegate: BoothDelegate?
// ...
func receiveVisitor() {
// 방문자가 들어왔을 때 호출
delegate?.boothDidReceiveVisitor(self)
}
func runOutOfSupplies() {
// 물자가 다 떨어졌을 때 호출
delegate?.boothDidRunOutOfSupplies(self)
}
// ...
}
3. 축제 주최자는 각 부스를 생성하고, 자신을 BoothDelegate로 설정합니다:
class FestivalOrganizer {
let boothA = Booth()
let boothB = Booth()
boothA.delegate = self
boothB.delegate = self
}
4. 부스에서 이벤트가 발생하면 Delegate 객체에게 알리고 조치를 취할 수 있습니다:
extension FestivalOrganizer: BoothDelegate {
func boothDidReceiveVisitor(_ booth: Booth) {
// 방문자가 들어왔을 때 조치
// ...
}
func boothDidRunOutOfSupplies(_ booth: Booth) {
// 물자가 다 떨어졌을 때 조치
// ...
}
}
5. 이제 각 부스에서 이벤트가 발생하면 Delegate 객체에게 알리고, Delegate 객체는 해당 이벤트에 대한 조치를 취합니다. 이를 통해 축제 주최자는 각 부스에서 발생하는 상황에 신속하게 대응할 수 있습니다.
위의 예시를 통해, Delegate 패턴이 어떻게 객체 간의 협력 관계를 형성하고 상호 작용을 가능하게 하는지 이해할 수 있을 것입니다.
iOS에서 Delegate 패턴은 주로 View Controller에서 많이 사용됩니다. 우리는 주로 ViewController에 TableView를 만들어 넣죠.
delegate를 활용해 테이블 뷰의 행을 터치하거나 선택했을 때, View Controller에게 이벤트를 전달하고 처리하도록 할 수 있습니다.
또, 객체 간 데이터를 전달하는 데에 사용하기도 합니다.
Protocol
위에서 자세한 설명도 없이 Protocol이라는 친구를 사용했는데요,
Protocol은 객체 간의 상호 작용을 정의하기 위한 인터페이스입니다.
프로토콜은 메서드, 프로퍼티 및 다른 요구사항을 명시하여 클래스, 구조체 또는 열거형이 특정 동작을 구현하도록 강제합니다.
프로토콜을 사용하면 다형성(polymorphism)을 구현하고, 코드의 재사용성과 유연성을 높일 수 있습니다.
프로토콜은 다음과 같은 방식으로 선언됩니다.
protocol SomeProtocol {
// 메서드 선언
func someMethod()
// 프로퍼티 선언
var someProperty: Int { get set }
}
프로토콜은 구현을 제공하지 않으며, 단지 요구사항을 정의합니다.
프로토콜을 채택한 클래스, 구조체 또는 열거형은 프로토콜에 정의된 요구사항을 모두 구현해야 합니다.
해당 프로토콜을 채택하게 되면 어떤 것을 구현해야 하는지 알려주는 역할을 할 수 있습니다.
프로토콜은 다중 상속과 유사한 효과를 낼 수 있습니다.
하나의 클래스는 여러 프로토콜을 동시에 채택할 수 있으며, 이를 통해 다양한 동작을 수행할 수 있습니다.
Delegate 패턴과 Protocol은 자주 함께 사용됩니다.
Delegate 객체는 보통 프로토콜을 채택하여 정의되며, 해당 프로토콜은 Delegate 객체가 수행해야 할 작업을 명시합니다. 이를 통해 Delegate 패턴을 통해 객체 간의 상호 작용을 정의하고, 프로토콜을 통해 요구사항을 명확히 할 수 있습니다.
실제 Swift 프로젝트에서 활용 예시
메모 애플리케이션을 개발하고 있는 상황을 가정해보겠습니다.
사용자가 새로운 메모를 작성할 때, 키보드의 리턴(Return) 버튼을 눌렀을 때 작성한 메모를 저장하고 화면을 닫아야 합니다.
이때 Delegate 패턴과 Protocol을 사용하여 구현할 수 있습니다.
1. 먼저, 저장 및 닫기 작업을 위한 프로토콜을 정의합니다. 예를 들어, "MemoViewControllerDelegate"라는 프로토콜을 만들어봅시다.
protocol MemoViewControllerDelegate: AnyObject {
func didSaveMemo(_ memo: String)
func didCloseMemo()
}
2. 메모 작성을 위한 View Controller를 구현하고, Delegate 객체를 참조하는 속성을 선언합니다.
class MemoViewController: UIViewController {
weak var delegate: MemoViewControllerDelegate?
// ...
}
3. 키보드의 리턴(Return) 버튼을 누를 때 Delegate 객체에게 작성한 메모를 전달하고 화면을 닫도록 구현합니다.
extension MemoViewController: UITextFieldDelegate {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
guard let memo = textField.text else { return true }
delegate?.didSaveMemo(memo)
delegate?.didCloseMemo()
return true
}
}
4. 이제 다른 View Controller에서 MemoViewController를 사용하고, 해당 Delegate 프로토콜을 채택하고 메서드를 구현합니다.
class MainViewController: UIViewController, MemoViewControllerDelegate {
// ...
func openMemoViewController() {
let memoVC = MemoViewController()
memoVC.delegate = self
present(memoVC, animated: true, completion: nil)
}
func didSaveMemo(_ memo: String) {
// 메모 저장 작업 수행 // ...
}
func didCloseMemo() {
dismiss(animated: true, completion: nil)
}
// ...
}
5. MainViewController에서 MemoViewController를 열기 위해 openMemoViewController() 메서드를 호출하면, MemoViewController가 표시되고 사용자가 메모를 작성한 후 리턴(Return) 버튼을 누르면 저장 및 화면 닫기 작업이 수행됩니다. 이를 통해 MainViewController에서 메모 작성 상황을 적절하게 처리할 수 있습니다.
이렇게, Delegate 패턴과 Protocol이 무엇인지, 그리고 실제 프로젝트에서 어떻게 사용되는지 알아보았습니다.
'iOS > 디자인패턴' 카테고리의 다른 글
[iOS 디자인패턴] Factory 패턴에 대해 (1) | 2023.05.25 |
---|---|
[iOS 디자인패턴] Singleton 패턴에 대해 (0) | 2023.05.11 |
[iOS 디자인패턴] Observer 패턴과 Notification 설계 (0) | 2023.05.11 |