[Swift] 주문하신 노티피케이션 나왔습니다.

GUNDY·2023년 11월 15일
1
post-thumbnail

특정 조건일 때마다 무언가를 해야하는 경우가 있다.

가령 커피가 만들어지면 고객님을 부른다거나

옷이 사고 싶어질 때마다 일단 짤을 본다거나

단발머리를 하고 싶어질 때마다 단발병 퇴치짤을 본다거나

필자는 비가 내릴 때마다 파막이 먹고싶다.

이처럼 특정 상황에 무엇을 할지 지정하는 방법은 여러가지가 있겠지만

오늘의 주제는 바로바로 노티피케이션 센터!

노티피케이션 센터가 뭔데?

트위치 애청자(이하 트수)라면 알겠지만 스트리머가 생방송을 시작하면 트위치는 트수에게 생방송 시작 알림을 보낸다.

알림을 확인한 트수는 당연히 바로 방송을 시청할 것이다.

이처럼 시청자(Observer)에게 알림(Notification)을 보내는 트위치(NotificationCenter) 같은 녀석이다.

이 노티피케이션 센터를 통해 할 수 있는 일은 크게 두 가지다.

1. 알림을 보낸다.
2. 알림을 받는다.

알림 보내기

알림을 보내는 방법은 post라는 메서드를 활용하는 것이다.

세 개나 있는데 어떤 걸 써?

사실 다 똑같다.

첫 번째의 post(_:)Notification 타입의 매개변수만 받는 메서드이다. 전달된 매개변수를 그대로 발송하게 된다.

두 번째의 post(name:object:userInfo:)post(name:object:) 같은 경우는 이 메서드 내부에서 노티피케이션을 만들어서 발송하게 된다.

그런데 노티피케이션의 이니셜라이저를 보게되면 매개변수는 name, object, userInfopost 메서드의 매개변수들과 같다.

단지 노티피케이션을 만들어서 전달하냐 전달된 값으로 만드냐의 차이만 있을 뿐이다.

재미있는 점은 두 번째 postuserInfonil이라는 매개변수 기본 값을 갖고 있는데, 이로 인데 userInfo를 생략하고 호출할 수 있다는 점이다.

그런데 생략하는 순간 세 번째 post의 모양과 같아지므로 세 번째 post가 호출될 것인데 굳이 따로 만든 이유는 무엇일까?

This is a convenience method for calling post(name:object:userInfo:) and passing nil to aUserInfo.

공식 문서에서는 두 번째 post에서 userInfonil을 전달하는 편리한 방법이라고는 하는데...나눠 놓은 이유는 잘 모르겠다. 옵젝씨를 안 써봐서 그런가?

알림 받기

알림을 보내기 위해서 post를 썼다면 받기 위해서는 addObserver라는 메서드를 사용한다.

알림을 받을 옵저버를 노티피케이션 센터에 등록해주는 과정인데 쉽게 생각하면 구독신청하는 것이다.

두 개는 뭐가 달라?

post와 달리 이번에는 두 메서드가 다르다.

첫 번째 addObserver는 알림이 오면 using에 전달한 클로저를 실행하는 것이고

두 번째 addObserver는 알림이 오면 selecter를 실행하게 된다.

말해놓고 보니 비슷한데?

다르다니깐!

우선 셀렉터는 이 문서를 보면 알 수 있듯이 Objective-C의 기능을 Swift에서도 사용할 수 있도록 메서드의 이름을 참조하는 타입이다.

중요한 것은 구조체에서는 @objc를 사용해 메서드를 정의할 수 없다. 만약 시도하면 다음과 같은 에러가 발생한다.

@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes

그래서 첫 번째 addObserver(forName:object:queue:using:) 메서드는 클래스에서도 구조체에서도 사용할 수 있지만 두 번째 addObserver(_:selector:name:object:) 메서드는 보통 클래스에서만 쓸 수 있다.

공통적인 매개변수를 살펴보자

1. name, forName: Notification.Name 타입으로 '이 이름으로 발송된 알림에만 반응하겠다'라는 의미다.
2. object: '이 객체가 보낸 알림에만 반응하겠다'라는 의미다.

이메일로 빗대어 표현하자면 name은 메일 제목이고, object는 보낸 사람이다.

이 두 매개변수는 Notification의 이니셜라이저에도 있었던 것을 기억할 것이다. 그 name, object와 비교한다는 소리다.

이제 실습이다.

우선 NotificationCenterFoundation 프레임워크에 포함되어 있기 때문에 꼭 import Foundation을 해 주어야 한다.

import Foundation

처음에 인방을 예시로 들었으니 인방을 하는 스트리머와 구독자를 모두 나타낼 수 있는 Person이라는 타입을 만들어보겠다.

struct Person {
    
    let name: String
    
    func postNotification(toCenter center: NotificationCenter) {
        print("\(name) 방송을 시작합니다.")
        center.post(name: .init(name),
                    object: nil)
    }
    
    func subscribe(toCenter center: NotificationCenter) {
        center.addObserver(forName: nil,
                           object: nil,
                           queue: nil) { notification in
            print("\(notification.name.rawValue) 방송 알림 왔다!")
        }
    }
}

이제 방송국이 필요하다.

let twitch = NotificationCenter()

이 트위치가 아닌데?

그리고 구독할 시청자를 하나 만들어주자.

let korean = Person(name: "한국인")

korean.subscribe(toCenter: twitch)

이제 알림을 보낼 스트리머만 만들어주면 된다.

let calmDownMan = Person(name: "침착맨")

그리고 스트리머가 알림을 방송국에 보내면?

calmDownMan.postNotification(toCenter: twitch)
// "침착맨 방송을 시작합니다." 출력
// "침착맨 방송 알림 왔다!" 출력

이처럼 subscribe 메서드에 설정된 대로 print("\(notification.name.rawValue) 방송 알림 왔다!")가 실행된다.

근데 노티피케이션 센터는 필요할 때마다 만들어 줘야 해?

노티피케이션 센터에는 default라는 타입 프로퍼티가 있다.

쉽게 접근해서 사용할 수 있고, 전역적으로 접근할 수 있기 때문에 이 타입 프로퍼티를 사용하는 경우도 있다.

내가 생각하는 장단점

장점

직접적인 연결 없이도 중간객체인 노티피케이션 센터를 통해 값을 전달할 수 있다.

일대다의 데이터 전달이 가능하다.

name이나 object 등을 통해 알림을 필터링할 수 있다.

비교적 간단하게 로직을 구현할 수 있다.

그 외 기타 등등...

단점

직접적인 연결이 없기 때문에 데이터 흐름을 직관적으로 파악하기 어렵다.

이 밖에도 다른 장단점들도 있겠지만 쓰기 나름이라고도 생각한다.

마무리

오늘은 구독자와 알림을 연결시켜주는 방송국, 노티피케이션 센터에 대해 알아보았다.

iPhone OS 2 시절부터 이어져온 유서깊은 기능이다.

Combine 프레임워크가 나온 이후로 퍼블리셔로도 사용할 수 있게 되었는데, 관심이 있는 사람들은 컴바인과 해당 부분을 공부해 보면 좋을 것 같다.

profile
개발자할건디?

0개의 댓글