옵저버 패턴은 객체 간의 상호 작용을 구현하기 위한 디자인 패턴 중 하나입니다.
iOS 개발에서는 주로 NotificationCenter와 KVO(Key-Value Observing)를 통해 옵저버 패턴을 활용합니다.
이번 글에서는 옵저버 패턴의 개념과 iOS에서의 활용 방법에 대해 자세히 알아보겠습니다.
옵저버 패턴이란?
옵저버 패턴은 객체의 상태 변화를 다른 객체에게 알리고, 해당 객체는 필요한 작업을 수행할 수 있도록 하는 디자인 패턴입니다.
객체 간의 결합도를 낮추고 확장성을 높이기 위해 사용됩니다.
주로 한 객체의 상태 변화에 따라 여러 객체가 동적으로 반응해야 하는 상황에서 유용하게 활용됩니다.
옵저버 패턴은 다음과 같은 주요 요소로 구성됩니다.
- Subject (주체): 상태 변화를 감지하여 옵저버에게 알리는 객체입니다. 주체는 관찰 대상이 되는 상태를 가지고 있으며, 옵저버가 등록되어 있습니다.
- Observer (옵저버): 주체의 상태 변화를 관찰하고 알림을 받는 객체입니다. 옵저버는 주체에게 등록되어야 하며, 상태 변화에 따른 작업을 수행합니다.
- Notification (알림): 주체가 옵저버에게 상태 변화를 알리기 위해 보내는 메시지 또는 이벤트입니다. 주체는 알림을 통해 옵저버에게 상태 변화를 전달합니다.
NotificationCenter를 활용한 옵저버 패턴
iOS에서는 NotificationCenter를 활용하여 옵저버 패턴을 구현할 수 있습니다.
NotificationCenter는 시스템 내에서 발생하는 이벤트를 관리하고, 이벤트를 수신하고자 하는 객체들에게 알림을 전달하는 중앙 집중화된 통지 메커니즘입니다.
NotificationCenter를 활용한 옵저버 패턴은 다음과 같은 단계로 구현할 수 있습니다.
- Notification을 발송하는 주체 객체는 NotificationCenter에 등록하여 해당 이벤트에 대한 알림을 전송할 수 있습니다. 이를 위해 `post(name:object:userInfo:)` 메서드를 사용합니다.
- 관찰하고자 하는 옵저버 객체는 NotificationCenter에 등록하여 특정 이벤트에 대한 알림을 받을 수 있습니다. 이를 위해 `addObserver(_:selector(_:name:object:)` 메서드를 사용합니다.
- 주체 객체에서 상태 변화가 발생하면, NotificationCenter를 통해 등록된 옵저버에게 알림을 전송합니다. 알림은 이름(name)과 함께 전달되며, 옵저버는 해당 이름을 기반으로 알림을 수신하고 적절한 작업을 수행합니다.
- 옵저버는 알림을 수신하기 위해 Notification의 이름(name)과 일치하는 셀렉터 메서드를 구현해야 합니다. 이 셀렉터 메서드는 알림을 수신하고 처리하는 역할을 담당합니다.
NotificationCenter를 사용한 옵저버 패턴은 다음과 같은 장점을 갖습니다.
- 객체 간의 결합도를 낮춥니다. 주체 객체는 옵저버의 존재를 알 필요 없이 알림만 전송하면 됩니다.
- 여러 객체가 동시에 같은 이벤트를 관찰할 수 있습니다. 하나의 이벤트에 대해 다수의 옵저버를 등록할 수 있습니다.
- 앱 전반에서 중앙 집중화된 이벤트 관리가 가능합니다. 여러 개의 클래스에서 동일한 이벤트를 처리해야 할 때 효율적으로 관리할 수 있습니다.
KVO (Key-Value Observing)를 활용한 옵저버 패턴
iOS에서의 옵저버 패턴을 구현할 수 있는 또 다른 방법은 KVO(Key-Value Observing)입니다.
KVO는 특정 객체의 속성 변화를 감지하고, 해당 변화에 따른 알림을 받을 수 있도록 하는 메커니즘입니다.
KVO를 활용한 옵저버 패턴은 다음과 같은 단계로 구현됩니다.
- 관찰하고자 하는 속성이 있는 객체에서
addObserver(_:forKeyPath:options:context:)
메서드를 호출하여 옵저버를 등록합니다. - 옵저버 객체는 등록된 속성의 변화를 감지하기 위해
observeValue(forKeyPath:of:change:context:)
메서드를 구현합니다. 이 메서드는 속성의 변화에 따라 호출됩니다. - 속성에 변화가 발생하면, KVO 메커니즘이 등록된 옵저버에게 알림을 전달합니다. 알림은
observeValue(forKeyPath:of:change:context:)
메서드를 호출하는 형태로 전달됩니다.
KVO를 활용한 옵저버 패턴의 장점은 다음과 같습니다.
- 두 객체를 손쉽게 동기화 할 수 있습니다.
- KVO는 특정 속성에 대한 변화를 감지하기 때문에, 세부적인 변경 사항을 추적하고 처리할 수 있습니다.
- 기존의 객체 구조를 수정하지 않고도 속성 변화에 대한 알림을 받을 수 있어, 유연성과 확장성이 높아집니다.
- 관찰 중인 프로퍼티의 oldValue와 newValue를 제공합니다.
- observer를 따로 해제해주지 않아도 되어, 메모리 관리가 용이합니다.
아래 글에서 더 자세한 내용을 참고해보세요.
https://zeddios.tistory.com/1220
Key-Value Observing(KVO) in Swift
안녕하세요 :) Zedd입니다. 오늘은 KVO에 대해서 공부! # KVO - Key-Value Observing의 약자 - 객체의 프로퍼티의 변경사항을 다른 객체에 알리기 위해 사용하는 코코아 프로그래밍 패턴 - Model과 View와 같이
zeddios.tistory.com
옵저버 패턴의 활용 예시
옵저버 패턴은 iOS 애플리케이션 개발에서 다양한 상황에서 활용됩니다. 몇 가지 예시를 살펴보겠습니다.
- 테마 변경: 사용자가 앱 내에서 테마를 변경할 때, 테마 변경에 따른 UI 업데이트 작업을 수행할 수 있습니다. 사용자 설정에서 테마 변경을 감지하고, UI를 업데이트하는 옵저버를 등록하여 테마 변경에 대한 작업을 처리할 수 있습니다.
- 데이터 동기화: 앱 내의 데이터가 변경되었을 때, 해당 변경 사항을 다른 객체에게 알리고 동기화 작업을 수행할 수 있습니다. 데이터 변경을 감지하는 옵저버를 등록하여, 변경 사항에 따른 작업을 수행할 수 있습니다.
- 키보드 표시/숨김: 키보드의 표시나 숨김 상태를 감지하여, 화면 레이아웃 조정 등 필요한 작업을 수행할 수 있습니다. 키보드 상태 변경을 감지하는 옵저버를 등록하여, 키보드에 따른 작업을 수행할 수 있습니다.
- 데이터 업데이트: 원격 서버에서 데이터가 업데이트되었을 때, 해당 업데이트에 대한 알림을 받고 UI를 업데이트할 수 있습니다. 데이터 업데이트를 감지하는 옵저버를 등록하여, 업데이트에 따른 작업을 수행할 수 있습니다.
- 사용자 인증 상태 변경: 사용자의 로그인 또는 로그아웃 상태 변경을 감지하여 다른 객체에게 알립니다. 이를 통해 UI 업데이트, 데이터 동기화 등 필요한 작업을 수행할 수 있습니다.
- 네트워크 연결 상태 변경: 디바이스의 네트워크 연결 상태 변경을 감지하여 네트워크 작업을 수행하는 객체에게 알립니다. 네트워크 연결이 복구되면 자동으로 데이터 업데이트를 시작할 수 있습니다.
- 애플리케이션 상태 변경: 애플리케이션의 백그라운드 진입, 포그라운드 복귀 등 애플리케이션 상태 변화를 감지하여 관련된 작업을 처리할 수 있습니다. 예를 들어, 백그라운드 진입 시 데이터 저장 및 상태 보존 작업을 수행할 수 있습니다.
- 주제 기반 통신: 특정 주제(topic)에 관련된 이벤트를 구독하는 옵저버를 등록하여, 해당 주제에 대한 업데이트를 실시간으로 수신하고 처리할 수 있습니다. 이는 채팅 애플리케이션, 실시간 게시판 등에서 유용하게 사용될 수 있습니다.
위의 예시에서는 NotificationCenter와 KVO를 활용하여 옵저버 패턴을 구현할 수 있습니다. 이를 통해 객체 간의 유연하고 효율적인 상호 작용을 구현할 수 있습니다.
옵저버 패턴은 iOS 애플리케이션 개발에서 매우 유용한 디자인 패턴입니다. 객체 간의 결합도를 낮추고, 상태 변화에 대한 실시간 알림과 작업 처리를 가능하게 해줍니다. 적절히 활용하면 유연하고 확장 가능한 앱을 개발할 수 있습니다.
하지만 주의해야 할 점은 옵저버 패턴을 과도하게 사용하면 코드의 가독성과 유지 보수성이 저하될 수 있습니다.
따라서 옵저버 패턴을 사용할 때는 신중하게 객체 간의 상호 작용을 설계하고, 필요한 경우에만 적용하는 것이 좋습니다.
실제 코드 예시
음악 애플리케이션을 개발하고 있다고 가정해보겠습니다.
사용자가 플레이리스트에 새로운 곡을 추가하면, 다른 화면에서 해당 곡이 추가되었음을 실시간으로 업데이트하여 사용자에게 보여줘야 합니다.
이때 NotificationCenter를 활용하여 옵저버 패턴을 구현할 수 있습니다.
- 곡 추가 동작이 발생하는 화면에서 새로운 곡을 추가하고, NotificationCenter를 통해 알림을 발송합니다.
class PlaylistViewController: UIViewController {
// ...
func addNewSong(_ song: Song) {
// 새로운 곡 추가 로직
// ...
// 알림 발송
NotificationCenter.default.post(name: Notification.Name("NewSongAddedNotification"), object: song)
}
// ...
}
- 새로운 곡이 추가되었을 때 업데이트할 화면인 NowPlayingViewController에서 NotificationCenter를 통해 알림을 수신하고 처리합니다.
class NowPlayingViewController: UIViewController {
// ...
override func viewDidLoad() {
super.viewDidLoad()
// 옵저버 등록
NotificationCenter.default.addObserver(self, selector: #selector(handleNewSongAdded(_:)), name: Notification.Name("NewSongAddedNotification"), object: nil)
}
@objc func handleNewSongAdded(_ notification: Notification) {
if let newSong = notification.object as? Song {
// 새로운 곡 추가 처리
// ...
}
}
deinit {
// 이 클래스에 등록된 모든 옵저버 등록 해제
NotificationCenter.default.removeObserver(self)
}
// ...
}
위의 예시를 통해, PlaylistViewController에서 곡을 추가할 때 NotificationCenter를 통해 newSongAdded
알림을 발송하고, NowPlayingViewController에서 해당 알림을 수신하여 새로운 곡을 처리하는 방식으로 옵저버 패턴을 구현했습니다.
이를 통해 사용자는 NowPlayingViewController에서 플레이리스트에 새로운 곡이 추가될 때마다 업데이트된 정보를 실시간으로 확인할 수 있습니다.
주의할 점은 NotificationCenter를 사용할 때 알림을 수신하는 객체가 반드시 올바른 타이밍에 등록 및 해제되어야 합니다.
NowPlayingViewController에서는 viewDidLoad에서 옵저버를 등록하고, deinit에서 등록을 해제하도록 처리하였습니다.
옵저버 패턴을 활용하면 다른 객체 간의 결합도를 낮출 수 있으며, 객체 간의 유연한 상호 작용을 가능하게 합니다.
이를 iOS UIKit 프로젝트에서 활용하면 다양한 상황에서 실시간 업데이트, 상태 변화 등을 감지하고 적절한 작업을 수행할 수 있습니다.
효율적인 관리를 위한 Notification 설계 팁
NotificationCenter로 observer 패턴을 구현하다 보면, observer를 등록하고 이벤트를 post 하는 코드가 상당히 길어지는 모습을 종종 봅니다.
// Post
NotificationCenter.default.post(name: Notification.Name("onChangeData"), object: nil)
// Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(doSomething(_:)), name: Notification.Name("onChangeData"), object: nil)
Notification.Name Extension을 만들어 관리하면 간략하게 표현할 수 있습니다.
더불어, 한 곳에서 모든 observer를 모아 볼 수 있어 관리가 용이합니다.
extension Notification.Name {
static let onChangeData = Notification.Name("onChangeData")
}
-> 간단해진 코드
// Post
NotificationCenter.default.post(name: .onChangeData, object: nil)
// Add Observer
NotificationCenter.default.addObserver(self, selector: #selector(doSomething), name: .onChangeData, object: nil)
'iOS > 디자인패턴' 카테고리의 다른 글
[iOS 디자인패턴] Factory 패턴에 대해 (1) | 2023.05.25 |
---|---|
[iOS 디자인패턴] Singleton 패턴에 대해 (0) | 2023.05.11 |
[iOS 디자인패턴] Delegate 패턴과 Protocol에 대해 (0) | 2023.05.11 |