Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

카테고리 CRUD를 CoreData와 연결하여 구현 #99

Merged
merged 29 commits into from
Nov 29, 2024

Conversation

Kyxxn
Copy link
Collaborator

@Kyxxn Kyxxn commented Nov 29, 2024

#️⃣ 연관된 이슈


⏰ 작업 시간

예상 시간 실제 걸린 시간
5 8

📝 작업 내용

  • �카테고리 CRUD를 코어데이터와 연결하여 구현
  • 카테고리 CRUD 테스트 코드 작성

📸 스크린샷

카테고리 CRUD 완성

스크린샷 2024-11-29 오후 8 44 07

📒 리뷰 노트

  • Storage CRUD가 do-catch로 동일한 로직을 하길래 performDatabaseTask 메소드로 묶어주었습니다.
  • 다른 코어데이터 로직도 비슷하다면 이 메소드로 리팩토링해도 좋을듯 하네요..!

⚽️ 트러블 슈팅

HomeViewController에서 시트지 버튼을 누르면,
기존에는 HomeViewModel이 카테고리를 들고 있었기 때문에 미리 CategoryViewController의 높이를 알아내고 Present 시킬 수 있었다.
그러나, 이제 CategoryViewModel이 카테고리 목록을 직접 Fetch 해오기 때문에 커스텀 높이 시트지를 사용할 수가 없었다.
우선은 Medium ~ Large로 높이를 고정적으로 두어 해결하였다.

Kyxxn added 26 commits November 28, 2024 16:52
@Kyxxn Kyxxn added ✨ Feature 기능 관련 작업 ✅ Test 테스트 관련 작업 labels Nov 29, 2024
@Kyxxn Kyxxn added this to the 0.4 milestone Nov 29, 2024
@Kyxxn Kyxxn requested a review from k2645 November 29, 2024 11:45
@Kyxxn Kyxxn self-assigned this Nov 29, 2024
@Kyxxn Kyxxn linked an issue Nov 29, 2024 that may be closed by this pull request
Copy link
Collaborator Author

@Kyxxn Kyxxn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

변경된 줄 수와 파일은 많은데.. 사실 네이밍 변경된 것도 많고 보일러 플레이트 뿐입니다
이 모든게 영현님 덕분이죠 CoreData 길을 뚫어주셔서 감사합니다,,

Comment on lines -147 to +149
sheet.detents = [.custom(identifier: .categorySheet) { _ in
categoryViewController.calculateSheetHeight()
}]
sheet.detents = [.medium(), .large()]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이제 CategoryViewModel이 카테고리 목록을 직접 Fetch 해오기 때문에 커스텀 높이 시트지를 사용할 수가 없어서
우선은 Medium ~ Large로 높이를 고정적으로 두어 해결했습니다

추후 수정할게요

Comment on lines 18 to 28
let container = NSPersistentContainer(name: CoreDataStorage.modelName)
let container = NSPersistentContainer(name: CoreDataStorage.modelName, managedObjectModel: Self.memorialHouseModel)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CoreData: error: Failed to load model named "우리팀모델.xcdatamodeld"
위 문제가 발생해서 다음과 같이 수정해주었습니다.

Comment on lines +12 to +33
private func performDatabaseTask<T>(
_ task: @escaping (NSManagedObjectContext) throws -> T
) async -> Result<T, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
return try await context.perform {
do {
return .success(try task(context))
} catch let error as MHDataError {
MHLogger.debug("Core Data 에러: \(error.description)")
throw error
} catch {
MHLogger.debug("알 수 없는 Core Data 에러: \(error.localizedDescription)")
throw error
}
}
} catch let error as MHDataError {
return .failure(error)
} catch {
return .failure(MHDataError.generalFailure)
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

코어데이터 CRUD 로직이 겹치길래 얘로 묶어줬는데, 다른 분들 코드에 적용해보면 어떨까요 ??
중복되는 코드 길이를 줄일 수 있을 거 같습니당

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋아욤!!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구뜨!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERROR를 처리할 때 Result의 failure와 throw를 나누신 의도가 궁금합니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yuncheol-AHN
내부 디버깅이랑 호출하는 곳에서의 에러 처리를 동시에 해주려고 그랬습니다 !!
crud 메소드에서 발생한 throw를 해당 메소드 catch에서 잡고 디버그 로그로 출력하고 이걸 Result로 반환합니다 !

Copy link
Collaborator

@iceHood iceHood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무 고생많으셨습니다... 대대대대대대효준님...

Comment on lines +12 to +33
private func performDatabaseTask<T>(
_ task: @escaping (NSManagedObjectContext) throws -> T
) async -> Result<T, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
return try await context.perform {
do {
return .success(try task(context))
} catch let error as MHDataError {
MHLogger.debug("Core Data 에러: \(error.description)")
throw error
} catch {
MHLogger.debug("알 수 없는 Core Data 에러: \(error.localizedDescription)")
throw error
}
}
} catch let error as MHDataError {
return .failure(error)
} catch {
return .failure(MHDataError.generalFailure)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋아욤!!

/// - 주요 특징:
/// - `NSPersistentContainer`를 활용해 Core Data 스택을 구성합니다.
/// - `saveContext` 메서드를 통해 변경된 컨텍스트를 저장합니다.
public class CoreDataStorage: @unchecked Sendable {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

혹시

public final class CoreDataStorage: Sendable {
    static let modelName: String = "MemorialHouseModel"
    
    nonisolated(unsafe) static let memorialHouseModel: NSManagedObjectModel = {
        guard let modelURL = Bundle(for: CoreDataStorage.self).url(
            forResource: CoreDataStorage.modelName,
            withExtension: "momd"
        ) else {
            fatalError("Error loading model from bundle")
        }
        return NSManagedObjectModel(contentsOf: modelURL)!
    }()
    
    let persistentContainer: NSPersistentContainer
    
    public init() {
        let container = NSPersistentContainer(
            name: CoreDataStorage.modelName,
            managedObjectModel: Self.memorialHouseModel
        )
        container.loadPersistentStores { _, error in
            guard let error else { return }
            MHLogger.error("\(#function): PersistentContainer 호출에 실패; \(error.localizedDescription)")
        }
        self.persistentContainer = container
    }
...
}

요렇게 하셔도 에러 뜨시나요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lazy 사용하지 않는 방식인 거죵 ?
한 번 해보겠습니다

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

문제없이 잘 돌아가네요, 저도 lazy 방식보다 정현님 말씀대로 하는게 좋은 거 같습니다
수정해서 올릴게요 !!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

참 테스트코드에서 final class MockCoreDataStorage: CoreDataStorage {}
이렇게 사용하고 있어서 final은 제거하겠습니다 !!

Comment on lines +68 to +69
// FIXME: MainActor 제거
@MainActor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋㅋㅋ Swift6의 쓴맛...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅠㅠ.. 우리의 숙원

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤯

Copy link
Collaborator

@yuncheol-AHN yuncheol-AHN left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

금요일도 쉬지 않으시는 군요...
대 효 준 🔥 열 정 보 이 🔥

Comment on lines +12 to +33
private func performDatabaseTask<T>(
_ task: @escaping (NSManagedObjectContext) throws -> T
) async -> Result<T, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
return try await context.perform {
do {
return .success(try task(context))
} catch let error as MHDataError {
MHLogger.debug("Core Data 에러: \(error.description)")
throw error
} catch {
MHLogger.debug("알 수 없는 Core Data 에러: \(error.localizedDescription)")
throw error
}
}
} catch let error as MHDataError {
return .failure(error)
} catch {
return .failure(MHDataError.generalFailure)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구뜨!

Comment on lines +12 to +33
private func performDatabaseTask<T>(
_ task: @escaping (NSManagedObjectContext) throws -> T
) async -> Result<T, MHDataError> {
let context = coreDataStorage.persistentContainer.viewContext
do {
return try await context.perform {
do {
return .success(try task(context))
} catch let error as MHDataError {
MHLogger.debug("Core Data 에러: \(error.description)")
throw error
} catch {
MHLogger.debug("알 수 없는 Core Data 에러: \(error.localizedDescription)")
throw error
}
}
} catch let error as MHDataError {
return .failure(error)
} catch {
return .failure(MHDataError.generalFailure)
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ERROR를 처리할 때 Result의 failure와 throw를 나누신 의도가 궁금합니다!

Copy link
Collaborator

@k2645 k2645 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

너무너무 수고 많으셨습니다 .ᐟ.ᐟ 덕분에 큰 산을 넘은 것 같네요 🥹

let bookCategory = NSManagedObject(entity: entity, insertInto: context)
bookCategory.setValue(category.order, forKey: "order")
bookCategory.setValue(category.name, forKey: "name")
try context.save()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: CoreDataStorage를 보면 실은 saveContext라는 메서드가 있어서.. try를 쓰지 않고도 저장할 수 있긴한데�이것도 괜찮아 보이네용.. saveContext 메서드는 지울까엽??

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

옷 그러네요..!
지워도 좋을 거 같아용

Comment on lines +75 to +86
private func handleError(with errorMessage: String) {
let alertController = UIAlertController(
title: "에러",
message: errorMessage,
preferredStyle: .alert
)
let okAction = UIAlertAction(title: "확인", style: .default)
alertController.addAction(okAction)

present(alertController, animated: true)
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

캬.. 에러처리까지.. 역시 효준님 .. 그저 "빛" 입니다 ✨

dismiss(animated: true, completion: nil)
}

func tableView(
_ tableView: UITableView,
heightForRowAt indexPath: IndexPath
) -> CGFloat {
CategoryTableViewCell.height
BookCategoryTableViewCell.height
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: 혹시 이제 Cell의 갯수에 따른 시트지의 높이를 계산하지 않아도 된다면 위에서 tableView.rowHeight = 00 이렇게 설정해줘도 되지 않을까?? 싶습니당

Comment on lines +68 to +69
// FIXME: MainActor 제거
@MainActor
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤯

@Kyxxn Kyxxn merged commit 6e8201f into develop Nov 29, 2024
2 checks passed
@Kyxxn Kyxxn deleted the feature/crud-coredata-category branch November 29, 2024 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
✨ Feature 기능 관련 작업 ✅ Test 테스트 관련 작업
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Category 작업에 대한 CoreData CRUD 처리
4 participants