본문 바로가기

SwiftUI에서 회고 데이터 모델 설계와 감정/내용 저장 방식

@Prof.SSong2025. 5. 6. 20:00
728x90

프로젝트 맥락

 

본 글은 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 같은 대안을 고려해야 할 시점도 생길 것 같다.

 

 

728x90
목차