본문 바로가기
iOS/iOS

커뮤니케이션 패턴 (Delegate, Notification, KVO, Target-Action)

by 헤콩 2021. 5. 4.
반응형

보통 DelegateNotificationKVOTarget-Action 같은 것들을 묶어서 커뮤니케이션 패턴 이라고 합니다. 이들이 커뮤니케이션 패턴으로 불리는 이유는 한 가지 공통점 때문인데요, 바로 특정 이벤트가 일어나면 원하는 객체에 알려주어 적절한 조치를 취하도록 한다는 점입니다. 즉, 객체 간의 소통을 위해 사용되는 것들입니다.

어떠한 객체는 그 자체로 존재하면서 다른 객체와 소통하고 싶을 뿐이지 다른 객체에 종속되어 동작하고 싶지는 않을 수 있습니다. 다른 객체에 종속되어 동작한다면 재사용성독립된 기능요소 측면에서 바람직하지 않기 때문이죠.

그래서 우리는 각각의 커뮤니케이션 패턴들을 알아볼 예정입니다 :)

 

 

Delegation

delegate는 보통 protocol을 정의하여 사용합니다. Protocol 이란 일종의 기능 명세서 같은 것으로 다른 언어를 사용해보신 분들이라면 Interface와 비슷하다고 생각하실 수 있어요! 이러한 Protocol에 Delegate로 지정된 객체가 해야하는 메서드들을 명시해놓고, Delegate 역할을 하려는 객체가 이 Protocol을 채택해서 명시된 메서드들을 구현하게 됩니다.

이렇게 세팅 후에 protocol에 메서드들을 명시해 놓은 객체는 어떤 이벤트가 일어났을 시에 그 protocol을 채택해서 구현한, 즉 delegate로 지정한 객체에 알려줄 수 있게 됩니다.

간단하게 그 순서를 나타낸다면 아래와 같겠죠?
    1. A - 자신이 수행해야하는 메서드를 ADelegate 라는 프로토콜에 명시합니다.
    2. B - ADelegate 라는 프로토콜을 채택해서 명시된 메서드의 내용을 구현합니다.
    3. B - A의 delegate를 자신을 지정해서 자신이 구현한 delegate 메서드를 사용할 수 있도록 합니다.
    4. A - 어떤 이벤트가 발생했을 때 ADelegate의 한 메서드를 실행하도록 구현합니다.
    5. A에 어떤 이벤트가 발생하면, B에서 구현된 ADelegate의 메서드가 호출됩니다.

 

  • 장점
    • 매우 엄격한 syntax로 인해 프로토콜에 필요한 메서드들이 명확하게 명시됩니다.
    • 시스템에서 컴파일 시에 프로토콜의 구편되지 않은 메서드를 경고나 에러로 알려줍니다.
    • 로직의 흐름을 따라가기 쉬워집니다.
    • 커뮤니케이션 과정을 유지하고 모니터링하는 제 3의 객체 (Notification Center 같은 외부 객체) 가 필요 없어집니다.
    • 프로토콜이 Controller의 범위 안에서 정의됩니다.
  • 단점
    • 많은 줄의 코드가 필요합니다.
    • delegate 설정에 nil이 들어가지 않게 주의해야 합니다.
    • 다수의 객체들에게 이벤트를 알려주는게 가능하기는 하지만 어렵고 비효율적입니다.

 

 

Notification

Notification Center 라는 싱글턴 객체를 통해서 이벤트들의 발생 여부를 옵저버로 등록한 여러 객체들에게 알려줍니다. Notification name이라는 key 값을 통해 보내고 받을 수 있습니다.

// 알림을 보내는 입장
NotificationCenter.default.post(
    name: Notification.Name("notificationKey"),
    object: sendButton,
    userInfo: ["backgroundColor": backgroundColor]
)

// 알림을 받는 입장
NotificationCenter.default.addObserver(
    self, 
    selector: #selector(receiveNotification(_:)),
    name: Notification.Name("notificationKey"),
    object: nil
)

 

  • 장점
    • 많은 줄의 코드가 필요없이 쉽게 구현이 가능합니다.
    • 다수의 객체들에게 동시에 이벤트의 발생을 알려줄 수 있습니다.
    • Notification과 관련된 정보를 Any? 타입의 object, [AnyHashable: Any]? 타입의 userInfo로 전달할 수 있습니다.
  • 단점
    • key 값으로 Notification의 이름과 userInfo를 서로 맞추기 때문에 컴파일 시에 구독이 잘 되고 있는지, 올바르게 userInfo의 value를 받아오는지 체크가 불가능합니다.
    • 추적이 쉽지 않습니다.
    • Notification post 이후의 정보를 알 수 없습니다.

 

 

KVO (Key-Value-Observing)

KVO는 A 객체에서 B 객체의 프로퍼티가 변화됨을 감지할 수 있는 패턴입니다. 사실 저는 여기까지 읽었을 때 안드로이드의 LiveData를 떠올렸어요ㅎㅅㅎ 위에서 다뤘던 Delegate와 Notification 패턴이 주로 Controller와 다른 객체 사이의 관계를 다룬다면, KVO 패턴은 객체와 객체 사이의 관계를 다루는데 적합합니다. 메서드나 다른 액션에서 나타나는 것이 아니라 프로퍼티의 상태에 반응하는 형태인거죠.

사실 상 스위프트의 property observers (willSet, didSet) 과 유사한 형태라고 볼 수 있는데요, KVO는 타입 정의 밖에서 observe( )를 추가한다는 점이 다릅니다.

 

그리고 순수 스위프트 코드를 작성할 때는 KVO를 잘 사용하지 않는 이유가, KVO는 Objective-C 런타임에 의존하고 있기 때문입니다. 그래서 NSObject를 상속받기 위해서는 @objc를 반드시 붙여줘야 하고, 때문에 KVO는 속성 각각에 @objc dynamic (@objc dynamic : objective-c의 문법 중 하나로, 특정 메서드나 function의 구현을 objective-c 런타임에서 하겠다고 결정한다는 키워드 입니다) 을 붙여줘야 합니다.

class Person: NSObject {
    @objc dynamic var name: String = "Jamie"
}

let person = Person()
person.observe(\.name) { object, change in
    print("name of person object changed to \(person.value)")
}
person.value = "Ellie"


// 또는 아래처럼 구현도 가능합니다
person.observe(\Person.name, options: .new) { person, change in
    print("person is now called \(person.name)")
}

 

  • 장점
    • 두 객체 사이의 정보를 맞춰주는 것이 간단해집니다.
    • new / old value를 쉽게 얻을 수 있습니다.
    • key path 로 옵저빙하기 때문에 nested objects 도 옵저빙이 가능합니다. 여기서 nested object란 중첩된 객체, 즉 어떠한 객체 안에 있는 또 다른 객체를 말합니다.
  • 단점
    • NSObject를 상속받는 객체에서만 사용이 가능합니다.
    • dealloc 될 때 옵저버를 지워줘야 합니다.
    • 많은 value를 감지할 때는 많은 조건문이 필요합니다.

 

 

Target-Action

Target-Action 패턴은 UIControl 객체가 이벤트에 응답하는 방식입니다. 사실 target-action이 뭐지? 하시더라도 알고 보면 다 사용을 하고 계셨던 방식들입니다 :) control이 직접 이벤트를 처리하지 않고, 처리할 target과 처리하는 방식인 action method로 책임을 넘기는 방식이며 하위 클래스로는 UIButton, UIDatePicker, UISlider 등이 있습니다.

 

 

대표적인 예로는 UIButton에 addTarget을 통한 Target-Action 연결이 있겠죠?

button.addTarget(self, #selector(clickButton(_:)), .touchUpInside)

func clickButton(_ sender: UIButton) {
    // ...code
}

 

그리고 Interface Builder도 하나의 예시입니다. @IBAction을 method 선언 앞에 명시한 뒤, 해당 method와 인터페이스 빌더의 control 객체에서 원하는 event와 연결해줍니다.

@IBAction func doSomething()

 

target은 어떠한 object도 될 수 있습니다. 주로 해당 control을 가진 최상위 view의 ViewController로 설정합니다.

 

 

Reference

반응형

'iOS > iOS' 카테고리의 다른 글

What's the difference between a singleton and a shared instance in Swift?  (0) 2021.05.10
Local Notification (로컬 푸시) 구현하기  (0) 2021.05.10
Promises  (0) 2021.04.29
CGRect, CGPoint, CGSize  (0) 2021.04.16
UIResponder  (0) 2021.04.16

댓글