[TIL] Delegate 패턴

박주하·2025년 6월 18일

Delegate 패턴


  • 일을 대신 수행해줄 객체(대리인, delegate)를 지정하고, 그 객체가 정해진 규칙(프로토콜)대로 대신 일을 처리하는 방식
  • 역방향 소통(하위 → 상위 컴포넌트)이나 이벤트 전달에 아주 유용
  • 즉, "일을 위임하고 위임받은 쪽이 처리"하는 구조

💡 학교 선생님과 학생 예

  • 학생(객체 A)이 질문을 하면, 선생님(Delegate, 객체 B)이 대신 대답해준다.
  • 즉, 학생은 "답하는 로직"을 몰라도, 선생님인 "대답해줄 사람"만 알고 있으면 된다!

구성요소

구성요소설명
Protocol어떤 일을 대신해줄지 정의 (약속)
Delegate일을 대신 해주는 객체 (보통 ViewController)
OwnerDelegate에게 일을 맡기는 객체 (보통 View)

구현 흐름


  1. 프로토콜 정의 (무엇을 위임할지 규칙 정하기)
  2. delegate 속성 만들기 (보통 weak으로 선언)
  3. delegate 호출하기 (이벤트 발생 시)
  4. delegate 채택해서 구현하기 (delegate 객체에서 실제 처리)

Step 1. 프로토콜 정의

protocol MyViewDelegate: AnyObject {
    func didTapButton()
}
  • AnyObject: 클래스 타입에서만 사용 가능하게
    weak 선언을 위해 필요

Step 2. View에서 delegate 속성 추가 + 호출

class MyView: UIView {
    weak var delegate: MyViewDelegate?

    let button = UIButton(type: .system)

    override init(frame: CGRect) {
        super.init(frame: frame)
        button.setTitle("클릭!", for: .normal)
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        addSubview(button)
        // ...
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    @objc func buttonTapped() {
        delegate?.didTapButton()
    }
}
  • delegate를 통해 이벤트를 외부로 전달!

Step 3. ViewController에서 delegate 채택 + 연결

class MyViewController: UIViewController, MyViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()

        let MyView = MyView()
        MyView.delegate = self  // delegate 연결
        view.addSubview(MyView)
        // ...
    }

    func didTapButton() {
        print("버튼 클릭!")
    }
}

View에서 버튼 클릭! → ViewController로 알려주기 (이벤트 처리)
View: 버튼 클릭 이벤트를 처리하지 않고, "대신 해줘!" 하고 delegate에게 전달
ViewController: 실제 동작(프린트, 화면 전환 등)을 구현


💡 왜 사용할까?

  • 재사용성: 로직을 분리하니까, 여러 곳에서 사용할 수 있음
  • 유연함: delegate만 바꾸면 행동도 바뀜
  • 낮은 결합도: View는 로직을 몰라도 됨. 그냥 delegate에게 시킴

메모리 관리 주의사항


// 🚨 retain cycle 예
class A {
    var delegate: B?   // 강한 참조
}

class B {
    var classA: A?      // 강한 참조
}
  • A와 B가 서로를 strong으로 참조 → 메모리에서 안 사라짐❗️
  • Delegate 패턴 사용 시, 잘못하면 앱이 메모리 누수에 빠져서 ViewController가 메모리에서 사라지지 않게 됨

delegate 안전하게 쓰는 방법

1. Protocol에 AnyObject 제약 추가

protocol MyViewDelegate: AnyObject {
    func didTapButton()
}
  • AnyObject: 클래스 타입만 채택 가능하게 함
  • AnyObject 제약을 붙여야 weak 사용 가능 → weak은 클래스 인스턴스에만 쓸 수 있기 때문

2. Delegate를 weak으로 선언

class MyView {
    weak var delegate: MyViewDelegate?
}
  • delegate는 참조만 하고, 소유 ❌
  • weak으로 선언해야 ViewController가 메모리에서 해제될 수 있음 (순환 참조 방지)

0개의 댓글