ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • UIKit에서 Delegate Pattern를 쓰는 이유
    공부/iOS 2023. 12. 21. 14:38

    # 시작하기 전...

    UIKit을 처음 접했을 때 가장 당황했던 것은 호출하지 않고도 실행되는 함수였습니다.

    무슨 소리인가? 싶겠지만 정말 func 로 구현은 되어있는데 어디에도 호출은 하지 않았지만 분명 그 코드는 동작하고 있었던 것이죠.

    그 미스테리의 정체는 오늘 알아볼 Delegate입니다.

     

    # Delegate란?

    Delegate를 직역하면 위임자란 뜻입니다.

    (이걸 처음 봤을 때 나의 심정)

    도대체 무슨 소리인가 싶고, 설명도 잘 이해가 되지 않죠...

     

    뭔가 위임자라고 하면 뭔가 대신 일을 처리해주는 종속된 관계가 아닐까 생각했다.

    하지만 정 반대로 일종의 Delegate를 채택하는 쪽이 팀장님, 요청하는 쪽이 직원에 가깝습니다.

     

    Delegate를 생성하는 과정을 비유를 섞어 설명해볼게요.

     

    1. Delegate 생성 및 채택

    회사에는 작업이 완료될 때 마다 보고를 하는 프로세스가 있습니다.

    구체적인 내용은 각  팀장의 재량에 맡깁니다.

    protocol WorkerDelegate: AnyObject {
    // 작업 종료 후에 보고하기
        func didFinishWork()
    }

     

    2. WorkerDelegate 프로토콜 채택

    한 팀장님이 이 체계가 마음에 들어 자신의 팀에도 도입하기로 합니다.

    보고를 할 때는 PPT를 만들어야 한다는 것을 추가합니다.

    extension ManagerViewController: WorkerDelegate {
    
        func didFinishWork() {
            // 작업이 완료된 후에 업무를 보고합니다.
            // PPT를 만든 뒤에 발표하세요.
            print("업무 보고")
        }
    }

     

    3. WorkerDelegate를 선언

    위에서 시키면 시키는대로 할 준비가 된 성실한 직원입니다. 

    class Worker {
        weak var delegate: WorkerDelegate?
        
    }

     

    4. ManagerViewController가 worker의 대행자

    위 직원을 팀장님(ManagerViewController)이 "직접" 직원(Worker)을 담당하기로 합니다.

    class ManagerViewController: UIViewController {
    
        private let worker = Worker()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            // self = ManagerViewController
            worker.delegate = self
        }
    }

     

    5. Worker에서 작업 구현

    Worker의 일을 정의합니다. 여기에는 업무가 끝난 뒤에 보고하는게 포함되어있습니다. 

    여기서 didFinishWork()는 팀장님이 시켰던 PPT를 포함한 업무보고입니다.

    class Worker {
        weak var delegate: WorkerDelegate?
    
        func doWork() {
            // 업무를 수행합니다.
            //
            // 업무 수행이 끝난 뒤에 일이 다 끝났다고 관리자에게 보고를 합니다.
            delegate?.didFinishWork()
        }
    }

     

    6. 구현한 작업을 실행

    worker에게 작업을 시킵니다.

    위에서 구현한대로 worker는 업무의 마지막 단계에서 2번에서 구현한 함수를 실행하면서 보고를 할 것입니다.

    class ManagerViewController: UIViewController {
    
        private let worker = Worker()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            worker.delegate = self
            
            // 직원에게 일을 시킵니다.
            worker.doWork()
        }
    }

     

     

    # 이해하기

    이 모든 과정은 팀장님과 직원의 1:1 커뮤니케이션으로 진행됩니다.

     

    만약 팀장님이 바뀌어 보고체계에 변화가 생긴다면?

    Worker의 코드는 바꿀 필요가 없습니다. 그저 하던 작업을 똑같이 수행하면 됩니다!

    Delegate를 채택한 팀장(NewViewConroller)에서 didFinishWork의 구현부만 수정하면 된다는 뜻이니까요 ㅎㅎ

     

    만약 보고를 할 담당자가 아무도 없다면? (worker.delegate = nil)

    그럼 업무만 딱 수행하고 별도의 보고는 안해도 되겠죠!

     

    # 활용하기

    이런 특이한 구조가 실전에서 어떻게 활용할 수 있을까요?

     

    UITextFieldDelegate를 예시로 들어서 설명해볼게요.

    평소 텍스트필드만 사용할 때는 이용할 수 없던 메서드를 Delegate를 채택하면 사용할 수 있게 됩니다.

    그럼 shouldChangeCharactersIn을 활용해 텍스트의 최대 길이를 10으로 제한해보겠습니다!

    extension ViewController: UITextFieldDelegate {
        
        func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
            
            let length = textField.text!.count + string.count - range.length
            
            textField.layer.borderWidth = 3.0
            textField.layer.borderColor = length <= 10 ? UIColor.green.cgColor : UIColor.red.cgColor
            return length <= 10
        }
    }

    1) textField.text!.count: 텍스트필드에 입력되어있는 텍스트의 길이입니다.

    2) string.count: 사용자가 입력에 새롭게 추가될 텍스트의 길이 입니다.

    3) range.length: 사용자의 입력에 기존 텍스트의 변화하게 될 텍스트의 길이입니다.

     

    이 메서드는 텍스트가 입력될 때 마다 위 함수를 호출해 입력 여부를 결정하게 됩니다.

    만약 사용자가 10글자를 초과한 입력을 하게 된다면 입력이 불가능하겠죠.

    ViewController에서 최대 길이에 대한 제한을 걸 수도 있겠지만, 이렇게 간단하게 함수를 구현만 하더라도 제약을 걸 수 있는 것이죠.

    또한 이 함수가 사용자가 입력할 때 마다 호출된다는 점을 이용해 컬러 변경과 같은 효과를 유동적으로 적용시킬 수도 있겠네요.

     

    # 결론

    오늘은 UIKit의 Delegate 패턴에 대해 알아봤습니다.

    예제에서 파라미터를 따로 지정해주지 않았지만 UITextFieldDelegate처럼 파라미터를 지정해주면 데이터를 전달하는 역할로도 사용할 수 있게 됩니다. 이를 활용하면 UIViewController와 UIView, UIViewController와 다른 UIViewController 사이에 데이터를 전달하는 용도로 사용할 수 있겠죠.

     

    결국 1:1 관계에선 강력하게 작동한다고 볼 수 있을 것 같습니다.

    다만 depth가 길어지거나 공통으로 사용되는 영역이 많은 경우, 통신과 같은 비동기 처리가 잦은 경우에는 부적절해보입니다.

    물론 didset 같은거로 막 도배하면 되기는 하겠지만... 

     

     

     

     

     

     

     

     

Designed by Tistory.