프로젝트 맥락
본 글은 SwiftUI로 Apple Developer Academy_Challenge 2에서 개발 중인 ‘Re:ToU(오늘의 너)’ 앱을 기반으로 작성되었습니다.
‘Re:ToU’는 사용자가 하루에 하나의 회고를 작성하는 구조로, 감정(이모지)과 텍스트 내용을 함께 저장합니다.
앱은 로컬 저장소(UserDefaults)를 사용하며, 회고 데이터 모델을 어떻게 설계하고,
입력값을 어떻게 저장/불러올 것인지가 핵심 과제였습니다.
기능 설명
앱에서는 다음 데이터를 한 세트로 관리합니다:
- 날짜 (Date)
- 감정 (EmotionType: enum)
- 회고 내용 (String)
이 데이터를 구조체로 정의하고, SwiftUI의 상태 관리 시스템과 로컬 저장소(UserDefaults)를 통해
기기 내에서 안전하게 저장/수정/불러오기를 구현했습니다.
시도 동기
처음에는 감정과 텍스트를 단순히 각각 @AppStorage에 저장하는 방식으로 시도했습니다.
하지만 회고를 여러 개 저장하고 리스트 형태로 관리하기 위해서는
구조화된 데이터 모델이 필요하다는 결론에 도달했습니다.
또한 앱에서 회고를 날짜별로 조회/수정하기 위해서는
id, 날짜 기반 구분, 수정 가능성을 고려한 설계가 필요했습니다.
문제 발견
데이터 저장 로직을 구현하면서 다음과 같은 문제가 발생했습니다:
- UserDefaults에 직접 구조체를 저장할 수 없음
- 기본 타입(String, Int, Bool, Array 등)만 지원
- 감정 이모지 enum이 문자열로 잘못 저장되거나 인코딩 실패
- 앱을 재실행하면 데이터가 로드되지 않음
- 저장/불러오기 시 Codable 인코딩/디코딩 누락
문제 해결
1. Reflection 모델 정의
struct Reflection: Identifiable, Codable {
var id: UUID
var date: Date
var emotion: String
var content: String
}
- Identifiable을 채택해 리스트 뷰에서 바로 활용 가능
- Codable로 UserDefaults 저장에 필요한 인코딩 지원
- 감정은 String으로 저장하고, UI에서는 EmotionType로 변환
2. ViewModel에서 저장/불러오기 처리
class ReflectionStorage: ObservableObject {
@Published var reflections: [Reflection] = []
private let storageKey = "reflections"
init() {
load()
}
func add(content: String, emotion: String) {
let reflection = Reflection(
id: UUID(),
date: Date(),
emotion: emotion,
content: content
)
reflections.append(reflection)
save()
}
func update(reflection: Reflection, content: String, emotion: String) {
if let index = reflections.firstIndex(where: { $0.id == reflection.id }) {
reflections[index].content = content
reflections[index].emotion = emotion
save()
}
}
private func save() {
if let encoded = try? JSONEncoder().encode(reflections) {
UserDefaults.standard.set(encoded, forKey: storageKey)
}
}
private func load() {
if let data = UserDefaults.standard.data(forKey: storageKey),
let decoded = try? JSONDecoder().decode([Reflection].self, from: data) {
reflections = decoded
}
}
}
비슷한 저장 방식 비교
저장 방식설명장점단점
@AppStorage로 단일값 저장 | 단순한 Key-Value 저장 | 빠름, 설정 쉬움 | 배열이나 구조체 저장 어려움 |
UserDefaults + Codable (현재 방식) | 구조화된 데이터 저장 | 적당한 유연성, 파일 불필요 | 타입 안전성 직접 관리해야 함 |
FileManager로 파일 저장 | JSON 파일로 직접 관리 | 디스크 제어 가능 | 직접 파일 경로/형식 처리 필요 |
CoreData | 관계형 저장, 쿼리 지원 | 복잡한 모델에 적합 | 설정 복잡, 러닝 커브 높음 |
느낀 점
단순히 데이터를 저장하는 것처럼 보여도,
앱 전체의 데이터 흐름과 구조를 고려한 모델 설계와 저장 방식 선택이 매우 중요하다는 걸 느꼈다.
UserDefaults는 작고 단순한 앱에서 빠르고 효율적으로 구조화된 데이터를 다룰 수 있는 좋은 방법이지만,
항상 Codable과 타입 일치를 신경 써야 한다.
앞으로 데이터가 더 많아지거나, 관계형 구조가 필요해진다면 CoreData나 Realm 같은 대안을 고려해야 할 시점도 생길 것 같다.
'iOS & SwiftUI' 카테고리의 다른 글
SwiftUI에서 작성/수정 화면을 분리하고 전환 흐름 구성하기 (2) | 2025.05.06 |
---|---|
SwiftUI에서 감정 이모지 선택 UI 구현하기: HStack 기반 구조 (2) | 2025.05.05 |
SwiftUI에서 입력 완료 여부에 따라 버튼 활성화 상태 제어하기 (1) | 2025.05.03 |
SwiftUI에서 NavigationLink를 안전하게 사용하는 방법: 상태 기반 전환 설계하기 (0) | 2025.05.01 |
SwiftUI 앱에서 UserDefaults를 이용한 로컬 데이터 저장 방식 설계하기 (2) | 2025.04.29 |