iOS/Swift

델리게이트 패턴이란?

밤새는 탐험가89 2024. 7. 8. 21:41

Delegate Pattern 이란?

 

"객체가 자신의 책임을 다른 객체에게 위임(delegate)하는 디자인 패턴"을 의미한다.

 

예를 들어, 테이블뷰 또는 컬렉션 뷰를 사용할 때 어떤 액션을 취할지에 대한 책임을 

뷰컨트롤러에게 UITableViewDelegate 또는 UICollectionViewDelegate를 사용하여 위임한다. 

 

그럼 왜 굳이 "위임"이라는 것을 사용하는 것일까?

 

애플이 미리 구현해 놓은 UITableView 또는 UICollectionView 등의

내부 코드는 비공개이기 때문에 개발자가 이를 알 수 없고, 그렇기에 수정할 수도 없다. 

 

위에서 예를 든 테이블의 셀을 탭했을때, 상황에 맞는 리액션을 개발자가 코드로 작성해야 한다. 

하지만 개발자는 애플이 구현해 놓은 내부 코드를 수정할 수 없다. 

따라서 다른 객체에서 개발자가 구현할 코드를 작성한 뒤에 

테이블뷰가 그 객체를 호출하는 방식을 사용한다. 

 

즉, 테이블뷰와 객체를 연결하는 방식이 바로 Delegate Pattern 이다. 

 

Delegate Pattern의 핵심은 두 객체를 연결하는 것이다. 

 

이벤트를 받는 객체 (예: UITableView)가 이벤트를 받아 

어떤 리액션을 취할지를 delegate (예: ViewController)에게 위임한다.

 

정리하면 delegate는 어떤 객체가 이벤트를 만났을 때, 그 객체를 대신하는 것을 말하고, 

delegating 객체는 이벤트를 받고 처리하는 responder 객체이다. 

 

 

실제로 앱에서는 어떤 방식으로 사용되나?

 

Delegate Pattern을 가장 많이 사용하는 경우는 두 개의 뷰컨트롤러 사이에 데이터를 전달할 때다.

 

예를 들어 사용자 프로필 수정창에서 사용자 정보를 수정하고 확인 버튼을 누르면

이전 화면으로 돌아가고, 이 때 입력받은 정보들을 보여줘야하는 경우에 사용한다. 

 

 

그럼 어떻게 사용하나?

ProfileViewController 화면이 밑에 있는 상태에서 그 위에 EditProfileViewController 화면이 올라오고, 

EditProfileViewController 에서 수정한 값을 ProfileViewController로 전달하고, 

EditProfileViewController 화면은 해제된다. 

 

1. 먼저 프로토콜을 생성한다. 

 

별도의 스위프트 파일을 생성하여 그 파일 내에 프로토콜을 생성한다. 

 

changeNameDelegate 라는 요리법을 가이드라인만 잡는다.

 

protocol changeNameDelegate {
    
    func changeName(name: String)
    
}

 

 

2. ProfileViewController 클래스 내에 위에 생성한 프로토콜을 채택한다.

ProfileViewController 가 바로 요리사라고 생각해보자 

class ProfileViewController: UIViewController, changeNameDelegate {
	...
}

 

 

3. ProfileViewController 클래스 내에 프로토콜을 채택할 경우, 

프로토콜 내의 함수를 구체적으로 구현한다.

 

 

changeName은 name이라는 파라미터를 profileView 내의 nameTextField의 값에 대입한다. 

 

ProfileViewController가 요리사 이기 때문에 요리법에 대해 제대로 알 필요가 있다. 

 

class ProfileViewController: UIViewController, changeNameDelegate {
    
    func changeName(name: String) {
        profileView.nameTextField.text = name
    }
    
    var profileView = ProfileView()
    ...
}

 

 

4. EditProfileViewController 파일 내에 코드를 구현한다. 

 

 4.1 delegate 변수를 선언한다. 

 

EditProfileViewController에 delegate라는 주문서를 생성한다. 

 

var delegate: changeNameDelegate?

 

 

 4.2 didTapButton() 메서드 내에서 delegate를 통해 프로토콜 내의 함수에 접근한다. 

 

changedName이라는 변수에 nameTextField 내의 입력한 값을 할당한다. 

그리고 delegate.changeName에 changeName을 전달한다. 

dismiss를 통해 EditProfileViewController를 제거한다. (이전 화면으로 돌아간다.)

 

delegate?.changeName(name: changeName) 이라는 주문서를 전달한다. 

 

@objc private func didTapButton() {
    changedName = editView.nameTextField.text ?? "잘못입력"
    delegate?.changeName(name: changedName)
    dismiss(animated: true)
}

 

 

 4.3 EditProfileViewController 내의 전체 코드 

 

class EditProfileViewController: UIViewController{
    
    var delegate: changeNameDelegate?
    
    var editView = EditView()
    
    var changedName: String = ""

    override func loadView() {
        self.view = editView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        editView.editButton.addTarget(self, action: #selector(didTapButton), for: .touchUpInside)
    }
    
    @objc private func didTapButton() {
        changedName = editView.nameTextField.text ?? "잘못입력"
        delegate?.changeName(name: changedName)
        dismiss(animated: true)
    }

}



5. 마지막으로 ProfileViewController 클래스 내에서 

didTapButton() 메서드에 아래코드를 구현한다. 

 

여기서 "editVC.delegate = self" 라는 의미는 editVC의 대리자를 

ProfileViewController로 선언한다는 것이다. 

 

editVC.delegate = self 라고 주문서를 확인한다. 

 

@objc private func didTapButton() {
        let editVC = EditProfileViewController()
        editVC.modalPresentationStyle = .fullScreen
        editVC.delegate = self
    
        present(editVC, animated: true)
    }

 

 

 

 

Delegate로 데이터 전달을 사용하는 경우

 

 두 개의 화면이 있다. 두 번째 화면에서 데이터를 첫 번째 화면으로 옮길 때, 

이미 메모리에 올라와 있는 상태에서 데이터 전달 할 때 사용한다.

 

즉, 첫 번째 화면 위로 두 번째 화면을 올리고, 두 번째 화면에 첫 번째 화면으로 present를 통해 

데이터를 전달할 때 사용한다. 

 

또는 A 라는 뷰 컨트롤러 위에 TableViewCell, CollectionViewCell 를 올릴 때, 

이를 갖고 있는 상위 뷰 컨트롤러로 데이터를 전달할 때 사용한다. 

 

 

⭐️ 주의할 점이 하나 있다. 바로 Strong Reference Cycle 이다. 

 

사실 위에서 delegate 프로퍼티를 정의할 때 weak로 선언해야 한다. 

 

이유는 두 개의 클래스 (ProfileViewController, EditProfileViewController) 사이에서 

델리게이트 패턴을 사용할 경우 Strong Reference Cycle이 생길 수 있기 때문이다.