[Swift] Core Data (2) : fetchRequest

Oni·2023년 9월 13일
1

TIL

목록 보기
45/47
post-thumbnail

아주 기초적으로 Core Data를 사용한 Todo App을 만들어보려고 한다.
이론만 알고 넘어가기엔 머릿속에 안들어와서(...) 직접 해보면서 익혀보겠다!

일단 Core Data를 사용하려면 (1) 프로젝트 생성 시 Use Core Data를 클릭하는 방법과, (2) 기존 프로젝트에 Core Data - Data Model을 생성해주면 된다.

두가지 방법이 있지만, 처음부터 Core Data를 선택해서 프로젝트를 생성하는 것이 효율적일 것 같다. 기존 프로젝트에 추가하게 되면은 9할은 리팩토링이 필수적인...ㅠㅋㅋㅋㅋ

간단하게 장단점을 비교해보면 아래와 같다. 각자 진행하고 있는 프로젝트 특징(?)에 맞게 알아서 선택하시길^_^


Core Data 생성

Core Data 사용방법

1. 기존 프로젝트에 Core Data 추가

  • 장점
    • 기존 데이터와 Core Data 통합 가능
    • 기존 프로젝트의 코드를 유지하면서 데이터베이스 지원 도입 가능
  • 단점
    • 기존 코드와의 호환성 문제나 데이터 마이그레이션 등의 복잡한 문제가 발생 가능성 높음
    • 코드의 수정 및 리팩토링이 필요할 수 있음

2. 프로젝트 시작 시 Core Data 선택

  • 장점
    • 처음부터 데이터 모델을 확실히 구성할 수 있음
    • 데이터 관리를 효율적으로 시작할 수 있음
  • 단점
    • 새로운 프로젝트를 시작하기 때문에 초기 설정이 필요하며, 추가 작업이 필요할 수 있음
    • 기존 데이터가 없기 때문에 초기 데이터 입력이 필요할 수 있음

프로젝트 생성

나는 처음부터 Core Data를 선택해서 프로젝트를 생성하였다.

초기에 생성된 프로젝트 파일 목록을 보면 Core Data Model이 생성된 걸 볼 수 있다.
해당 Data Model에 Entities(Todo)를 추가하고 Attributes(todo, createDate, isComplete)를 추가해준다. 해당 프로퍼티에 맞는 타입도 지정해주면 된다.


NSManagedObject 생성

Entities랑 Attributes 입력이 다 끝나면 NSManagedObject subclass를 생성해줘야 하는데, 그 전에 inspector에서 설정할 부분이 있다.
클래스를 설정하기 전에 Codegen을 선택해야 하는데 각 항목이 의미하는 바는 다음과 같다.

Codegen

Manual/None

  • 엔터티와 관련된 클래스를 생성하지 않음
  • 개발자가 직접 모든 코드를 작성해야 함
  • 일반적으로 Manual/None로 설정할 때 사용자 정의 NSManagedObject 하위 클래스를 작성하고 엔터티를 관리함

Class Definition (기본값)

  • NSManagedObject의 하위 클래스를 직접 작성해야 함
  • Core Data는 자동으로 생성된 하위 클래스를 제공하지 않음
  • 엔터티와 관련된 커스텀 NSManagedObject 클래스를 직접 작성해야 함

Category/Extension

  • Xcode가 엔터티에 대한 확장(extension)을 생성해줌
  • 이 확장을 사용하여 엔터티에 추가적인 속성, 메서드 또는 커스텀 로직을 추가할 수 있음
  • 기존 NSManagedObject 클래스를 직접 확장하는 방식

Codegen을 선택한 후 Xcode의 Editor - Create NSManagedObject Subclass... 를 선택하여 하위 클래스를 생성해준다.

Todo+CoreDataClass.swift, Todo+CoreDataProperties.swift 두가지 파일이 생성됐다.
각 파일이 의미하는 바가 뭘까? 그리고 심지어 지금 CoreDataProperties 파일은 빨간줄 대잔치 🤮

NSManagedObject Subclass

Todo+CoreDataClass.swift

  • "Todo" 엔터티의 관리 객체 관련 코드를 포함
  • 관리 객체 클래스의 코드를 정의하며, 엔터티의 속성, 관계 및 기타 메타데이터를 포함
  • 이 클래스는 직접 수정하지 않는 것이 좋음. 수정이 필요하다면 "Todo+CoreDataProperties.swift" 파일을 사용하여 사용자 지정 코드를 추가

Todo+CoreDataProperties.swift

  • "Todo" 엔터티의 속성 및 관계에 대한 사용자 지정 코드를 추가하는 곳
  • 속성에 대한 추가 로직, 계산 속성, 메서드 등을 작성 가능
  • 이 파일을 통해 "Todo" 엔터티에 대한 사용자 지정 동작 및 로직을 구현할 수 있음

CoreDataProperties 파일의 에러 발생 이유는 이러하다. 나는 Codegen을 Extension으로 선택해서 Xcode가 알아서 확장을 생성해줬는데 Subclass로 중복 생성됐기 때문에 문제가 있다고 알려주는 것이다. 그럼 해당 파일을 지워주면 된다~!

객체를 얻는 방법

NSManagedObjectContext

NSManagedObjectContext는 Core Data에서 데이터를 관리하고 조작하기 위한 핵심 클래스 중 하나이다. 이 클래스는 앱의 데이터 모델과 데이터베이스 간의 중간 역할을 수행하며, 데이터를 읽고 쓰고 변경하는 데 사용된다. NSManagedObjectContext를 사용하여 데이터의 생명주기를 관리하고 데이터의 일관성과 무결성을 유지한다.

주요 역할 및 속성

  • 데이터 관리
    • 데이터를 가져오고 저장하는 데 사용됨
    • 데이터를 읽어오거나 추가, 수정, 삭제 가능
    • 변경된 데이터는 임시로 저장되며, 데이터베이스와의 실제 저장은 save() 메서드를 호출할 때 이루어짐
  • 변경 추적
    • 데이터 변경 사항을 추적함
    • 변경 사항은 관리 객체(managed objects)의 상태에 반영되며, 변경 이력을 유지
    • 변경 사항을 저장하면 데이터베이스에 적용됨
  • 병합
    • 다중 스레드 환경에서 작업하는 경우, 데이터를 병합하고 동기화하는 데 사용됨
    • 메인 스레드와 백그라운드 스레드 간의 데이터 일관성을 유지하는 데 도움을 줌
  • 커밋
    • save() 메서드를 호출하여 변경된 데이터를 커밋하고 데이터베이스에 저장할 수 있음
    • 변경 내용이 롤백되거나 영구적으로 저장
  • 데이터 관리 범위
    • 특정 데이터베이스 연결과 연결된 데이터를 관리함
    • 다수의 NSManagedObjectContext 인스턴스를 사용하여 다중 데이터베이스 연결을 다룰 수 있음
  • 캐시
    • 데이터를 캐시하여 데이터베이스 연산을 최적화함
    • 캐시된 데이터는 읽기 작업을 가속화하고 중복된 쿼리를 방지함
  • 상위 및 하위 컨텍스트
    • 상위 및 하위 컨텍스트 구조를 지원함
    • 상위 컨텍스트에서 변경 사항을 만들고 하위 컨텍스트에서 변경 사항을 커밋하여 다중 레벨의 컨텍스트를 구성할 수 있음

내 코드

class ViewController: UIViewController {
    
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
    
    ...
    
}
  • UIApplication.shared
    • iOS 애플리케이션의 공유 인스턴스를 나타냄
    • 모든 iOS 애플리케이션에서 shared 프로퍼티를 통해 앱의 주요 객체에 접근할 수 있음
  • .delegate
    • 앱의 델리게이트(delegate) 객체에 접근함
    • 앱의 델리게이트는 앱의 주요 동작을 관리하고 제어하는 객체로, 일반적으로 UIApplicationDelegate 프로토콜을 채택한 클래스
  • as! AppDelegate
    • 델리게이트 객체를 AppDelegate 클래스로 캐스팅함
    • 이것은 델리게이트 객체가 실제로 AppDelegate 클래스의 인스턴스임을 확신하는 것
  • .persistentContainer
    • AppDelegate 클래스의 속성 또는 메서드 중 하나로, Core Data 스택(Core Data Stack)을 관리하는 객체인 NSPersistentContainer의 인스턴스를 반환함
    • 이 객체는 Core Data 애플리케이션의 핵심 부분 중 하나이며, 데이터베이스 관리와 데이터 모델 로딩을 담당
  • .viewContext
    • NSManagedObjectContext 객체를 반환함
    • NSManagedObjectContext는 Core Data에서 데이터를 읽고 쓰는 데 사용되며, 주로 앱의 사용자 인터페이스와 상호작용하고 데이터 저장소와 데이터 모델 사이의 중간 역할을 함
    • viewContext는 주로 메인 스레드에서 사용되며, 데이터 모델과 상호작용하는데 사용됨

따라서 해당 코드는 앱의 AppDelegate를 통해 Core Data 스택에서 메인 스레드에서 사용할 수 있는 NSManagedObjectContext를 얻는 방법이다. 이 NSManagedObjectContext는 데이터를 검색하고 저장하는 데 사용된다.

데이터 가져오고 보여주기

NSFetchRequest

NSFetchRequest는 Core Data에서 데이터를 검색하고 조회하기 위한 객체다. 이것은 데이터베이스에서 데이터를 쿼리하고 가져오는 데 사용되며, 검색 조건, 정렬 순서, 반환 유형 등을 지정할 수 있다. NSFetchRequest는 Core Data에서 데이터를 읽어오는 작업을 정의하며, 주로 NSManagedObjectContext와 같이 사용된다.

주요 속성 및 사용법

  • entity
    • 검색할 대상 엔터티를 지정
    • 엔터티는 데이터 모델에서 정의한 데이터 유형(테이블)을 나타냄
      (ex) let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Person")
  • predicate
    • 검색 조건을 지정하는 프레디케이트(Predicate)를 설정
    • 프레디케이트는 데이터 필터링에 사용됨
      (ex) let predicate = NSPredicate(format: "age >= %@", argumentArray: [25])
    • predicate를 설정하면 조건에 부합하는 데이터만 반환됨
  • sortDescriptors
    • 검색 결과를 정렬하는 데 사용
    • 정렬 순서를 나타내는 NSSortDescriptor 객체 배열을 설정할 수 있음
      (ex) let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
    • sortDescriptors를 사용하면 결과를 원하는 순서로 정렬할 수 있음
  • fetchLimit
    • 최대 검색 결과 수를 제한할 수 있음
    • 대량의 데이터를 일부만 검색할 수 있음
      (ex) fetchRequest.fetchLimit = 10
  • returnsObjectsAsFaults
    • 기본적으로 검색 결과를 객체의 불러올(파울트) 상태로 가져옴
    • 필요한 경우 객체를 실제로 로드하고 데이터를 채울 수 있음
      (ex) fetchRequest.returnsObjectsAsFaults = false
  • resultType
    • 반환 유형 설정
    • 기본값은 .managedObjectResultType, NSManagedObject 객체의 배열을 반환
    • .countResultType (검색 결과 수를 반환) 및 .dictionaryResultType (딕셔너리 형태로 반환) 등이 있음
  • propertiesToFetch
    • 반환하려는 속성 지정
    • 특정 열을 선택적으로 로드할 때 유용
  • includesSubentities
    • 하위 엔터티를 검색할지 여부를 설정
    • 기본적으로 true로 설정되어 있어 하위 엔터티도 검색 대상에 포함됨

내 코드

// Lifecycle
override func viewDidLoad() {
	super.viewDidLoad()

	...
	fetchTodo()
}

// Fetch Todo
private func fetchTodo() {
	let request = Todo.fetchRequest()
        
	do {
		self.todos = try context.fetch(request)
        DispatchQueue.main.async {
			self.tableView.reloadData()
		}
	} catch {
		print("fetchTodo Error")
	}
        
}
  • let request = Todo.fetchRequest()
    • 데이터를 가져오기 위한 NSFetchRequest 객체를 생성
    • Todo는 Core Data에서 정의한 엔터티로, fetchRequest() 메서드를 사용하여 해당 엔터티에 대한 검색 요청을 생성
  • do { ... }
    • 데이터를 가져오는 도중 발생할 수 있는 오류를 처리하기 위한 do-catch 구문
  • self.todos = try context.fetch(request)
    • 생성된 요청을 context (NSManagedObjectContext)에서 실행하여 데이터를 가져옴
    • 가져온 데이터는 self.todos에 할당
    • NSManagedObjectContext를 통해 데이터를 읽어오고 self.todos 배열에 할당하는 부분임
  • DispatchQueue.main.async { ... }
    • 가져온 데이터를 테이블 뷰에 표시하기 위해 메인 스레드에서 UI 업데이트를 수행하기 위한 비동기 블록을 생성
    • UI 업데이트는 메인 스레드에서만 수행 가능
  • self.tableView.reloadData()
    • 테이블 뷰를 리로드하여 새로운 데이터로 화면 갱신
    • 새로운 데이터가 테이블 뷰에 반영됨
  • catch { ... }
    • 데이터 검색 중에 오류가 발생하면 catch 블록이 실행되고, "fetchTodo Error" 메시지가 출력됨

📱 적용화면

profile
하지만 나는 끝까지 살아남을 거야!

0개의 댓글