내부 저장소란?
앱 종료와 함께 사라지지 않는, 앱이 기억하고 있어야 할 비휘발성 데이터를 저장하기 위해 사용되는 개념
디바이스에 저장되기에 많은 데이터를 저장하기에는 부적합
1. Device에 데이터를 저장하는 여러가지 방법들
1-1. UserDefaults
UserDefaults는 간단한 Key-Value 데이터를 저장하는데 사용
주로 설정 값이나 사용자 기본 설정과 같은 작은 데이터를 저장할 때 사용
(여기서 key값은 반드시 'String' 타입)
// 데이터 추가 및 업데이트
UserDefaults.standard.set(value, forKey: key)
// 데이터 조회 | Any? 타입이여서 as를 사용한 타입 변환 후 사용
UserDefaults.standard.value(forKey: key)
// 데이터 조회 | 특정 타입으로 반환할 경우 (String)
UserDefaults.standard.string(forKey: key)
// 데이터 삭제
UserDefaults.standard.removeObject(forKey: key)
1-2. KeyChain
iOS 앱에서 보안 정보를 안전하게 저장하고 관리하기 위한 메커니즘
키체인은 사용자의 비밀번호, 토큰, 인증서, 민감한 데이터와 같은 보안 관련 정보를 저장하는데 사용
키체인은 암호화 → 앱 외부에서 쉽게 접근불가
사용자 데이터 안전성 보장
// ADD
// Keychain에 저장할 데이터를 추가하는 쿼리 생성
let addQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword, // Keychain 내에서 다루고자 하는 데이터의 유형을 지정
kSecAttrService as String: "service", // 데이터를 저장하는 서비스나 앱을 식별하는데 사용
kSecAttrAccount as String: "key", // Keychain에서 데이터를 찾거나 식별하는데에 사용
kSecValueData as String: "data" // 실제로 Keychain에 저장될 데이터를 의미
]
// 데이터를 Keychain에 추가
let addStatus = SecItemAdd(addQuery as CFDictionary, nil)
// GET
// Keychain에서 데이터를 조회 위한 쿼리 생성
let getQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword, // Keychain 내에서 다루고자 하는 데이터의 유형을 지정
kSecAttrService as String: "service", // 데이터를 저장하는 서비스나 앱을 식별하는데 사용
kSecAttrAccount as String: "key", // Keychain에서 데이터를 찾거나 식별하는데에 사용
kSecReturnData as String: kCFBooleanTrue, // Keychain에서 데이터를 가져올 때 반환할 데이터의 형태를 지정
kSecMatchLimit as String: kSecMatchLimitOne // 일치하는 항목의 수를 제한하는데 사용
]
// 데이터를 Keychain에서 조회
var item: CFTypeRef?
let getStatus = SecItemCopyMatching(getQuery as CFDictionary, &item)
// UPDATE
// Keychain에서 데이터를 업데이트하기 위한 쿼리 생성
let updateQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword, // Keychain 내에서 다루고자 하는 데이터의 유형을 지정
kSecAttrService as String: "service", // 데이터를 저장하는 서비스나 앱을 식별하는데 사용
kSecAttrAccount as String: "key", // Keychain에서 데이터를 찾거나 식별하는데에 사용
]
// 업데이트할 데이터로 속성 설정
let attributes: [String: Any] = [
kSecValueData as String: "newData"
]
// 데이터를 Keychain에서 업데이트
let updateStatus = SecItemUpdate(updateQuery as CFDictionary, attributes as CFDictionary)
// DELETE
// Keychain에서 데이터를 삭제하기 위한 쿼리 생성
let deleteQuery: [String: Any] = [
kSecClass as String: kSecClassGenericPassword, // Keychain 내에서 다루고자 하는 데이터의 유형을 지정
kSecAttrService as String: "service", // 데이터를 저장하는 서비스나 앱을 식별하는데 사용
kSecAttrAccount as String: "key", // Keychain에서 데이터를 찾거나 식별하는데에 사용
]
// 데이터를 Keychain에서 삭제
let deleteStatus = SecItemDelete(deleteQuery as CFDictionary)
1-3. 파일 시스템
앱 내부 또는 외부 파일 시스템에 데이터 저장 가능
(주로 텍스트, 이미지, 동영상 등의 파일 데이터를 저장할 때 사용)
// WRITE
let fileName = "example.txt"
let content = "이것은 파일에 쓰여진 내용"
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(fileName)
do {
try content.write(to: fileURL, atomically: false, encoding: .utf8)
print("파일이 성공적으로 생성됨.")
} catch {
print("파일 생성 중 에러가 발생: \(error.localizedDescription)")
}
}
// READ
let fileName = "example.txt"
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(fileName)
do {
let content = try String(contentsOf: fileURL, encoding: .utf8)
print("파일 내용: \(content)")
} catch {
print("파일 읽기 중 에러가 발생: \(error.localizedDescription)")
}
}
1-4. Core Data
iOS 앱에서 데이터를 관리 → 영구적으로 저장에 사용되는 프레임워크
데이터 모델을 정의 → 데이터를 읽고 사용
* Core Data 사용 설정하기 *
Core Data를 사용을 위해 기본적인 프로젝트 설정방법
( 프로젝트 생성 시, "Core Data"를 선택하였다면 해당 단계 건너뛰기 가능)
* 데이터 모델 만들기 *

1-5. Core Data Stack 설정
"AppDelegate.swift" 파일 → "Core Data 스택(Stack)"을 설정가능
"NSPersistentContainer" 를 생성 시 "name" → 앞서 생성한 "Data Model"의 파일명과 동일하게 입력
import CoreData
// Core Data 스택
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: /* 생성한 Data Model 명 */)
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext() {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
* Core Data Stack의 개념 *

* Managed Object Model (NSManagedObejctModel) *
↓
애플리케이션의 데이터 모델을 정의하는 객체
엔티티(Entity)와 그들의 속성들을 정의 → 이러한 모델은 데이터베이스 스키마에 해당
* Managed Object Context (NSManagedObjectContext) *
↓
애플리케이션의 데이터를 가져오거나 수정하는데 사용되는 객체
(주로 메모리에서 데이터를 관리하고 데이터베이스와 상호 작용)
* Persistent Store Coordinator (NSPersistentStoreCoordinator) *
↓
영구 저장소를 관리, 데이터베이스와의 통신을 담당하는 객체
SQLite, XML, Binary 등 다양한 데이터 저장소 유형과의 연결을 관리
* Persistent Store (NSPersistentStore) *
↓
실제로 데이터를 영구적으로 저장하는데 사용되는 데이터베이스 파일 or 다른 형태의 저장소
2. Data Model 파일 구성 (데이터 모델링)
'Data Model' 파일의 구성요소를 이해 → 저장할 데이터 구조를 모델링
2-1 Entity (엔티티)
데이터베이스의 테이블과 비슷한 개념인 'Entity(엔티티)'는
데이터 모델 내에서 객체의 유형을 정의
해당 객체들이 갖는 속성들을 나타냄
2-2. Entity(엔티티) 추가
편집기 영역 하단에서 'Add Entity' 를 클릭 시 'Entity'가 생성 → 'Entities List'에 'Entity' 표시
↓
'Entities List(엔티티 리스트)' 에서 새로 추가된 개체를 두 번 클릭
↓
이름변경
(이 단계에서 데이터 모델 검사기에 표시되는 'Entity(엔티티)'이름과 클래스 이름을 모두 업데이트)
'View' - 'Inspectors' - 'Show Data Model Inspector'를 사용
↓
'Data Model Inspector'를 열어, 'Entity'를 구성가능
2-3. Entity 주요 속성들
Entity Name | Abstract Entity | Parent Entity | Class Name | Module |
'Entity' 이름 | 해당 'Entity'를 추상(Abstract) 'Entity'로 만들 경우 ↓ 해당 옵션 선택 |
유사 "Entity"가 여러 개 있는 경우 ↓ 'Parent Entity'를 정의 ↓ 공통 속성 정의 |
이 'Entity'의 인스턴스 생성 시 ↓ 사용할 'Class Name' |
'Entity class'가 존재 하는 모듈 |
'Entities List' ↓ 표시된 이름과 동일 |
기본적으로 이 옵션은 선택 X ↓ 데이터베이스에 실제로 저장되는 'Entity'의 형태 ↓ 'concrete Entity'가 생성 |
'Child Entity' ↓ 해당 속성 상속가능 (기본적으로 이 필드는 비어있음) |
기본적으로 'Class Name'은 'Entity'의 이름 ↓ 미러링 |
기본적으로 'Core Data' ↓ 전역 네임 스페이스 클래스 파일을 찾음 |
'Abstract Entity'는 상속 통해 확장 ↓ 공통적 특성을 가진 여러 'Entity' ↓ 효율적 관리가능 |
반대로 'Class Name' 변경한 경우 ↓ 'Entity' 이름변경 X |
2-4. Attribute
데이터베이스 'column(컬럼)' 혹은 'attribute(엣트리부트)'와 비슷한 개념인
'Attribute'는, 'Entity(엔티티)'가 가지는 속성들을 나타냄
2-5. Attribute 추가
'Entity' 선택한 상태 → 편집기 영역 아래쪽 'Add Attribute' 클릭 시 → 'Attributes List'에 해당에 'Attribute'가 표시
↓
'Attributes List' → 새로 추가된 'Attribute' 두 번 클릭 → 이름바꿈
↓
아래 두번째 이미지처럼, 해당 데이터의 'Type'을 선택 가능
'View' - 'Inspectors' - 'Show Data Model Inspector' 를 사용
↓
'Data Model Inspector' → Attribute를 구성 가능
↓
'Transformable(트랜스포머블)' 타입 선택 → 오른쪽 'Inspector(인스펙터)'영역 → 'Custom Class'를 지정
↓
리스트에 노출되는 기본적인 타입 외 커스텀한 타입을 저장 가능
2-6. Attribute 주요 속성들
Attribute Type | Optional | Validation | Default Value |
'Attribute'의 데이터 형식 | 해당 'Attribute'의 필수 속성 선택 가능 |
해당 'Attribute' 의 유효성 검사 옵션 지정 가능 |
데이터가(데이터베이스에서의 행) 생성되는 시점 ↓ 해당 'Attribute'의 기본값 지정 가능 |
'Attribute List'에서 ↓ 선택한 타입과 동일 |
2-7. Relationship
두 개 이상 'Entity'를 정의 → 'Entity' 간의 관계 추가 가능
2-8. Relationship(릴레이션쉽) 추가
'Entity' 선택 상태 → 'Relationship' 섹션의 좌측 하단 + 버튼 누를 시
↓
'Relationships List'에 해당에 'Relationship'이 표시
'Relationships List'에서 새로 추가된 'Relationship'을 두 번 클릭 → 이름 변경
'View' - 'Inspectors' - 'Show Data Model Inspector' 를 사용
↓
'Data Model Inspector'를 열어, Relationship을 구성가능
2-9. Relationship 주요 속성
Optional | Destination | Delete Rule | Type |
해당 'Relationship' 필수 속성선택 가능 |
관계 설정 대상이 되는 'Entity'를 선택 |
'Core Data'가 원본 인스턴스를 삭제할 때 변경 내용이 관계 'Relationship'간에 전파되는 방식을 지정 |
'Relationship'을 1:1(To One) 또는 1:n(To Many)로 지정 할지 선택 |
'No Action' ▼ 원본 객체 인스턴스를 삭제하되 수동으로 업데이트하는 모든 대상, 객체 인스턴스에 해당 객체에 대한 참조를 남겨 둠 |
|||
'Nullify' ▼ 'Destination'의 참조를 'nil'로 설정 |
|||
'Cascade(캐스케이드)' ▼ 'Destination(데이스터네이션)' 객체를 연쇄적으로 삭제 |
|||
'Deny' ▼ 아무 'Destination'을 가리키지 않는 경우에만 원본 개체를 삭제 |
2-10. 코드에서 Data 사용
위에서 정의한 'Data Model' 기반,
코드에서 데이터의 CRUD(크러드)하는 방식
2-11. 'persistentContainer'에 접근
'appDelegate'에 정의한 'persistentContainer'에 다음과 같이 접근 가능
import CoreData
var persistentContainer: NSPersistentContainer? {
(UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
}
2-12. 데이터 쓰기 (Create) - 데이터 작성 방법
// name이 “benz”인 Data를 생성하는 코드
guard let context = self.persistentContainer?.viewContext else { return }
let newCar = Car(context: context)
newCar.id = UUID()
newCar.name = "benz"
try? context.save()
2-13. 데이터 읽기 (Read) - 데이터 조회 방법
// Car Entity타입의 모든 Data를 조회하는 코드
guard let context = self.persistentContainer?.viewContext else { return }
let request = Car.fetchRequest()
let cars = try? context.fetch(request)
print(cars)
2-14. 데이터 수정 (Update) - 데이터 수정 방법
// name이 “benz”인 모든 Car의 name을 “tesla”로 수정하는 코드
guard let context = self.persistentContainer?.viewContext else { return }
let request = Car.fetchRequest()
guard let cars = try? context.fetch(request) else { return }
let filteredCars = cars.filter({ $0.name == "benz" })
for car in filteredCars {
car.name = "tesla"
}
try? context.save()
2-15. 데이터 삭제 (Delete) - 데이터 삭제 방법
// name이 “tesla”인 모든 Car를 삭제하는 코드
guard let context = self.persistentContainer?.viewContext else { return }
let request = Car.fetchRequest()
guard let cars = try? context.fetch(request) else { return }
let filteredCars = cars.filter({ $0.name == "tesla" })
for car in filteredCars {
context.delete(car)
}
try? context.save()
'IOS > Swift-Study' 카테고리의 다른 글
[Swift-Study] iOS 앱 개발 심화 1주차 - 아키텍처 (0) | 2024.05.01 |
---|---|
[Swift-Study] iOS 앱 개발 숙련 1주차 - 화면전환 (0) | 2024.04.14 |
[Swift-Study] iOS 앱 개발 숙련 1주차 - 네트워크 통신 (0) | 2024.04.12 |
[Swift-Study] iOS 앱 개발 숙련 1주차 - 메모리&ARC 개념 (0) | 2024.04.12 |
[Swift-Study] 베이직반 2회차 - 1회차 복습, Closure 활용 VC 간 통신 (0) | 2024.04.11 |