안녕하세요. 이번 포스팅은 iOS에서 프로퍼티 리스트를 이용하여 간단하고 적은 양의 데이터를 저장하는 방식에 대해 정리하도록 하겠습니다. (대량의 데이터를 관리하기에 프로퍼티 리스트는 적합하지 않습니다.)
프로퍼티 리스트, Plist
- *객체 직렬화를 위한 XML 형식의 파일
- 애플 왈: 간단한 데이터 계층을 표현하기 위한 *추상화
- 단순한 데이터를 XML 포맷(dictionary)으로 저장하는것
- 앱 배포 파일(ipa 파일)에 반입되었다가, 사용자의 기기에 앱이 설치될때 App Bundle에 함께 추가
*객체 직렬화 : 객체 내용을 바이트 단위로 변환하여 파일에 기록하거나 네트워크를 통해 전달이 가능하도록 하는 것
*추상화 : 구체화의 반대, 일체의 개별적인 특성을 배제하고 공통성을 띄는것 여기서 데이터 자체를 추상화 한다는 것이 x, 데이터의 타입을 추상화 → 이후 어느 타입으로든 구체화 가능함.
UserDefaults
- 런타임에서 동작하는 객체로, 앱이 실행되는 동안 기본 저장소에 접근하여 데이터를 가져오고 기록
- UserDefaults 객체는 데이터 저장을 위해 내부적으로 Plist 사용 (데이터를 읽거나 쓰기 위해 Plist를 직접 참조할 필요 없음) → UserDefaults의 standard 속성을 이용하여, 표준 사용자 기본 저장소에 데이터를 write, read
- 싱글톤 패턴으로 설계되어있기 때문에, 앱 전체에서 딱 하나의 인스턴스만 생성되며 이것을 앱 전체에서 공유해서 사용 (대부분의 프로퍼티, 메소드 → 클래스 프로퍼티, 클래스 메소드)
- 데이터 read → optional 타입 (nil인 경우: 해당 key의 데이터 존재하지 않음, 타입 캐스팅 실패, nil 값 저장)
- *블로킹 알고리즘으로 설계되어 있어 Thread-safe
- *인메모리 캐싱 메커니즘 사용
- 장점: 메모리에 캐싱해두고 사용하기 때문에 성능상 유리
- 단점: 저장소와 메모리 간에 동기화를 위해, 데이터를 저장한 후 반드시 동기화 처리(synchronize 메소드 호출)를 해주어야함
*블로킹 알고리즘 : 하나의 공유되는 자원에 여러 객체가 동시에 접근할때, 먼저 들어온 요청에 우선권을 주어 잠금을 걸고 데이터를 읽고 쓰는 일련의 과정이 끝날 때까지 다음 요청의 접근을 차단. 해당 요청이 모두 처리가 되면 잠금 해제 후 다음 요청을 받고 다시 잠금의 과정 반복
*인메모리 캐싱 : 저장소에서 데이터를 매번 새로 읽는 것이 아닌 한번 읽은 데이터를 메모리에 저장해두고 사용하는 방식
UserDefaults 사용법
let plist = UserDefaults.standard
plist.setValue(value, forKey: "name") // "name" key로 value 저장
plist.synchronize() // 데이터 동기화처리
self.name.text = plist.string(forKey: "name") // 데이터 read
Custom Plist
- 편하게 쓸 수 있는 UserDefaults가 아닌 Custom Plist를 쓰는 이유
- API Key 등 Private 하게 관리해야하는 데이터의 경우, 코드에 나타나지 않게 저장을 하기 위해 UserDefaults 대신 직접 생성한 커스텀 Plist 파일에 데이터를 저장
- 비슷한 형식의 데이터 그룹 반복 (UserDefualts에서 중복된 키 저장 불가 → Custom Plist의 파일 분리로 해결)
- 일부 기능에 국한된 데이터, 그룹을 묶어서 저장할 방대한 데이터 (앱 공통의 단순 데이터라면 UserDefaults)
- Custom Plist는 애플리케이션 내부 샌드박스에 저장 (iOS는 보안의 이유로 애플리케이션이 파일을 읽고 쓸 수 있는 범위를 제한)
- 장단점
- 장점: 데이터를 분리하여 저장할 수 있음
- 단점: UserDefaults에서 기본으로 제공해주는 메소드가 없음. 따라서 데이터를 저장하려면, 딕셔너리 객체를 이용하여 데이터 정의 후 이를 파일에 직접 작성해야함. 읽을 때도 마찬가지로 파일 내용 전체를 읽어들인 후 딕셔너리 객체로 변환해야함
Custom Plist 사용법
// 1. 커스텀 Plist 읽기 (샌드박스 안 문서나 데이터 전용 디렉터리 안)
// NSSearchPathForDirectoriesInDomains 함수: 해당 애플리케이션과 연관된 디렉터리 정보를 반환
// 첫번째 parameter: 문서 관련 디렉터리 지정
// 두번째 parameter: 애플리케이션 범위 지정
// 세번째 parameter == true ? 전체 디렉터리 경로 반환 : 전체 경로 생략, 필요한 디렉터리 명만 반환
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) // [String] 타입 -> 검색 조건에 부합하는 디렉터리 여러 개
let path = paths.first! as NSString
let plist = path.strings(byAppendingPaths: ["data.plist"]).first! // 기존 경로에 하위 경로 붙임
let data = NSMutableDictionary(contentsOfFile: plist) // plist 경로의 파일 dictionary에 담기
// NSDictionary(contentsOfFile: plist)로 작성하면 데이터 수정 불가. 따라서 read만 필요할땐 NSDictionary.
// 2. 데이터 read
let name = data?.value(forKey: "name") as? String
let age = data?.value(forKey: "age") as? String
// 3. 데이터 write
// 마찬가지로 같은 key이면 기존 value 덮어씀. (수정)
data?.setValue("hi", forKey: "test") // 메모리에만 저장
data?.write(toFile: plist, atomically: true) // atomically = 임시 파일 생성 여부 결정
'iOS > 개념' 카테고리의 다른 글
팀 세팅 작업 : Certificate, Provisioning profile 생성 및 등록 과정 정리 (3) | 2022.05.10 |
---|---|
Test 코드 작성하기 : UnitTest, Nimble, RxTest, RxBlocking (0) | 2022.02.15 |
[곰튀김 RxSwift + MVVM 시즌2 - 2교시] RxSwift 활용하기 - 쓰레드의 활용과 메모리 관리 (0) | 2021.12.29 |
[곰튀김 RxSwift + MVVM 시즌2 - 1교시] RxSwift를 사용한 비동기 프로그래밍 (0) | 2021.12.26 |
[RxSwift] Operator 연습 4탄 : Time Based Operator (0) | 2021.12.14 |