https://explorer89.tistory.com/486
📸 Core Data에서 ‘이미지 피드 + 게시글 연결’ 기능을 설계하는 방법
“저장된 이미지를 한눈에 보여주고,이미지를 누르면 해당 일기(게시글)로 이동하게 만들고 싶어요!” 많은 앱들이 이런 UI를 가지고 있습니다.대표적으로 인스타그램, 네이버 블로그, 다이어리
explorer89.tistory.com
앱을 설계할 때 가장 중요한 건 데이터 구조를 어떻게 정의하느냐다.
이번 포스트에서는 나의 감정일기 앱에서
Core Data 모델을 어떤 기준으로 설계했는지, 그리고
“이미지 데이터를 왜 별도의 엔티티로 분리했는지”를 정리해보려 한다.
🧩 1️⃣ 기본 구조 개요
감정일기 앱은 사용자가 하루의 감정을 기록하고,
그날의 이미지를 함께 첨부하는 구조다.
이때 데이터를 관리하기 위해 Core Data를 사용했고,
두 개의 엔티티(Entity)를 설계했다.
| 엔티티 | 역할 |
| EmotionDiaryEntity | 감정일기의 기본 데이터 (감정, 내용, 날짜 등) |
| DiaryImageEntity | 첨부된 이미지의 정보 (파일 경로, 생성일 등) |
🧱 2️⃣ 감정일기와 이미지의 관계: 1 : N
한 개의 감정일기에는 여러 장의 이미지를 저장할 수 있어야 한다.
그래서 두 엔티티는 다음과 같은 관계를 갖는다 👇
EmotionDiaryEntity (1)
└──> images (To-Many)
▼
DiaryImageEntity (N)
└──> diary (To-One)
이렇게 설계함으로써,
하나의 일기에 여러 이미지를 연결할 수 있고
반대로 특정 이미지가 어떤 일기에 속하는지도 쉽게 알 수 있다.
즉, Core Data가 제공하는 양방향 관계(Inverse Relationship) 를 활용해
데이터 일관성을 유지할 수 있다.
🔄 3️⃣ Inverse Relationship이란?
Core Data에서 관계를 설정할 때는 “inverse”를 반드시 지정해야 한다.
이 inverse는 관계의 양방향 동기화 역할을 한다.
| 관계 | Inverse |
| EmotionDiaryEntity.images | DiaryImageEntity.diary |
| DiaryImageEntity.diary | EmotionDiaryEntity.images |
이 설정 덕분에 Core Data는
“이 이미지가 어떤 일기에 속하는지”를 자동으로 추적할 수 있다.
예를 들어 아래 코드를 보면 👇
diary.images.insert(imageEntity)
이 코드 한 줄만으로 Core Data는 내부적으로
imageEntity.diary = diary
까지 자동으로 동기화한다.
즉, 양방향 데이터 연결이 보장되어 일관성이 깨지지 않는다.
💡 4️⃣ 이미지 데이터를 별도의 엔티티로 분리한 이유
많은 초보자들이 흔히 이렇게 설계하곤 한다 👇
EmotionDiaryEntity
└── images: [String] (Transformable)
즉, 여러 이미지 경로를 하나의 배열로 묶어서 저장하는 방식이다.
하지만 이 방식에는 다음과 같은 문제가 있다.
| 문제점 | 설명 |
| ⚠️ 확장성 부족 | 나중에 이미지에 “위치 정보”나 “태그”를 추가하기 어려움 |
| ⚠️ 수정 비효율 | 배열 내 하나의 이미지만 수정해도 전체 배열을 다시 저장해야 함 |
| ⚠️ 검색 불가 | Core Data는 Transformable 타입 내부를 인식하지 못함 |
| ⚠️ Cascade 미지원 | 일기 삭제 시 개별 이미지 자동 삭제 불가 |
따라서 나는 이미지 데이터를 별도의 DiaryImageEntity로 분리했다.
이렇게 하면
1. 각 이미지가 독립적으로 관리되고
2. Core Data의 Cascade Delete Rule을 활용해 일기 삭제 시 자동 정리되며
3. 나중에 이미지별로 위치 정보(latitude, longitude)나 태그 등을 확장하기 쉬워진다.
✅ 확장성, 관리 효율성, 검색 성능 모두 확보되는 구조다.
🧠 5️⃣ id는 왜 String 타입으로 설정했는가?
Core Data에는 고유 식별자로 UUID 타입을 많이 사용한다.
하지만 나는 String 타입을 선택했다.
그 이유는 다음과 같다 👇
| 이유 | 설명 |
| ✅ FileManager와의 호환성 | 이미지 파일명을 직접 id와 동일하게 쓸 수 있음 |
| ✅ 외부 연동 용이 | CloudKit, Firebase 등과의 동기화 시 변환 불필요 |
| ✅ 디버깅 편의성 | id 값을 바로 로그로 확인 가능 |
예를 들어,
이미지 파일명을 "감정일기id_image_1.jpg" 형태로 저장하면,
해당 파일명과 Core Data의 image id가 완전히 일치하게 된다.
imageEntity.id = "\(diary.id)_image_\(index)"
imageEntity.imagePath = FileManagerHelper.save(image, id: imageEntity.id)
이렇게 하면 파일과 데이터베이스의 관계가 명확해지고,
디버깅이나 데이터 정리 시에도 혼동이 없다.
⚙️ 6️⃣ Delete Rule 설정
| 관계 | Delete Rule | 설명 |
| EmotionDiaryEntity → images | Cascade | 일기를 삭제하면 연결된 이미지도 자동 삭제 |
| DiaryImageEntity → diary | Nullify | 이미지를 개별 삭제해도 부모 일기에는 영향 없음 |
이 조합은 Core Data에서 가장 안정적인 형태다.
✅ 부모 삭제 시 자식 자동 정리,
✅ 자식 단독 삭제 시 안전한 null 처리.
🧩 7️⃣ 최종 Core Data 모델 요약
| Entity | 주요 속성 | 관계 |
| EmotionDiaryEntity | id (String), emotion (String), content (String), createdAt (Date) | images (To-Many, Cascade) |
| DiaryImageEntity | id (String), imagePath (String), createdAt (Date) | diary (To-One, Nullify) |
EmotionDiaryEntity
├── id: String
├── emotion: String
├── content: String
├── createdAt: Date
└── images (To-Many) ──▶ DiaryImageEntity
├── id: String
├── imagePath: String
├── createdAt: Date
└── diary (To-One)


🚀 8️⃣ 결론
이번 Core Data 설계의 핵심은 단순하다.
“이미지를 배열로 묶지 말고, 개별 Entity로 관리하자.”
이 결정 하나로
1. 데이터 정합성
2.삭제 규칙의 자동화 (Cascade)
3. 미래 확장성 (위치, 태그 추가)
4. FileManager와의 구조적 일관성
을 모두 확보했다.
이 구조는 추후
1. 위치 기반 일기 정렬,
2. AI 감정 분석 기능,
3. 이미지 갤러리 화면 구현으로 확장하기에도 가장 안정적인 기반이 된다.
'감정일기(가칭)' 카테고리의 다른 글
| 🪵 개발/배포 환경을 구분한 로깅 설계 (0) | 2025.10.17 |
|---|---|
| 📦 Core Data와 FileManager로 이미지 관리 구조 설계하기 (0) | 2025.10.17 |
| 🧩 Core Data 설계할 때, “나중에 쓸 속성”을 지금 넣어야 할까? (0) | 2025.10.16 |
| 🧠 Core Data + Enum: 감정(EmotionCategory) case를 추가·변경할 때 생기는 문제와 해결법 (0) | 2025.10.16 |
| 📸 Core Data에서 ‘이미지 피드 + 게시글 연결’ 기능을 설계하는 방법 (0) | 2025.10.16 |