Delegate: 개념, 위임 패턴 및 iOS 프로토콜

저자: IT Sectr 게시일: 2026-02-17 읽는 시간: 9 분

Delegate는 한 객체가 작업 실행을 다른 객체에 위임하는 디자인 패턴입니다. iOS에서는 Swift 프로토콜과 @protocol Objective-C를 통해 패턴이 구현됩니다. 위임은 Cocoa Touch의 기본 패턴 중 하나이며 UITableViewDelegate, UITextFieldDelegate 및 수백 개의 다른 Apple API에서 사용됩니다. Apple Developer Documentation(2025)에 따르면 UIKit 시스템 클래스의 약 70%가 상속 없이 동작 사용자 정의를 위해 델리게이트를 사용합니다.

핵심 사항

  • Delegate — 객체 A가 프로토콜 또는 인터페이스를 통해 객체 B에 작업 실행을 위임하는 패턴
  • Swift 프로토콜은 델리게이트가 구현할 수 있거나 해야 하는 메서드 집합을 정의합니다
  • Weak 참조는 델리게이트의 retain cycle 및 메모리 누수를 방지하기 위해 필수입니다
  • UITableViewDelegate — iOS에서 20개 이상의 선택적 메서드를 가진 가장 일반적인 델리게이트 예제
  • Observer와의 차이점: 델리게이트는 1:1 관계인 반면, 알림은 1:N 원리로 작동합니다

Delegate 패턴이란?

Delegate는 객체가 자신의 책임 일부를 다른 객체에 위임할 수 있게 하는 행동 디자인 패턴입니다. 자식 클래스가 부모 메서드를 재정의하는 상속과 달리, 델리게이트는 컴포지션을 사용합니다. 소유자 객체는 델리게이트에 대한 참조를 유지하고 특정 지점에서 해당 메서드를 호출합니다.

델리게이트는 프로토콜을 정의합니다 — 델리게이트가 구현할 수 있는 메서드 집합입니다. 메서드는 필수(required)와 선택적(optional)으로 나뉩니다. Swift에서는 선택적 프로토콜 메서드가 @objc optional 키워드로 표시됩니다.

델리게이트 작동 방식

객체 A(소유자)는 delegate 속성 — 객체 B(델리게이트)에 대한 약한 참조(weak reference)를 포함합니다. 이벤트가 발생하면 A는 B가 해당 프로토콜 메서드를 구현하는지 확인하고 호출합니다. 약한 참조는 필수입니다. 이것이 없으면 소유자가 강한 참조로 유지하기 때문에 델리게이트가 메모리에서 해제될 수 없습니다.

Swift
protocol LoaderDelegate: AnyObject {
    func loaderDidStart(_ loader: DataLoader)
    func loader(_ loader: DataLoader, didLoad data: Data)
    func loader(_ loader: DataLoader, didFailWith error: Error)
}

class DataLoader {
    weak var delegate: LoaderDelegate?
    
    func start() {
        delegate?.loaderDidStart(self)
        // 비동기 로딩
    }
}

DataLoader 클래스는 LoaderDelegate 프로토콜을 정의하고 로딩 라이프사이클의 주요 지점에서 델리게이트 메서드를 호출합니다. AnyObject는 프로토콜이 클래스에 의해서만 구현될 수 있음을 보장합니다 — 이는 약한 참조에 필요합니다.

Swift에서 Delegate는 어떻게 작동하나요?

Swift의 델리게이트 구현은 세 단계로 구성됩니다: 프로토콜 선언, 소유자에 약한 delegate 속성 생성, 델리게이트 클래스에서 프로토콜 구현입니다. 유효성 검사가 있는 사용자 정의 UITextField 예제를 살펴보겠습니다.

Swift
protocol ValidatorDelegate: AnyObject {
    func validate(_ input: String) -> Bool
    func validatorDidFail(_ input: String)
}

class ValidatedTextField: UITextField {
    weak var validator: ValidatorDelegate?
    
    override func textDidChange() {
        guard let text = self.text else { return }
        if validator?.validate(text) == false {
            validator?.validatorDidFail(text)
            self.layer.borderColor = UIColor.red.cgColor
        }
    }
}

class LoginViewController: UIViewController, ValidatorDelegate {
    let textField = ValidatedTextField()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        textField.validator = self
    }
    
    func validate(_ input: String) -> Bool {
        return input.count >= 6
    }
    
    func validatorDidFail(_ input: String) {
        print("유효성 검사 실패: 입력이 너무 짧습니다")
    }
}

LoginViewController는 ValidatorDelegate 프로토콜을 구현하고 자신을 textField의 델리게이트로 설정합니다. 텍스트가 변경될 때마다 ValidatedTextField는 validate(_:)를 호출하고, 유효성 검사가 실패하면 validatorDidFail(_:)을 호출합니다. 컨트롤러는 View와 유효성 검사 로직 사이의 중개자 역할을 합니다.

Delegate vs NotificationCenter vs Closure

델리게이트, 알림, 클로저 중 선택은 수신자 수와 컴포넌트 결합도에 따라 달라집니다. 각 메커니즘은 객체 간 통신 문제를 해결하지만, 서로 다른 트레이드오프가 있습니다.

특성DelegateNotificationCenterClosure
통신 유형1:11:N1:1
결합도약함 (프로토콜을 통해)매우 약함 (문자열 키)중간 (컨텍스트 캡처)
타입 안전성완전없음 (Any?)완전
Retain cycle 위험없음 (weak)없음있음 (self 캡처)
사용 시기여러 메서드가 있는 복잡한 콜백많은 사람이 관심 있는 이벤트1-2 콜백이 있는 간단한 클로저

Delegate는 일련의 관련 이벤트를 단일 수신자에게 전달해야 할 때 최적입니다. NotificationCenter는 브로드캐스트 알림에 더 좋습니다. Closure는 URLSession의 완료 핸들러와 같은 간단한 비동기 작업에 사용합니다.

Objective-C에서 Delegate 구현

Objective-C는 델리게이트 선언을 위해 @protocol과 @optional을 사용합니다. Swift와 달리 모든 프로토콜 메서드는 기본적으로 선택적입니다. 주요 차이점은 메서드가 구현되지 않았을 수 있으므로 델리게이트에 메시지를 보내기 전에 respondsToSelector:를 호출한다는 점입니다.

Objective-C
@protocol ImageCacheDelegate 
@optional
- (void)cacheDidStartDownload: (ImageCache *)cache;
- (void)cache: (ImageCache *)cache didCacheImage: (UIImage *)image;
@required
- (void)cache: (ImageCache *)cache didFailWithError: (NSError *)error;
@end

@interface ImageCache : NSObject
@property (nonatomic, weak) id<ImageCacheDelegate> delegate;
- (void)downloadImageAtURL: (NSURL *)url;
@end

@implementation ImageCache
- (void)downloadImageAtURL: (NSURL *)url {
    if ([self.delegate respondsToSelector:@selector(cacheDidStartDownload:)]) {
        [self.delegate cacheDidStartDownload:self];
    }
    // 비동기 이미지 로딩
}
@end

Objective-C의 주요 차이점: 선택적 메서드를 호출하기 전에 respondsToSelector: 확인이 필요합니다. Swift에서는 선택적 프로토콜 메서드가 이 확인을 불필요하게 만듭니다 — 옵셔널 체이닝(?.)이 구현 부재를 자동으로 처리합니다.

델리게이트 작업 시 일반적인 실수

델리게이트 사용 실수는 메모리 누수, 앱 충돌 및 명확하지 않은 버그로 이어집니다. 다섯 가지 가장 일반적인 문제를 살펴보겠습니다.

델리게이트에 대한 강한 참조

Retain cycle은 가장 일반적인 실수입니다. delegate 속성이 strong으로 선언되고 델리게이트가 소유자 객체를 소유하는 경우, 보유 주기가 형성됩니다. 두 객체 모두 메모리에서 해제되지 않습니다. 해결책: Swift에서는 항상 weak var로, Objective-C에서는 @property (weak)로 델리게이트를 선언하세요.

Dealloc 후 델리게이트가 null이 아님

소유자 객체가 델리게이트보다 오래 살고 참조가 남아 있으면, 델리게이트 메서드를 호출할 때 EXC_BAD_ACCESS가 발생합니다. 약한 참조는 이 문제를 자동으로 해결합니다: 델리게이트가 해제된 후 속성은 nil이 됩니다. 그러나 멀티스레드 시나리오에서는 메인 스레드에서 델리게이트를 추가로 확인해야 합니다.

프로토콜의 과도한 메서드 수

20개 이상의 메서드가 있는 프로토콜은 인터페이스 분리 원칙(ISP)을 위반합니다. UITableViewDelegate에는 약 30개의 선택적 메서드가 있습니다 — 이것은 역사적 예외입니다. 자신의 프로토콜에서는 책임을 여러 개의 작은 프로토콜로 나누는 것이 좋으며, 각각 고유한 역할을 가집니다.

iOS 시스템 API의 Delegate 예제

Apple의 시스템 API는 Delegate 패턴을 적극적으로 사용합니다. 모든 iOS 애플리케이션에 등장하는 UIKit의 세 가지 주요 예제를 살펴보겠습니다.

API프로토콜주요 메서드
UITableViewUITableViewDelegatedidSelectRowAt, heightForRowAt, willDisplay
UITextFieldUITextFieldDelegateshouldChangeCharactersIn, didBeginEditing, shouldReturn
URLSessionURLSessionDelegatedidReceiveChallenge, didCompleteWithError, didBecomeInvalidWithError

이 프로토콜들은 각각 다른 동작 측면을 구현합니다: UITableViewDelegate는 모양과 터치 응답을 관리하고, UITextFieldDelegate는 텍스트 입력을 제어하며, URLSessionDelegate는 네트워크 이벤트를 처리합니다. 이는 패턴의 유연성을 보여줍니다: 델리게이트는 모든 책임 영역에 적응할 수 있습니다.

자주 묻는 질문

iOS에서 delegate와 dataSource의 차이점은 무엇인가요?

Delegate는 동작과 모양을 관리합니다(셀 높이, 터치 응답). DataSource는 데이터를 제공합니다(행 수, 셀 내용). UITableViewDelegate와 UITableViewDataSource에서는 — 프레젠테이션과 데이터에 대한 책임을 나누는 두 개의 별도 프로토콜입니다.

왜 delegate가 weak로 선언되나요?

약한 참조는 retain cycle을 방지합니다. 소유자(예: UITableView)는 델리게이트에 대한 참조를 weak로만 유지합니다. 델리게이트(UIViewController)가 테이블을 소유하는 경우, 델리게이트에 대한 강한 참조는 순환을 생성합니다: ViewController → UITableView → Delegate(ViewController). Weak가 이 순환을 끊습니다.

SwiftUI에서 delegate를 사용할 수 있나요?

SwiftUI에서는 Delegate 패턴이 덜 자주 사용됩니다 — @Binding, @State 및 클로저로 대체됩니다. 그러나 델리게이트는 UIViewRepresentable을 통한 UIKit 통합을 위해 여전히 사용됩니다. 예를 들어, MKMapViewDelegate와 WKUIDelegate는 UIKit 컴포넌트를 SwiftUI로 래핑할 때 여전히 관련이 있습니다.

Swift 프로토콜의 @objc optional이란 무엇인가요?

@objc optional은 Swift 프로토콜에서 선택적 메서드를 선언할 수 있게 합니다. 이는 Objective-C 런타임과의 호환성 메커니즘입니다. @objc가 없으면 모든 Swift 프로토콜 메서드는 기본적으로 필수입니다. Optional은 델리게이트가 필요한 메서드만 구현할 수 있는 UIKit 프로토콜에서 사용됩니다.

하나의 객체는 몇 개의 델리게이트를 가질 수 있나요?

하나의 객체는 각 delegate 속성에 대해 하나의 델리게이트만 가질 수 있습니다. 여러 객체에 알려야 하는 경우, 멀티캐스트 델리게이트, 델리게이트 배열 또는 NotificationCenter를 사용하세요. Delegate 패턴은 원래 1:1 관계로 설계되었습니다.

요약

  • Delegate — 프로토콜을 통해 한 객체에서 다른 객체로 작업 실행을 전송하는 행동 디자인 패턴
  • Swift 프로토콜은 메서드 집합을 선언합니다; 선택적 메서드는 @objc optional로 표시되고, 필수 메서드는 항상 구현됩니다
  • 약한 참조는 델리게이트의 retain cycle 및 메모리 누수를 방지하기 위해 필수입니다
  • UITableViewDelegate는 가장 잘 알려진 예제입니다; 그 메서드는 셀 높이, 선택 및 표시를 관리합니다
  • Delegate는 1:1 대 1:N 관계 및 완전한 타입 안전성에서 NotificationCenter와 다릅니다
  • Objective-C는 선택적 메서드에 대해 respondsToSelector: 확인이 필요하지만, Swift는 자동으로 처리합니다
  • 권장 사항: 여러 관련 메서드가 있는 복잡한 콜백에는 delegate를, 간단한 클로저에는 closure를 사용하세요

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기