Roen의 iOS 개발로그

NSUbiquitousKeyValueStore 1탄: 그게 뭔데?

by Steady On
아무리 뒤져도 한글 자료가 없어서 목마른 놈이 우물을 판다는 심정으로 공부하면서 정리하는 시리즈입니다.
자료가 없다보니 애플 공식문서(https://developer.apple.com/)를 토씨하나 빼먹지 않고 파헤쳐보겠습니다.
영어를 아주 잘하지는 못하므로 번역기의 도움도 받는지라 틀린 표현이 있을 수 있는 점 감안하고 보시면 되겠습니다.

 

왜 NSUbiquitousKeyValueStore를 사용하게 되었는가?

단어장 앱을 만들면서 아이패드와 아이폰 모두에서 단어장을 동일한 순서로 보여주길 원했다. 고정된 단어장은 Vocabulary 객체에 isPinned라는 bool 타입의 프로퍼티를 true로 바꿔주면 되었지만, 그것으로는 사용자가 스스로 설정한 단어장의 순서까지는 연동되는 기기 모두에서 동일할 수 없었기 때문에 단어장의 순서를 저장하고 연동시킬 무언가가 필요했다.

처음에는 아무 생각없이 UserDefaults를 사용했는데, 이 친구의 단점은 정말 단순한 로컬데이터여서 아이폰에서 설정한 것을 아이패드에 동일하게 연동을 시킬 수는 없는 것이었다. 그래서 찾아낸 것이 NSUbiquitousKeyValueStore이다.

 

NSUbiquitousKeyValueStore는 무엇인가?

그냥 정말 단순하게 내가 이해한대로 표현하자면, '동일한 Apple ID로 로그인한 기기에서 연동이 되는 UserDefaults 같은 것'이다. NSUbiquitousKeyValueStore는 UserDefaults와 닮은 점이 많다. 어떤 특정한 key에 대해 value를 저장하는 부분이나 value의 타입이 원시 타입으로만 한정된다는 것이다. 또, 둘다 많은 양의 데이터를 저장할 수는 없다. 그럼 공식문서에서는 뭐라고 하는지 좀 보자.

 

Designing for Key-Value Data in iCloud

앱 기본 설정, 앱 구성 또는 앱 상태에 대한 개별 값을 iCloud에 저장하기 위해 iCloud key-value storage를 사용합니다. key-value store는 로컬 UserDefaults 데이터베이스와 유사하지만 key-value store에 저장한 값은 사용자의 모든 다양한 장치에서 앱의 모든 인스턴스에서 사용할 수 있습니다. 그림 2-1과 같이 앱의 한 인스턴스가 값을 변경하면 다른 인스턴스가 해당 변경 사항을 확인하고 이를 사용하여 구성을 업데이트할 수 있습니다.

그림 2-1. iCloud key-value storage

그림 2-2와 같이 단일 장치의 key-value storage 상호 작용을 확대하면, 앱이 공유된 NSUbiquitousKeyValueStore 객체를 사용하여  key-value storage에 액세스하는 것을 확인할 수 있습니다.

그림 2-2.  iCloud key-value storage access

NSUserDefaults 객체와 마찬가지로, iCloud key-value store를 사용하여 스칼라 값(Bool 같은)과 속성 목록 객체 타입(NSNumber, NSString, NSDate, NSData, NSArray, and NSDictionary)을 저장하고 가져올 수 있습니다. Array와 Dictionary 값은 이러한 값 타입 중 하나를 취할 수 있습니다. NSUbiquitousKeyValueStore class는 각각의 타입을 읽고 쓰는 메서드를 제공합니다(NSUbiquitousKeyValueStore Class Reference에 설명된 대로). 
key-value data가 작성될 때마다, 작업은 자동으로 성공하거나 실패합니다. 즉 모든 데이터가 작성되거나 작성되지 않습니다. 앱이 유효성을 보장하기 위해 일련의 값을 함께 저장해야 할 때 이 동작을 활용할 수 있습니다. 즉, 상호 종속적인 값을 사전에 배치하고 setDictionary:forKey: 메서드를 호출합니다.

요약하자면, 앱의 설정, 상태와 같은 데이터를 iCloud의 Key-Value Store에 저장해 두었다가 같은 apple ID로 로그인 된 기기에 동일한 앱을 실행하는 경우 Key-Value Store에서 값을 가져와서 동일한 설정과 상태를 동기화 할 수 있게 해준다는 것이다. UserDefaults와 닮았지만, iCloud를 통해 연동되는 데이터라는 것!

 

Data Size Limits for Key-Value Storage

앱의 iCloud iCloud key-value storage에서 사용할 수 있는 총 공간은 사용자당 1MB입니다. 지정할 수 있는 최대 key 수는 1024개이며, key와 연결된 각 value의 크기 제한은 1MB입니다. 예를 들어, 단일 키에 대해 정확히 1MB의 큰 값을 저장하는 경우, 이는 지정된  할당량을 완전히 사용합니다. 각 키에 대해 1KB의 데이터를 저장하는 경우 1000개의 key-value 쌍을 사용할 수 있습니다.
key 문자열의 최대 길이는 UTF8 인코딩을 사용하는 64바이트입니다. 누적 키 문자열의 데이터 크기는 iCloud 키 값 스토리지에 대한 1MB 총 할당량에 포함되지 않으며, 사용자의 키 문자열(최대 64KB 사용)은 사용자의 총 iCloud 할당에 포함됩니다.
앱이 key-value storage의 할당량을 초과한 경우 iCloud 키 값 저장소는 NSUbiquitousKeyValueStoreDidChangeExternallyNotification 알림에 NSUbiquitousKeyValueStoreQuotaViolationChange의 값을 게시합니다.

저장 공간이 엄청나게 크지는 않기 때문에 대단한 데이터를 저장할 수는 없고, 앱의 설정이나 간단한 값 정도만 저장할 수 있다고 보면 된다. 그럼에도 불구하고 NSData 객체를 저장하려고 들면 encoding, decoding의 수고를 들여서 저장을 할 수는 있다. 이에 대한 주의 사항은 다음과 같다.

 

Exercise Care When Using NSData Objects as Values

NSData 객체를 앱의 key-value store에 배치할 수 있지만, 이 작업은 삼가하십시오. 가장 큰 문제는 공간입니다. 앱은 key-value storage에 1MB의 데이터만 저장할 수 있습니다. 데이터 개체가 잠재적으로 클 경우 앱이 스토리지 제한을 빠르게 초과할 수 있습니다. 따라서 1MB 제한에 근접한 경우 대신 해당 데이터에 대한 문서 저장을 고려할 수 있습니다.
대용량 데이터 객체를 약간 변경할 때마다 전체 데이터 객체를 iCloud로 전송해야 합니다. 많은 데이터를 하나의 데이터 객체로 묶는 것보다 해당 데이터를 작은 조각으로 나누고 데이터를 별도로 저장하는 것이 좋습니다. 가급적이면 숫자와 문자열과 같은 투명한 유형을 사용합니다. 다른 유형의 속성 목록 개체를 사용하여 데이터를 보다 세밀하게 저장하면 작은 변경을 수행할 때 전송해야 하는 데이터 양을 최소화할 수 있습니다.
데이터 객체는 사용자 지정 객체이기 때문에 앱에서만 읽을 수 있습니다. 데이터 형식을 변경하면 새 형식을 사용하는 데이터 객체를 읽을 때 이전 버전의 앱이 손상될 수 있습니다. 앱의 이전 버전이 손상되지 않도록 하려면 견고성 및 교차 플랫폼 호환성 설계에서 설명한 대로 데이터 객체에 버전 정보를 포함해야 할 수 있습니다.

결국은 어지간하면 custom data object는 쓰지말고 최대한 작은 단위로 쪼개서 원시타입으로 저장하라는 의미이다. 하지만, 우리 앱에서는 문자열 배열을 사용할 거라 크게 크기를 고려하지는 않았다. 그 외 주의사항은 다음과 같이 말하고 있다.

 

Don’t Use Key-Value Storage in Certain Situations

App Store 또는 Mac App Store에 제출된 모든 앱은 key-value storage를 채택해야 하지만, 일부 유형의 데이터는 key-value storage에 적합하지 않습니다. 예를 들어 document-based app에서는 일반적으로 현재 페이지 또는 현재 선택과 같은 각 문서에 대한 상태 정보에 key-value storage를 사용하는 것이 적절하지 않습니다. 대신, 영구 문서 상태 설계에 설명된 대로 필요에 따라 각 문서와 함께 문서별 상태를 저장하십시오.
또한 오프라인 상태에서 앱 동작에 필수적인 데이터에 key-value storage를 사용하지 않도록 하십시오. 대신 해당 데이터를 로컬 UserDefaults database에 직접 저장하십시오.

아무래도 오프라인의 경우에는 동기화가 일어날 수 없을테니 앱 동작에 필수적인 데이터의 경우에는 UserDefaults를 이용하라는 이야기이고, document-based app에 대한 이야기는 아직 그런걸 안건드려봐서 사실 무슨 말인지는 잘 모르겠다.

 

저장된 시점 중에 어떤 것이 더 최신인지 수동으로 계산해야할까?

물론 아니다. 애플에서 다 똑똑하신 분들께서 만든거라 이것도 자동으로 할 수 있게 해두었다. 우린 그냥 동기화만 하면 된다. 관련 내용을 살펴보면

Resolving Key-Value Conflicts

iCloud 계정에 연결된 장치가 key-value storage에 값을 쓰려고 하면 iCloud는 다른 장치에서 최근에 key-value storage에 변경된 내용이 있는지 확인합니다. 최근에 변경된 내용이 없으면 NSUbiquitousKeyValueStore 객체는 보류 중인 로컬 변경 내용을 서버에 기록합니다. 최근에 변경한 경우 로컬 값을 서버에 쓰지 않습니다. 대신 NSUbiquitousKeyValueStoreDidChangeExternalNotification 알림을 생성하여 업데이트된 서버 값에 따라 앱을 업데이트합니다.

라고 되어있다. 그럼 아까부터 계속 나오는 NSUbiquitousKeyValueStoreDidChangeExternalNotification나 라는 놈은 대체 뭘하는 녀석인가? 

 

마찬가지로 공식문서를 좀 찾아보자.

iCloud에서 push된 들어오는 데이터로 인해 로컬 key-value store에서 하나 이상의 키 값이 변경된 경우 게시됩니다.

Discussion
이 알림은 iCloud에서 변경 사항을 수신한 경우에만 전송되며, 앱에서 값을 설정할 때 전송되지 않습니다.
사용자 정보 사전에는 다음과 같이 알림 이유와 변경된 값 목록이 포함될 수 있습니다:
- NSUbiquitousKeyValueStoreChangeReasonKey*키의 값이 있는 경우 key-value store가 변경된 이유를 나타냅니다. 이 값은 이유 값 변경에 있는 상수 중 하나입니다.
- NSUbiquitousKeyValueStoreChangedKeysKey의 값은 변경된 키의 이름을 가진 문자열의 배열입니다.
notification 객체는 내용이 변경된 NSUbiquitousKeyValueStore 객체입니다.

Important
앱 실행 시퀀스 초기에 NotificationCenter 클래스를 사용하여 didChangeExternalNotification 알림에 등록합니다. 기본 key-value store 객체(기본 클래스 메서드를 사용하여 얻은 객체)를 알림을 받을 개체로 지정합니다.

* NSUbiquitousKeyValueStoreChangeReasonKey : 이 key의 값은 Change Reason Values**를 구독하면서 key-value store가 변화된 이유를 나타낸다.
** Change Reason Values : NSUbiquitousKeyValueStoreChangeReasonKey의 키와 연관될 가능성이 있는 value들

 

사실 지금 위의 내용이 내가 찾던 핵심이다. NSUbiquitousKeyValueStore를 적용하긴 했는데, 앱이 실행될 때 synchronize메서드를 사용했더니 앱과 데이터를 모두 삭제하고 재설치 했을때 데이터가 중간에서 꼬여버리는 현상이 발생했다. 그런데 지금 위의 설명을 읽어보면 syncronize 메서드를 사용할 것이 아니라 NotificationCenter 클래스를 사용해서 didChangeExternalNotification을 통해 값을 업데이트 해야하는 것 같다. 우리의 애플은 친절하게 이 방법에 대해 예시까지 만들어서 보여주고 있으므로 다음 포스팅에서는 그걸 한번 다뤄보겠다.

 

요약

1. NSUbiquitousKeyValueStore는 같은 Apple 계정으로 로그인 된 기기라면, iCloud를 통해 동기화 될 수 있는 key-value를 쌍으로 저장할 수 있는 데이터 저장소이다.

2. NSUbiquitousKeyValueStore는 기기간의 저장된 데이터끼리의 conflict를 막기 위해 NotificationCenter 클래스에 NSUbiquitousKeyValueStoreDidChangeExternalNotification을 등록하여 최근에 업데이트 된 값을 받아와서 쓸 수 있게 만들어놨다.

 

 

참고 링크

https://developer.apple.com/library/archive/documentation/General/Conceptual/iCloudDesignGuide/Chapters/DesigningForKey-ValueDataIniCloud.html#//apple_ref/doc/uid/TP40012094-CH7

 

Designing for Key-Value Data in iCloud

Designing for Key-Value Data in iCloud To store discrete values in iCloud for app preferences, app configuration, or app state, use iCloud key-value storage. Key-value storage is similar to the local user defaults database; but values that you place in key

developer.apple.com

https://developer.apple.com/documentation/foundation/nsubiquitouskeyvaluestore/1412267-didchangeexternallynotification

 

Apple Developer Documentation

 

developer.apple.com

블로그의 정보

Roen의 iOS 개발로그

Steady On

활동하기