Delegate Pattern 왜 사용하는 걸까?

Delegate Pattern 왜 사용하는 걸까?

과제나 강의를 들어도 꼭 나오는 델리게이트 패턴은 정말 많이 쓰이는 것 같지만 정작 왜 사용하는지 모르고 사용하는 것 같은 느낌이 들어 개념을 한번 잡아보려고 한다.

Delegate Pattern 이란

객체가 자신의 책임을 다른 객체에게 위임하는 디자인 패턴

테이블뷰는 셀을 탭했을 때 어떤 행동을 할지에 대한 책임을 뷰컨트롤러에게 UITableViewDelegate 를 사용하여 위임한다.

테이블 뷰 외에도 콜렉션뷰 , 텍스트필드 등 많은 UI 요소들이 델리게이트 패턴을 사용해 다른 객체에게 책임을 위임하고 있다.

왜 위임을 하는 걸까?

UI요소에서의 Delegate Pattern

셀을 탭하면 테이블 뷰는 탭 이벤트를 받는다. 테이블뷰가 탭 이벤트를 받으면 델리게이트의 didSelectRowAt 메소드를 실행 시킨다. 이벤트를 받았을 때 어떤 행동을 할건지 델리게이트에게 위임 하는 것이다.

보통은 뷰컨트롤러에 tableView.delegate = self 와 같은 코드를 작성해 뷰컨트롤러의 인스턴스 자신을 위임자로 설정하고 didSelectRowAt 메소드에 셀을 탭했을 때 어떤 행동을 할지 정의한다.

테이블뷰가 알아서 다 하면 될 것 같은데 굳이 객체에게 위임하는 이유는 무엇일까?

우리가 UITableView 내부 코드를 수정할 수 없기 때문

셀이 탭되었을 때 어떤 행동을 할지 상황에 따라 다르기 때문에 개발자가 코드를 작성해야 한다. 하지만 우리는 테이블 뷰 안의 코드를 수정 할 수 없기 때문에 다른 객체에서 코드를 작성해준 뒤 테이블 뷰가 그 객체의 코드를 호출해줘야 한다.

이때 테이블뷰와 객체를 연결해주는 방식이 Delegate Pattern 이다.

예시를 한번 보자

protocol StudentDelegate: AnyObject {
    func receiveMessage(message: String)
}
  • Protocol 정의 : 먼저, 학생이 받을 수 있는 메시지를 정의하는 프로토콜을 작성한다.
class Student {
    weak var delegate: StudentDelegate?
    
    func sendMessageToTeacher(message: String) {
        delegate?.receiveMessage(message: message)
    }
}
  • Student 클래스 작성 : 학생 클래스는 위에서 정의한 프로토콜을 채택하여 메시지를 받을 준비를 한다.
class Teacher: StudentDelegate {
    let student = Student()
    
    init() {
        student.delegate = self
    }
    
    func sendMessageToStudent(message: String) {
        student.sendMessageToTeacher(message: message)
    }
    
    func receiveMessage(message: String) {
        print("학생이 메시지를 받았습니다: \\(message)")
    }
}
  • Teacher 클래스 작성 : 선생님 클래스는 학생에게 메시지를 전달하고 학생으로 부터 응답을 받는다.
let teacher = Teacher()
teacher.sendMessageToStudent(message: "금요일에 모의고사가 있습니다.")
  • 선생님이 학생에게 메세지를 보내고 학생이 그에 대한 응답을 받는다.

이렇게 프로토콜을 사용해 델리게이트 패턴을 구현하면 훨씬 더 유연하고 재사용 가능한 코드를 만들 수 있다.

실제 사용할 때 두개의 뷰컨트롤러를 연결하는 예시를 한번 보자

protocol DataTransferDelegate: AnyObject {
    func sendData(data: Any)
}

class ViewControllerB: UIViewController {
    weak var delegate: DataTransferDelegate?
    
    // 데이터를 처리하는 메서드
    func processData(data: Any) {
        // 받은 데이터를 처리하는 코드
    }
}

 

데이터를 받는 ViewControllerB 를 작성하여 프로토콜을 채택한다.

class ViewControllerA: UIViewController, DataTransferDelegate {
    let viewControllerB = ViewControllerB()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        viewControllerB.delegate = self
    }
    
    // DataTransferDelegate 프로토콜 메서드 구현
    func sendData(data: Any) {
        // ViewControllerB로부터 전달받은 데이터를 처리하는 코드
        viewControllerB.processData(data: data)
    }
    
    // 데이터를 전달하는 메서드
    func sendDataToViewControllerB() {
        let dataToSend: Any = // 전달할 데이터
        viewControllerB.delegate?.sendData(data: dataToSend)
    }
}

class ViewControllerB: UIViewController {
    weak var delegate: DataTransferDelegate?
    
    // 데이터를 처리하는 메서드
    func processData(data: Any) {
        // 받은 데이터를 처리하는 코드
    }
}

ViewControllerA에서 ViewControllerB로 데이터 전달하기 위해  ViewControllerB 의 delegate 에 자신을 할당하고 데이터를 전달 한다.

class ViewControllerB: UIViewController {
    weak var delegate: DataTransferDelegate?
    
    // 데이터를 처리하는 메서드
    func processData(data: Any) {
        // 받은 데이터를 처리하는 코드
    }
}

ViewControllerB는 전달받은 데이터를 처리하는 메서드를 구현 한다.

 

이렇게 하면 ViewControllerA에서 ViewControllerB로 데이터를 전달하고, ViewControllerB는 전달받은 데이터를 처리할 수 있다. 이때 ViewControllerB는 ViewControllerA에게 의존하지 않고, 데이터 전달에만 집중할 수 있으며, ViewControllerA는 ViewControllerB에게 데이터를 전달하고 결과를 받는 역할을 수행한다.

 

* 출처 : https://velog.io/@nala/iOS-Delegate-%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EC%9E%90