아요 개발 일기

iOS 데이터 저장 방식 알아보기 - UserDefaults 편 본문

iOS

iOS 데이터 저장 방식 알아보기 - UserDefaults 편

김망복 2024. 11. 4. 22:15
반응형

안녕하세요~~

이번 글에서는 UserDefaults 공식 문서를 참조하여 UserDefaults의 개념과 특징 및 주의 사항들에대해 알아보도록하겠습니다! 

사용법이 조금 간단해서 글을 하나로 빼는게 애매하다는 생각을 했는데..

그래도 중요하고, 자주 사용하기에!!(거의 모든 앱에서 필수로 사용하는 듯합니다)

 

혹시 데이터 저장 방식에대해 처음 접하거나, 전체적으로 한번 훑고 싶으신 분들은

iOS 데이터 저장 방식 알아보기 - 개념?편 을 참고해주세요!


UserDefaults

앱이 시작할 때의 기본상태나 기본 동작을 결정하는데 사용되므로 UserDefaults라고 불립니다.

앱이 다시 시작되더라도 데이터가 유지되며, 네트워크 연결이 필요하지 않아 사용이 매우 간편합니다.

특징

- 네트워크 연결이 필요하지 않고, 앱이 시작될 때 주요 설정을 메모리에 캐싱

- 키-값 쌍으로 데이터를 저장

- 보안이 중요한 데이터(예: 비밀번호)저장에는 적합하지 않음

💡 캐시/ 캐싱이 뭐에요?
캐시: 데이터를 저장하는 공간, 저장된 데이터 자체
캐싱: 데이터를 캐시에 저장하는 행위나 과정

 

 

잠깐! 앱이 시작될 때 주요 설정을 메모리에 캐싱한다??? 주요 설정이 어디있는데???

자세히 알아보기위해 UserDefults 데이터를 저장하고 사용하는 방식에대해서 살펴봅시다!!


UserDefaults 데이터는 어떻게 사용하고, 저장할까?

 

앱이 시작될 때, (데이터 사용할 때)

1. UserDefaults는 영구 저장소(파일 시스템)에 있는 Plist 파일에서 데이터를 읽어옴

2. 메모리(캐시)에 로드

3. 앱에서 사용

 

 

앱에서 UserDefault 데이터를 저장할 때,

1. 메모리에 즉시(동기적) 반영

2. 비동기적으로 영구 저장소(파일 시스템)에 있는 Plist 파일에 저장 됨

 

 

 

오잉?? 데이터를 저장할 때 바로 영구 저장하는게 아니네요??

성능적인 이유때문에 메모리에 올려두고 나중에 영구 저장소에 비동기적으로 반영하는 것인데..

 

🤔 그러면 만약에 메모리(캐시)에만 올라간 상태로 사용자가 앱을 강제 종료한다면 데이터가 사라질 수 있는거 아닌가??

맞습니다! 그래서

 

1. 즉시 영구 저장 요청: syschronize()메서드를 이용해 비동기 작업을 강제로 완료 시킬 수 있지만,,

이 메서드는 성능 문제로 인해 형재는 Deprecated(사용 중단) 상태입니다

 

2. 데이터 무결성 설계: 중요한 데이터는 CoreData나 파일 시스템에 직접 저장하거나,

별도의 저장 방식으로 추가적인 백업 고려

 

등등 UserDefaults를 사용할때는 비동기 파일 저장 전에 데이터가 날아갈 수 있음을 염두하여 사용해야합니다.

그래서 비밀번호같은 중요한 정보 저장할 때는 사용하면 안되겠지요?

 

이번에는 공식 문서에있는 설명을 쭉 훑어보겠습니다!


기본 객체 저장

UserDefaults 클래스는 float, double, 정수, Boolean, URL 등 간단한 타입은 쉽게 저장할 수 있지만, 다른 복잡한 객체는 NSData로 변환 후 저장해야 합니다!

 

즉, UserDefaults에 저장하려면 객체가 속성 리스트(Property List)에 속해야 하며, NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary 인스턴스이거나, 이들의 조합으로 이루어진 컬렉션이어야 합니다.

💡속성 리스트: 키-값 쌍의 구조로 데이터를 저장하는 형식

 

간단한 변환 예시입니다!

let customData = CustomData(name: "Sojin", age: 25)
if let encodedData = try? JSONEncoder().encode(customData) {
    UserDefaults.standard.set(encodedData, forKey: "customData")
}

 

UserDefaults의 불변성

UserDefaults에서 반환된 값은 불변(immutable) 즉, 변경할 수 없는 형태 입니다.

공식 문서는 설명이 좀 복잡?해서 제가 쉽게 예를 들어보면,

name이라는 변수를 사용해서 UserDefaults에 값을 저장했을때, name 변수의 값을 변경해도 set(_:forKey:)를 사용하지 않으면 UserDefaults에 값이 반영되지 않는다! 로 정리할 수 있겠습니다!!

 

UserDefaults 값 변경 시 자동 알림 받기

UserDefaults 값이 변경될 때 자동으로 알림을 받아 특정 처리를 하고 싶다면,

키-값 관찰(Key-Value Observing)을 사용할 수 있습니다.

 

아래 예시 코드 처럼 기본 알림 센터의 didChangeNotification에 옵저버로 등록해 로컬 기본 설정 데이터베이스의 모든 업데이트에 대한 알림을 받으면 됩니다!!

import Foundation

// 옵저버 등록
NotificationCenter.default.addObserver(
    self,
    selector: #selector(defaultsDidChange),
    name: UserDefaults.didChangeNotification,
    object: nil
)

// 알림을 받을 때 실행될 메서드
@objc func defaultsDidChange(notification: Notification) {
    // UserDefaults의 변경 사항에 대응하는 작업 수행
    print("UserDefaults 값이 변경되었습니다!")
}

 

파일 참조의 지속성

파일 URL 값은 파일 시스템의 위치를 나타냅니다.

그 특정 파일의 위치를 set(_:forKey:)를 이용해 저장하면 사용자가 파일을 이동시켰을 때 다음 실행 시 파일을 찾지 못할 수 있습니다

 

그렇다면 어떻게 사용하느냐??

바로 파일 시스템 ID로 파일을 참조하면 됩니다! 방법은 어렵지 않아요

bookmarkData(options:includingResourceValuesForKeys:relativeTo:) 메서드를 사용해 NSURL북마크 데이터를 생성한 값을 저장하면 됩니다!

그럼 사용할 때는 반대로 파일 URL로 변환해야겠죠?

그때는 URLByResolvingBookmarkData를 사용하여 북마크 데이터 -> 파일 URL로 변환하여 사용합니다.

 

샌드박스 환경 고려 사항

UserDefaults는 샌드박스 규칙을 따르므로 다른 앱의 기본 설정에 접근하거나 수정할 수 없습니다.

만약, 앱 간 데이터 공유가 필요하다면 앱 그룹을 설정하고 addSuite(named:)를 사용해 앱 그룹 내에서 데이터를 공유할 수 있습니다.

 

+ 아래의 경우도 데이터 공유가 가능합니다

  • macOS 및 iOS의 앱 확장 프로그램
  • macOS의 같은 애플리케이션 그룹에 속한 앱

⚠️ 주의 사항 ⚠️

 

 

환경 설정 하위 시스템에 직접 접근하지 마세요.
환경 설정 속성 리스트 파일을 수정하면 변경 사항이
손실되거나, 변경 반영이 지연되거나, 앱이 충돌할 수 있습니다.

환경 설정을 구성하려면, macOS에서 defaults 명령줄 유틸리티를 대신 사용하세요.

 

 

해석해보면 뭔가 복잡한 느낌인데, 먼저 여기서 말하는 환경 설정 속성 리스트 파일(preference property list files)은 Plist 파일입니다!

Plist 파일을 잘 모르시는 분중에 프로젝트를 한 번이라도 만들어 보셨다면 자기도 모르게 건들여봤을 수 있어요!!

여기 프로젝트에 있는 Info 부분이 Plist 파일 형식을 따르고 있습니다. 즉, Info.plist 파일인거죠!

(Xcode 13 이전에는  Info.plist 파일로 따로 있었는데, 프로젝트 설정?탭으로 이동했어요!)

 

Plist이 뭔데요????

Plist란, Property List의 약자로 macOS와 iOS에서 환경설정, 구성 데이터, 기본 설정 정보를 저장하기 위해 표준화된 파일 형식입니다. 파일 형식은 키-값 쌍으로 데이터를 저장하며, XML 또는 바이너리 형태로 저장되어 다양한 데이터 유형(String, Number, Boolean 등)을 다룰 수 있습니다.

 

위에서 UserDefaults의 특징에 있던 키-값 쌍 으로 데이터는 저장하는 이유가 plist 파일 형식이기때문이겠죠?

 

그러면, 왜 직접 접근하지 말라는건데?

UserDefaults에 값을 저장하면 메모리(캐시)에 즉시 저장된다고 했잖아요??

이때는 메모리(캐시)에서만 저장된 상태이고 영구 저장소에 저장되지는 않은 상태입니다!

 

영구 저장소인 Plist파일에는 데이터가 비동기적으로 저장되기때문에, Plist파일의 정보와 메모리(캐시)에 반영된 정보가 일치하지 않을 수 있습니다.

이때 plist파일을 임의로 수정하게되면 데이터 충돌이나 손실이 발생할 수 있기때문에 직접 수정하지 말라는 것이었습니다!!


예시 코드

자 어느정도 UserDefaults에 대해서 알아보았으니, 코드를 보면서 직접 사용해볼까요?

제가 작성한 예시는 세가지이고, 사실 코드는 아주아주 쉽습니다..

 

1. 강제 종료 시 마지막 탭 저장

// 값 저장
UserDefaults.standard.set(tab, forKey: "SelectedTab")

// 값 가져오기
selectedTab = UserDefaults.standard.integer(forKey: "SelectedTab")

 

2. 이름(회원 정보) 저장

// 값 저장
UserDefaults.standard.set(name, forKey: "UserName")
 
// 값 가져오기
let savedName = UserDefaults.standard.string(forKey: "UserName")

 

3. 앱 자체의 다크 모드/ 라이트 모드 확인

// 값 저장
UserDefaults.standard.set(isDarkMode, forKey: "isDarkMode")
 
// 값 가져오기
var isDarkMode: Bool = UserDefaults.standard.bool(forKey: "isDarkMode")

 

정말 간단하죠???


마무리

이렇게해서 UserDefaults에 대해서 쭉 훑어보았는데 어떠셨나요?!!

뭔가 쓸게 없을 줄 알았는데 은근 많네요..ㅎㅎㅎㅎㅎㅎ

혹시 수정해야할 부분이나 궁금한 부분이 있으시면 편하게 댓글 달아주세요!!

읽어주셔서 감사합니다🙇🏻‍♀️

반응형