^(코딩캣)^ = @"코딩"하는 고양이;

[번역글] View Controller 사이에 데이터를 교환하는 방법(6) - NotificationCenter [完]

Language/Objective-C & Swift
2021. 9. 16. 09:49

본 게시글은 How To: Pass Data Between View Controllers in Swift를 바탕으로 작성하였습니다.

 

여러분의 앱이 여러 개의 사용자 인터페이스(UI)를 가지고 있다면 여러분은 하나의 UI에서 다른 UI로 데이터를 전달해야 하는 경우도 생길 것입니다. Swift에서는 View Controller 사이에 어떤 방법으로 데이터를 전달할 수 있을까요?

뷰 컨트롤러(View Controller) 사이에 데이터를 주고 받는 것은 iOS 개발의 중요한 일부입니다. 여러분은 몇 가지 방법으로 이를 해낼 수 있고 각기 다른 이점과 약점을 가지고 있습니다.

뷰 컨트롤러 사이에 쉽게 데이터를 교환하는 방법을 선택하는 것은 여러분이 앱 구조를 어떻게 할 것인지에 달려 있습니다. 앱의 구조(App architecture)는 여러분이 뷰 컨트롤러 사이에 어떻게 작동이 이루어 질 것인지에 영향을 주고, 반대로 여러분이 뷰 컨트롤러 사이에 데이터 교환을 어떻게 할 것인지에 따라 앱의 구조가 달라집니다.

 

Swift에서 여러분은 뷰 컨트롤러 사이에 다음의 6가지 방법으로써 데이터를 주고 받을 수 있습니다.

  1. 인스턴스 프로퍼티(property)를 사용하는 방법(A → B 방향)
  2. 스토리보드(Storyboard)와 세그웨(segue)를 사용하는 방법
  3. 인스턴스 프로퍼티와 함수를 사용하는 방법(A ← B 방향)
  4. 델리게이션(delegation) 패턴을 사용하는 방법
  5. 클로저(closure) 또는 핸들러(completion handler)를 사용하는 방법
  6. NotificationCenter 또는 Observer 패턴을 사용하는 방법

 

이 게시글에서 여러분은 뷰 컨트롤러 사이에 데이터를 주고 받는 6가지의 각기 다른 방법에 대해 익히게 될 것입니다. 이 방법에는 프로퍼티(property)를 사용하는 방법, 세그웨(segue)를 사용하는 방법 및 NSNotificationCenter를 사용하는 방법도 들어 있습니다. 비교적 간단한 이들 방법을 통해 좀 더 복잡한 응용까지 나아갈 수 있습니다. 준비가 되었다면 이제 시작하겠습니다.

 

NotificationCenter 또는 Observer 패턴을 사용하기

NotificationCenter 클래스를 사용하여 뷰 컨트롤러 사이에 데이터를 전달할 수도 있습니다. Swift 3부터는 NS 접두어가 빠지면서 그냥 NotificationCenter으로 씁니다. 또한 여기서 말하는 알림(notification)은 푸시 알림(push notification)과는 다른 것임을 알아두시기 바랍니다.

NotificationCenter는 각종 알림들을 다루는데, 특히 접수되는 알림들을 자신의 상태를 청취하고 있는 각종 요소들에게 다시 배분해주는 역할도 합니다. iOS의 SDK에서 NotificationCenter를 구현하는 것은 관찰자(Observer-Observable) 디자인 패턴으로 접근합니다.

전혀 관계가 없는 두 클래스 사이에서도 Notification Center를 사용하면 데이터 교환이 가능하다.

NotificationCenter는 크게 세 가지의 핵심 단계들로 이뤄집니다.

  1. 알림(notification)이 있는지 관찰하기
  2. 알림(notification) 보내기
  3. 알림(notification)에 응답하기

지금부터 알림(notification)을 “관찰(observe)”해보겠습니다. 어떤 알림에 응답하기에 앞서, 여러분은 NotificationCenter에게 여러분이 자신을 관찰하고 싶어함을 알려주어야 합니다. 그러면 NotificationCenter는 자신을 거쳐가는 어떤 알림이든 여러분께 알려줄 것입니다.

모든 알림은 스스로를 식별할 수 있는 이름을 갖고 있습니다. MainViewController 클래스 상단에 다음과 같이 정적 프로퍼티를 하나 만듭니다.

// Swift
static let notificationName = Notification.Name("myNotificationName")
// Objective-C
@interface ...
+ (NSNotificationName)notificationName;
@end
@implement ...
+ (NSNotificationName)notificationName {
    static NSString * name = @"myNotificationName";
    return name;
}
@end

 

“클래스 프로퍼티(class property)”라고도 하는 정적 프로퍼티(static property)는 코드의 어디에서든 MainViewController.notificationName로써 호출될 수 있습니다. 즉 문자열 상수만 가지고 여러 notification 중 이에 해당하는 것만을 식별할 수 있는 것입니다. 이렇게 문자열 상수를 한 곳에 정의해 놓는다면 문자열을 잘못 입력하여 알림을 못 받는 불상사가 일어나지 않을 것 입니다.

이제 notification을 관찰해 보겠습니다.

// Swift
NotificationCenter.default.addObserver(self, selector:#selector(onNotification(notification:)), 
                                                 name:MainViewController.notificationName,
                                               object:nil)
// Objective-C
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(onNotification:)
                                             name:notificationName
                                           object:nil];

 

여러분은 위와 같은 코드를 대체로 viewDidLoad()viewWillAppear(_:) 같은 메소드에 작성할 것입니다. 그래서 뷰 컨트롤러가 화면에 나타날 때 여러분의 관찰 행동이 notification에 등록이 되겠지요. 위 예제에서 어떤 작업들이 수행되는지 살펴보겠습니다.

(1) 먼저 기본으로 내장된 NotificationCenter를 뜻하는 NotificationCenter.default를 사용하고 있습니다. 물론 특수한 용도를 위해 여러분이 직접 NotificationCenter를 생성할 수도 있습니다. 하지만 기본 제공된 것을 사용하는 것이 나을 때가 많습니다.

(2) 위의 NotificationCenter에 대해 addObserver(_:selector:name:object:) 메소드를 호출하고 있습니다.

  1. 첫 번째 매개변수는 관찰을 수행하는 객체(인스턴스)를 지정하는 것인데 거의 항상 self일 때가 많습니다.
  2. 두 번째 매개변수는 알림이 관측되었을 때 호출될 함수(메소드)를 가리키는 셀렉터(selector)입니다. 대개 여기서 지정되는 함수는 현 클래스 내에 있는 메소드일 경우가 많습니다.
  3. 세 번째 매개변수는 알림의 이름입니다. 여기서는 앞서 정적 프로퍼티로 선언 및 정의했던 notificationName을 적어주면 됩니다.
  4. 네 번째 매개변수는 여러분이 알림에 동반되는 객체를 받고자 할 때 사용되는 매개변수인데, 대개는 nil을 지정하지만 특정 객체에서 생성되는 알림만을 받고자 할 때 그 객체를 이 곳에 지정할 수도 있습니다.

이후에 다음과 같은 코드로써 알림 관측을 중지시킬 수도 있습니다.

// Swift
NotificationCenter.default.removeObserver(self, name:MainViewController.notificationName,
                                              object:nil)
// Objective-C
[[NotificationCenter defaultCenter] removeObserver:self
                                              name:notificationName
                                            object:nil];

 

등록시켰던 모든 알림 관측을 한 번에 중지시킬 수도 있습니다.

// Swift
NotificationCenter.default.removeObserver(self)
// Objective-C
[[NotificationCenter defaultCenter] removeObserver:self];

 

모든 알림은 명시적(explicit)이기 때문에 여러분은 알림이 발생할 때 하나의 객체당(대개는 self) 하나의 함수(메소드) 호출이 이뤄지는 한 가지 유형의 알림 단위로 관측하게 될 것임을 기억해 두기 바랍니다.

알림이 발생하였을 때 호출될 함수(메소드)는 onNotification(notification:)입니다. 이것도 클래스에 추가해 보겠습니다.

// Swift
@objc func onNotification(notification:Notification) {
    print(notification.userInfo)
}
// Objective-C
-(void) onNotification:(NSNotification *)notification {
    NSLog("%@", [notification userInfo])
}

 

Swift 4부터 @objc 키워드가 필요하게 되었습니다. 왜냐하면 NSNotificationCenter는 Objective-C 코드로 적힌 프레임워크의 일부이기 때문입니다. 이 함수(메소드)에서 여러분은 간단하게 notification.userInfo를 출력할 것입니다.

알림을 띄우는 것은 쉽습니다. 다음과 같이 하면 됩니다.

// Swift
NotificationCenter.default.post(name:MainViewController.notificationName,
                              object:nil,
                            userInfo:["data": 42, "isImportant": true])
// Objective-C
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName 
                                                    object:nil 
                                                  userInfo:@{@"data": @42, @"isImportant": @true}];

몇 가지 중요한 사실을 다시 확인해 보겠습니다.

(1) 여러분은 앞서 사용한 Notification Center인 NotificationCenter.default 객체를 통해 post(name:object:userInfo:)를 호출하여 알림을 띄워야 합니다.

(2) 첫 번째 매개변수는 띄울 알림을 식별할 수 있는 이름입니다. 여러분이 앞서 정의한 정적 상수를 이 곳에서도 사용하면 됩니다.

(3) 두 번째 매개변수는 알림을 띄우는 객체입니다. 대개 nil을 전달하겠지만, 알림을 관측하기 위해 어떤 객체를 지정하였다면 알람을 띄울 때에도 같은 객체를 이 곳에 지정하여 그 객체만을 위해 배타적으로 알림을 띄우고 수신할 수 있습니다.

(4) 세 번째 매개변수는 userInfo라고 불리는 부가적인 정보입니다. 여러분은 딕셔너리(Dictionary / NSDictionary) 형식으로써 어떤 종류의 데이터든 전달할 수 있습니다. 이 예제에서도 정수와 불리언 값으로 이루어진 약간의 데이터를 추가로 주고 받아보았습니다.

 

Notification Center를 사용하여 뷰 컨트롤러 사이에 데이터를 주고 받는 방법은 이것으로 모두 살펴보았고, 예를 들어 다음과 같은 상황에서 사용할 수 있습니다.

(1) 데이터를 주고 받고자 하는 뷰 컨트롤러 또는 다른 클래스 사이에는 밀접하게 관련되어있지 않는 경우입니다. REST API가 새로운 데이터를 받을 때마다 응답해야 하는 테이블 뷰 컨트롤러(Table View Controller)에 대해 떠올려봅니다.

(2) 뷰 컨트롤러가 꼭 지금 당장 존재할 필요는 없는 경우입니다. 테이블 뷰 컨트롤러가 화면에 나타나기에 앞서 REST API가 데이터를 수신하는 경우도 생길 수 있습니다. 알림을 관찰하는 것은 선택적인 사항이기 때문에 앱의 요소 중 알림을 받아야 하는 일부 요소가 아직 만들어지지 않았거나 이미 사라졌더라도 알림을 띄우는 입장에서는 이에 개의치 않고 알림을 띄울 수 있습니다.

(3) 뷰 컨트롤러들은 어떤 하나의 알림에 대해 응답할 필요가 있을 것이고, 하나의 뷰 컨트롤러는 여러 개의 알림에 응답할 필요가 있을 수 있습니다. 다시 말하면 Notification Center는 다대다(many-to-many) 관계를 갖습니다.

여러분은 이 Notification Center를 마치 정보의 간선 고속도로처럼 생각할 수도 있습니다. iOS 내 각 부분에서 띄워진 알림은 일단 이 쪽으로 모인 다음에 다시 다양한 목적지로 향해 가기 때문입니다. 단지 뷰 컨트롤러 사이에 발생하는 “지역적인 트래픽(local traffic)”만을 원할 경우 Notification Center를 선택하는 것은 적합하지 않습니다. 앞서 살펴 본 델리게이트, 프로퍼티 또는 클로저 같은 비교적 단순한 방법들이 있기 때문입니다. 그러나 여러분이 어떤 데이터를 반복적으로, 규칙적으로 여러분의 앱의 한 부분에서 다른 부분으로 보내고자 한다면 Notification Center는 최고의 방법입니다.

카테고리 “Language/Objective-C & Swift”
more...