1단계: 파일 경로 설정하기
파일을 저장하거나 불러오기 위해 기본적으로 앱의 Documents Directory 경로를 설정합니다. 이 경로는 파일을 영구적으로 저장할 위치를 지정합니다.
import Foundation
// FileManager 기본 설정
let fileManager = FileManager.default
let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first!
documentsDirectory는 사용자의 앱 데이터가 저장되는 기본 경로입니다. 이곳에 이미지를 저장하거나, 저장된 이미지 파일을 불러올 때 경로로 활용할 수 있습니다.
2단계: 폴더 만들기 (필요시)
파일을 관리하기 쉽게 개별 폴더를 만들고, 필요한 경우 폴더를 생성합니다. 예를 들어, 각 피드 항목마다 고유 폴더를 생성하여 이미지나 기타 데이터를 관리할 수 있습니다.
func createFolderIfNeeded(named folderName: String) -> URL {
let folderURL = documentsDirectory.appendingPathComponent(folderName)
if !fileManager.fileExists(atPath: folderURL.path) {
try? fileManager.createDirectory(at: folderURL, withIntermediateDirectories: true, attributes: nil)
print("폴더 생성: \(folderURL.path)")
}
return folderURL
}
설명: createFolderIfNeeded 함수는 지정된 폴더 이름으로 경로를 만들고, 해당 폴더가 없으면 생성합니다. 예를 들어, feedID 폴더를 만들고 해당 폴더에 이미지를 저장할 수 있습니다.
3단계: 파일 저장하기 (예: 이미지 저장)
UIImage를 저장할 때 jpegData 또는 pngData로 변환한 후 파일로 저장할 수 있습니다.
func saveImage(_ image: UIImage, to folder: String, withName name: String) -> String? {
let folderURL = createFolderIfNeeded(named: folder)
let fileURL = folderURL.appendingPathComponent("\(name).jpg")
if let imageData = image.jpegData(compressionQuality: 1.0) {
try? imageData.write(to: fileURL)
print("이미지 저장: \(fileURL.path)")
return "\(folder)/\(name).jpg" // 상대 경로 반환
}
return nil
}
설명: saveImage 함수는 이미지를 특정 폴더에 저장하고, 저장된 파일의 상대 경로를 반환합니다. 이 상대 경로는 Core Data나 데이터베이스에 저장하여 나중에 해당 경로를 통해 이미지를 불러올 때 사용됩니다.
4단계: 파일 경로 저장하기 (Core Data에 경로만 저장)
이미지를 저장할 때 이미지 파일의 전체 경로 대신 상대 경로를 저장하는 것이 좋습니다. Core Data와 같은 데이터베이스에 경로만 저장하면, 전체 이미지 파일을 로드할 필요 없이 해당 경로를 이용해 효율적으로 접근할 수 있습니다.
let savedImagePath = saveImage(selectedImage, to: "FeedImages", withName: "image_1")
feedEntity.imagePath = savedImagePath // Core Data에 저장할 때 이미지 경로만 저장
5단계: 파일 불러오기
파일을 불러올 때는 저장된 상대 경로를 사용하여 해당 위치에서 이미지를 불러옵니다.
func loadImage(from relativePath: String) -> UIImage? {
let fileURL = documentsDirectory.appendingPathComponent(relativePath)
return UIImage(contentsOfFile: fileURL.path)
}
설명: loadImage 함수는 relativePath로 전달된 경로를 사용하여 파일에서 이미지를 불러옵니다.
6단계: 파일 삭제하기 (필요 시)
더 이상 사용하지 않는 파일은 삭제하여 저장 공간을 관리할 수 있습니다.
func deleteImage(at relativePath: String) {
let fileURL = documentsDirectory.appendingPathComponent(relativePath)
do {
try fileManager.removeItem(at: fileURL)
print("파일 삭제 완료: \(fileURL.path)")
} catch {
print("파일 삭제 오류: \(error)")
}
}
이미지 저장 및 로드 과정
// 이미지 저장
let relativePath = saveImage(selectedImage, to: "FeedImages", withName: "image_1")
feedEntity.imagePath = relativePath // Core Data에 이미지 경로 저장
// 이미지 불러오기
if let imagePath = feedEntity.imagePath {
let loadedImage = loadImage(from: imagePath)
imageView.image = loadedImage
}
요약
- 파일 경로 설정: 기본적으로 documentsDirectory를 사용합니다.
- 폴더 생성: 필요 시 데이터를 조직적으로 저장할 수 있도록 폴더를 생성합니다.
- 파일 저장: 데이터를 저장하고, Core Data에는 파일의 상대 경로를 저장합니다.
- 파일 불러오기: 저장된 경로를 사용하여 파일을 로드합니다.
- 파일 삭제: 필요 없는 파일을 삭제하여 공간을 관리합니다.
FileManager를 통해 이미지를 저장하고 해당 이미지 파일 경로를 반환하는 메서드
import UIKit
class FeedStorageManager {
// Documents 폴더 경로 가져오기
private func getDocumentsDirectory() -> URL {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
// 이미지 저장
func saveImages(images: [UIImage], feedID: String) -> [String] {
let feedFolder = getDocumentsDirectory().appendingPathComponent(feedID)
// 폴더가 없으면 생성
if !FileManager.default.fileExists(atPath: feedFolder.path) {
try? FileManager.default.createDirectory(at: feedFolder, withIntermediateDirectories: true, attributes: nil)
}
var savedImagePaths: [String] = []
for (index, image) in images.enumerated() {
let fileName = "image_\(index).jpg"
let fileURL = feedFolder.appendingPathComponent(fileName)
if let imageData = image.jpegData(compressionQuality: 1.0) {
try? imageData.write(to: fileURL)
savedImagePaths.append("\(feedID)/\(fileName)") // 상대경로 저장
}
}
return savedImagePaths
}
// 이미지 로드
func loadImages(from relativePaths: [String]) -> [UIImage] {
var images = [UIImage]()
for relativePath in relativePaths {
let fullPath = getDocumentsDirectory().appendingPathComponent(relativePath)
if let image = UIImage(contentsOfFile: fullPath.path) {
images.append(image)
}
}
print("불러온 이미지", images)
return images
}
}
1. getDocumentsDirectory()
private func getDocumentsDirectory() -> URL {
FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}
- 이 메서드는 FileManager를 통해 앱의 Documents Directory 경로를 반환합니다.
- documentDirectory는 앱이 사용자 데이터를 저장할 수 있는 기본 위치이며, 앱이 삭제될 때까지 유지됩니다.
- 이 함수는 저장하고 불러오는 작업에서 파일의 기본 경로로 사용됩니다.
2. saveImages(images: [UIImage], feedID: String) -> [String]
func saveImages(images: [UIImage], feedID: String) -> [String] {
let feedFolder = getDocumentsDirectory().appendingPathComponent(feedID)
// 폴더가 없으면 생성
if !FileManager.default.fileExists(atPath: feedFolder.path) {
try? FileManager.default.createDirectory(at: feedFolder, withIntermediateDirectories: true, attributes: nil)
}
var savedImagePaths: [String] = []
for (index, image) in images.enumerated() {
let fileName = "image_\(index).jpg"
let fileURL = feedFolder.appendingPathComponent(fileName)
if let imageData = image.jpegData(compressionQuality: 1.0) {
try? imageData.write(to: fileURL)
savedImagePaths.append("\(feedID)/\(fileName)") // 상대경로 저장
}
}
return savedImagePaths
}
- feedID를 사용하여 해당 이미지들을 저장할 고유 폴더를 생성합니다. 이 폴더는 Documents Directory 내에서 feedID라는 이름으로 저장됩니다.
- 예를 들어, feedID가 12345라면 Documents/12345/ 폴더에 이미지가 저장됩니다.
- FileManager.default.fileExists(atPath:)로 해당 폴더가 존재하는지 확인하고, 없을 경우 createDirectory를 사용해 생성합니다.
- savedImagePaths 배열은 저장한 각 이미지 파일의 상대 경로를 담고, 이 배열이 Core Data에 저장될 예정입니다.
- for (index, image) in images.enumerated():
- 주어진 images 배열의 각 이미지를 순회하면서 image_0.jpg, image_1.jpg 등 고유한 파일명으로 저장합니다.
- image.jpegData(compressionQuality: 1.0)을 사용하여 UIImage를 JPEG 데이터로 변환 후 write(to:)로 저장합니다.
- 상대 경로로 저장:
- 경로는 Documents Directory를 기준으로 savedImagePaths 배열에 상대 경로(feedID/image_x.jpg) 형태로 저장됩니다.
- 이 경로는 앱 내에서 이미지를 불러올 때 사용되며, Core Data에는 전체 경로가 아닌 상대 경로만 저장하여 앱이 경로 구조를 변경해도 유연하게 대처할 수 있습니다.
3. loadImages(from relativePaths: [String]) -> [UIImage]
func loadImages(from relativePaths: [String]) -> [UIImage] {
var images = [UIImage]()
for relativePath in relativePaths {
let fullPath = getDocumentsDirectory().appendingPathComponent(relativePath)
if let image = UIImage(contentsOfFile: fullPath.path) {
images.append(image)
}
}
print("불러온 이미지", images)
return images
}
- relativePaths는 Core Data에 저장된 이미지들의 상대 경로 배열입니다. 이 경로를 사용해 이미지를 불러옵니다.
- for relativePath in relativePaths:
- 각 상대 경로를 순회하여 fullPath를 생성하고, UIImage(contentsOfFile:)로 해당 경로의 이미지를 불러옵니다.
- UIImage(contentsOfFile:) 메서드는 파일 경로에 있는 이미지를 UIImage로 변환합니다.
- 불러온 이미지들을 images 배열에 추가하고 최종적으로 반환합니다.
- 핵심:
- 이 방식은 앱의 파일 구조가 변경되지 않는 한 이미지 파일을 빠르게 로드할 수 있습니다.
- Core Data에는 이미지 자체가 아닌 파일 경로만 저장되므로 앱의 성능을 향상시킬 수 있습니다.
코어데이터를 통해 텍스트 및 이미지는 파일 매니저로 저장한 상태에서 얻어온 이미지 경로를 관리하는 클래스
import CoreData
import UIKit
class FeedDataManager {
static let shared = FeedDataManager()
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
private let storageManager = FeedStorageManager()
// Core Data에 저장
func saveFeedItem(feedItem: FeedItem) {
let feedEntity = FeedEntity(context: context)
let feedID = UUID().uuidString // 각 피드마다 고유 ID 생성
// FeedItem의 데이터 설정
feedEntity.tripLog = feedItem.tripLog
feedEntity.location = feedItem.location
feedEntity.date = feedItem.date
feedEntity.category = feedItem.category
feedEntity.feedID = feedID
// 이미지 저장 및 경로 설정
if let images = feedItem.images {
let imagePaths = storageManager.saveImages(images: images, feedID: feedID)
feedEntity.imagePaths = imagePaths.joined(separator: ",")
}
do {
try context.save()
print("FeedItem이 성공적으로 저장되었습니다.")
print("저장된 이미지 경로:", feedEntity.feedID!)
} catch {
print("FeedItem 저장 실패: \(error)")
}
}
// Core Data에서 불러오기
func fetchFeedItems() -> [FeedItem] {
let request: NSFetchRequest<FeedEntity> = FeedEntity.fetchRequest()
var feedItems: [FeedItem] = []
do {
let feedEntities = try context.fetch(request)
for entity in feedEntities {
let images: [UIImage]
if let imagePathsString = entity.imagePaths,
let feedID = entity.feedID {
let imagePathsArray = imagePathsString.components(separatedBy: ",")
images = storageManager.loadImages(from: imagePathsArray)
} else {
images = []
}
// FeedItem을 생성하고 데이터 할당
let feedItem = FeedItem(
images: images,
tripLog: entity.tripLog,
location: entity.location,
date: entity.date,
category: entity.category
)
feedItems.append(feedItem)
}
} catch {
print("데이터 로드 실패: \(error)")
}
return feedItems
}
}
이 코드에서는 FeedDataManager라는 클래스를 통해 Core Data와 FileManager를 조합하여 데이터를 저장하고 관리합니다. 이 클래스는 Core Data로 텍스트와 메타데이터를 저장하고, FileManager를 통해 이미지를 파일로 저장하며, 이미지 경로만 Core Data에 저장해 영구적으로 관리하는 구조입니다.
1. 기본 설정
static let shared = FeedDataManager()
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
private let storageManager = FeedStorageManager()
- shared: 싱글톤 패턴을 사용하여 앱 전반에서 FeedDataManager를 단일 인스턴스로 접근하게 합니다.
- context: Core Data의 NSManagedObjectContext를 가져와 데이터베이스 작업에 사용합니다.
- storageManager: 이미지 저장과 로드를 담당하는 FeedStorageManager 클래스 인스턴스입니다.
2. Core Data에 데이터 저장
func saveFeedItem(feedItem: FeedItem) {
let feedEntity = FeedEntity(context: context)
let feedID = UUID().uuidString // 각 피드마다 고유 ID 생성
- feedItem 데이터를 저장하기 위해 FeedEntity를 생성하고, 각 FeedItem마다 고유한 UUID를 생성해 feedID로 사용합니다.
- 이 feedID는 FileManager 내에서 폴더를 생성하는 데 사용되어 이미지 파일을 고유하게 구분할 수 있습니다.
텍스트 및 메타데이터 설정
feedEntity.tripLog = feedItem.tripLog
feedEntity.location = feedItem.location
feedEntity.date = feedItem.date
feedEntity.category = feedItem.category
feedEntity.feedID = feedID
- feedEntity의 각 필드에 feedItem의 데이터를 설정합니다.
- feedID를 feedEntity의 필드에 저장해, 추후 이미지를 불러올 때 이 ID를 사용해 폴더 경로를 찾습니다.
이미지 저장 및 경로 설정
if let images = feedItem.images {
let imagePaths = storageManager.saveImages(images: images, feedID: feedID)
feedEntity.imagePaths = imagePaths.joined(separator: ",")
}
- feedItem이 가진 이미지 배열을 saveImages(images:feedID:) 함수로 전달합니다.
- storageManager.saveImages는 이미지를 FileManager에 저장하고 저장된 이미지의 경로 목록을 반환합니다.
- 경로 목록을 문자열로 ,로 구분해 imagePaths 필드에 저장하여 Core Data에 경로를 관리합니다.
데이터베이스에 저장
do {
try context.save()
print("FeedItem이 성공적으로 저장되었습니다.")
print("저장된 이미지 경로:", feedEntity.feedID!)
} catch {
print("FeedItem 저장 실패: \(error)")
}
- 모든 데이터가 feedEntity에 저장되면 context.save()를 호출해 Core Data에 저장합니다.
3. Core Data에서 데이터 불러오기
func fetchFeedItems() -> [FeedItem] {
let request: NSFetchRequest<FeedEntity> = FeedEntity.fetchRequest()
var feedItems: [FeedItem] = []
- fetchFeedItems는 Core Data에서 저장된 FeedEntity 데이터를 불러옵니다.
- NSFetchRequest<FeedEntity>를 통해 FeedEntity의 모든 항목을 요청합니다.
데이터 로드 및 이미지 경로 처리
do {
let feedEntities = try context.fetch(request)
for entity in feedEntities {
let images: [UIImage]
if let imagePathsString = entity.imagePaths,
let feedID = entity.feedID {
let imagePathsArray = imagePathsString.components(separatedBy: ",")
images = storageManager.loadImages(from: imagePathsArray)
} else {
images = []
}
- context.fetch(request)를 통해 Core Data에 저장된 모든 FeedEntity 데이터를 가져옵니다.
- 각 entity에서 imagePaths 문자열을 불러오고, feedID를 이용해 관련 이미지 파일을 FileManager에서 찾습니다.
- storageManager.loadImages(from:)를 통해 FileManager에서 이미지를 읽어들여 images 배열에 저장합니다.
FeedItem 생성 및 반환
let feedItem = FeedItem(
images: images,
tripLog: entity.tripLog,
location: entity.location,
date: entity.date,
category: entity.category
)
feedItems.append(feedItem)
- images와 텍스트 데이터를 이용해 FeedItem을 생성하고 feedItems 배열에 추가합니다.
- 모든 데이터를 배열에 추가한 뒤 반환합니다.
'iOS > UIKIT' 카테고리의 다른 글
UISheetPresentation을 통해 수정, 삭제, 닫기 버튼 기능 구현 (0) | 2024.11.16 |
---|---|
FileManager 사용방법 - 수정 (3) | 2024.11.14 |
iOS 앱에서 네트워크 통신을 하는 방법에는 어떤 것들이 있나요? (5) | 2024.11.11 |
UIImage와 UIImageView의 차이 (1) | 2024.11.10 |
갤러리에서 선택한 내용을 컬렉션 뷰에 보이는 방법 (0) | 2024.11.08 |