
공식 문서에 따르면 유저 디폴트는 '사용자의 기본 설정 데이터베이스에 대한 인터페이스로, 앱이 실행되는 동안 지속적으로 키-값 쌍을 저장하는 곳'이다. 즉, 사용자의 기본 설정 데이터베이스에 접근할 수 있도록 도와주는 도구이다.
사용자의 기본 설정이라는 것이 무엇일까. 여기서 사용자는 앱의 사용자를 말하며, 기본 설정이란 다크 모드처럼 앱 테마나 알림을 받을지 말지에 대한 알림 설정, 사용자가 앱에서 사용하는 언어 등을 말한다.
즉 유저 디폴트는 앱의 테마나 알림, 언어 설정 등의 기본 설정에 접근할 수 있게 해주는 인터페이스이다.
아래 사용법과 보면 이해가 더 빠를 것이다.
유저 디폴트를 통해 위와 같은 사용자 설정에 접근하는 방법을 알아보자.
import Foundation
// 테마 설정 저장
func saveThemePreference(isDarkMode: Bool) {
UserDefaults.standard.set(isDarkMode, forKey: "isDarkMode")
}
// 테마 설정 불러오기
func loadThemePreference() -> Bool {
return UserDefaults.standard.bool(forKey: "isDarkMode")
}
// 사용 예시
saveThemePreference(isDarkMode: true) // 다크 모드 설정 저장
let isDarkModeEnabled = loadThemePreference()
print("다크 모드 활성화: \(isDarkModeEnabled)")
유저 디폴트를 이용해 사용자 기본 설정을 바꾸고 불러오는 사용자 정의 함수이다.
공식 문서에도 나와있듯이 유저 디폴트는 Foundation 프레임워크에 속한 클래스이기에 Foundation을 import해줘야 한다.
UserDefaults.standard.set를 이용해 사용자 설정을 바꿀 수 있고, UserDefaults.standard.bool을 통해 사용자 설정을 불러올 수 있다.
이 외에 유저 디폴트로 간단한 데이터를 저장하는 예를 알아보자.
import Foundation
// UserDefaults 인스턴스 가져오기
let userDefaults = UserDefaults.standard
// 데이터 저장
userDefaults.set("Hello, World!", forKey: "greeting")
// 데이터 불러오기
if let greeting = userDefaults.string(forKey: "greeting") {
print(greeting) // 출력: Hello, World!
}
먼저 유저디폴트 인스턴스를 생성한 다음, 키-값의 형식으로 데이터를 set하면 된다. 불러올 때는 키값을 이용해 불러오면 된다. 우리가 저장한 키값이 스트링이니 userDefaults.standard.string("키값")으로 값을 불러왔다.
만약 키값을 Int형식으로 저장했다면 UserDefaults.standard.integer(forKey: "myInteger")으로 값을 불러오면 된다.
유저 디폴트를 이용해 저장한 데이터는 앱의 샌드박스 내의 Library/Preferences 디렉토리에 저장된다. 파일 이름은 bundle identifier.plist(프로퍼티 리스트) 형식으로 저장되는데, 만약 앱의 번들 식별자가 com.example.MyApp이면 com.example.MyApp.plist라는 파일이 생성되는 것이다.
.plist는 macOS와 iOS에서 사용되는 데이터 저장 형식으로, 주로 설정 정보, 사용자 기본 설정, 애플리케이션 데이터 등을 저장하는 데 사용된다. .plist의 내용은 XML 또는 바이너리 형식으로 표현된다.
UserDefaults의 plist파일과 프로젝트의 info.plist파일
왠지 모르게 .plist의 형식이 매우 익숙했는데, 다름이 아니라 xCode에서 프로젝트를 만들면 항상 디렉토리에 있던 info.plist때문이었다. 예전에 프로젝트를 진행하면서 info.plist에서 카메라, 앨범, 사용자 위치 접근 권한을 설정해준적이 있는데 이 info.plist는 사용자의 기본 설정이 아닌 앱의 기본 설정을 관리하는 파일이다.
유저 디폴트로 저장한 데이터는 앱의 샌드박스 내에 저장되기 때문에 앱이 삭제되면 같이 삭제되지만, 앱이 삭제되지 않는 한 계속 유지된다.
또한 간단한 사용자 정보를 저장하기 때문에 문자열, 숫자, 불리언, 배열, 사전 등의 기본 데이터 타입을 지원한다.
iOS에서 데이터 모델을 관리하고 데이터를 영구적으로 저장하기 위한 프레임워크이다.

공식 문서를 확인해보면 코어 데이터의 역할을 '하나의 디바이스에서 데이터를 영구적으로 저장 또는 캐시하거나, CloudKit을 사용하여 여러 디바이스 간에 데이터를 동기화한다'라고 소개하고 있다.
말 그대로 여러 디바이스 간 데이터를 동기화하거나, 한 디바이스에서 데이터를 영구적으로 저장하는 역할이다.
코어 데이터는 유저 디폴트와 마찬가지로 샌드박스 내 SQLite 데이터베이스 파일로 저장되기 때문에 앱을 종료하더라도 데이터가 유지된다.
하지만 저장경로와 확장자는 다른데, 유저 디폴트는 샌드박스의 Preferences 디렉터리에 저장되었지만 코어 데이터는 샌드박스내 Documents에 저장된다. 파일 확장자는 .sqlite인데 내용은 SQLite, XML, 바이너리 형식으로 작성될 수 있다.
한 디바이스에서 데이터를 수정하면 이 내용이 앱의 샌드박스에 저장되고, CloudKit를 통해 같은 앱을 사용하는 다른 디바이스의 샌드박스에 동기화된다.

이를 순서대로 풀어서 설명해보자면
1. 로컬 데이터 수정
사용자가 디바이스에서 코어 데이터를 이용해 데이터를 수정하면, 이 내용이 앱 샌드박스 내 데이터베이스에 저장된다.
2. CloudKit를 통한 동기화
수정된 데이터는 CloudKit을 통해 클라우드로 업로드된다. CloudKit은 변경 데이터를 서버에 저장하고, 다른 디바이스와 동기화할 준비를 한다.
3. 다른 디바이스에서 동기화
다른 디바이스에서 동일할 앱이 실행될 때 CloudKit과 통신해 클라우드에 저장된 변경사항을 가져온다. 이 내용은 앱의 샌드박스에 있는 코어 데이터 저장소에 저장된다.
코어 데이터는 앱의 데이터 구조를 정의한다. 아래 3가지 요소로 데이터 구조를 정의할 수 있다.
import Foundation
import CoreData
@objc(User)
public class User: NSManagedObject {
@NSManaged public var name: String?
@NSManaged public var gender: String?
@NSManaged public var age: Int16
@NSManaged public var posts: NSSet? // Post와의 관계
}
@objc(Post)
public class Post: NSManagedObject {
@NSManaged public var title: String?
@NSManaged public var user: User? // User와의 관계
}
여기서 NSSet은 엔티티 간의 관계를 표현할 때 주로 사용되는 타입이다.
1. Entity(엔티티)
데이터베이스의 테이블처럼 특정 종류의 데이터를 나타낸다. 예를 들어 사용자 테이블, 게시물 테이블이 있는 것처럼 엔티티도 사용자 엔티티, 게시물 엔티티를 정의할 수 있다.
2. Attribute(속성)
엔티티가 가지는 특성과 정보를 저장한다. 사용자 엔티티는 이름, 나이, 성별 등의 속성을 가질 수 있다.
3. Relationship(관계)
엔티티 간의 관계를 정의한다. 사용자 엔티티와 게시물 엔티티 간의 관계를 정의할 수 있는데 한 사용자가 여러 개의 게시물을 작성할 수 있도록 정의할 수 있다. 관계는 일대일, 일대다, 다대다로 설정할 수 있다.
4. 데이터 모델 파일

사진은 이번에 진행한 프로젝트에서 팀원분이 만들어주신 모델이다. 감싸합니닷!
Xcode에서 .xcdatamodeld 파일을 사용하여 데이터 모델을 시각적으로 설계할 수 있다. 사진처럼 엔티티, 속성, 관계를 정의하고 관리할 수 있다.
코어 데이터는 메모리 내에서 객체 간의 관계를 관리한다. 데이터 모델을 아래와 같이 정의하고 객체 그래프를 관리하는 기능에 대해 알아보자.
import Foundation
import CoreData
@objc(User)
public class User: NSManagedObject {
@NSManaged public var name: String?
@NSManaged public var gender: String?
@NSManaged public var age: Int16
@NSManaged public var posts: NSSet? // Post와의 관계
}
@objc(Post)
public class Post: NSManagedObject {
@NSManaged public var title: String?
@NSManaged public var user: User? // User와의 관계
}
1. 객체 생성 및 초기화
import CoreData
// 1. NSManagedObjectContext 가져오기
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
// 2. User 엔티티의 새로운 객체 생성
let newUser = User(context: context) // User는 NSManagedObject의 서브클래스
// 3. 속성 초기화
newUser.name = "홍길동" // name 속성에 값 설정
newUser.gender = "남성" // gender 속성에 값 설정
newUser.age = 30 // age 속성에 값 설정
// 4. 데이터 저장
do {
try context.save() // 컨텍스트를 저장하여 영구 저장소에 반영
print("사용자 정보가 저장되었습니다.")
} catch {
print("저장 오류 발생: \(error)")
}
엔티티에 해당하는 객체를 쉽게 생성할 수 있다. 새로운 사용자를 추가한다면 NSManagedObject를 생성하고 속성을 설정하면 된다.
2. 객체 간의 관계 관리
import CoreData
// 1. NSManagedObjectContext 가져오기
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
// 2. User 객체 생성
let newUser = User(context: context)
newUser.name = "홍길동"
// 3. Post 객체 생성
let newPost1 = Post(context: context)
newPost1.title = "첫 번째 게시물"
// 4. Post 객체를 User에 추가
newUser.addToPosts(newPost1) // User의 posts 관계에 새 게시물 추가
let newPost2 = Post(context: context)
newPost2.title = "두 번째 게시물"
newUser.addToPosts(newPost2) // 또 다른 게시물 추가
// 5. User의 posts 속성 확인
print("사용자 \(newUser.name!)의 게시물:")
if let posts = newUser.posts as? Set<Post> {
for post in posts {
print(post.title!)
}
}
// 6. 데이터 저장
do {
try context.save() // 변경 사항 저장
print("사용자와 게시물이 저장되었습니다.")
} catch {
print("저장 오류 발생: \(error)")
}
객체 간의 관계를 정의하고 유지한다. 한 사용자가 여러 게시물을 작성한 경우에는 사용자 객체와 여러 게시물 객체 간의 연결을 자동으로 관리한다.
3. 변경 감지
객체의 상태 변화를 감지하고 이를 여구 저장소에 반영한다. 사용자의 속성을 수정하면 코어 데이터는 이 변경 사항을 추적해 적절한 시점에 저장한다.
4. 페치(Fetching)
import CoreData
// 1. NSFetchRequest 생성
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "User")
// 2. NSPredicate 설정: 성별이 여성인 조건 추가
fetchRequest.predicate = NSPredicate(format: "gender == %@", "여성")
// 3. 데이터베이스에서 데이터 가져오기
do {
let users = try context.fetch(fetchRequest)
// users 배열에는 성별이 여성인 사용자 객체들이 담김
} catch {
print("데이터를 가져오는 데 실패했습니다: \(error)")
}
NSFetchRequest로 특정 조건에 맞는 객체를 검색할 수 있다. 성별이 여성인 사용자를 검색할 수 있는 것이다.
위의 내용을 보면 알 수 있듯이, 코어 데이터는 데이터베이스가 아니라 데이터 모델링 및 관리 '프레임워크'이다. 데이터 모델을 정의하고 관리하고, 객체 간의 관계를 관리, 객체의 생성, 수정, 삭제를 쉽게 처리할 수 있게 해준다.
또한 MySQL같은 DBMS를 사용해봤다면 알 수 있듯이, 데이터베이스를 설계할 때처럼 세세하게 구현해야 하는 것이 아니라 개발자가 데이터베이스의 세부 구현에 신경 쓰지 않고도 데이터를 관리할 수 있게 한다.
코어 데이터는 데이터 모델링과 객체 그래프 관리 및 영구 저장 기능에 중점을 둔다.
오늘은 유저 디폴트와 코어 데이터가 무엇이고 어떤 역할을 하는지 알아보았습니다!
다음 글에서는 코어 데이터의 자세한 사용에 대해 알아보도록 하겠습니다~!
참고
https://f-lab.kr/insight/local-data-storage-options-in-ios
https://velog.io/@kirri1124/UserDefaults-%EC%99%80-CoreData