ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • CoreData와 실습(2)
    카테고리 없음 2024. 7. 10. 16:42

    CoreData 를 활용한 CRUD(Create, Read, Update, Delete)

    이전에 NSPersistentConatiner 를 만들었으므로, ViewController 에서 이를 활용해서 데이터 접근

     

     

    CoreData와 실습(1)

    CoreData 는 앱에서 기기의 **디스크**에 데이터를 읽고 쓸 수 있게 돕는 프레임워크 Swift 로 기기 내 디스크에 데이터를 저장할 수 있는 대표적인 방법으로는CoreDataUserDefaults 1. CoreData 프로젝트 생

    bluewiper.tistory.com

     

     

    1. CoreData 를 통해 CRUD 의 C, R 을 하는 과정

    • ViewController.swift
    • entityName, forKey를 위해 "PhoneBook", "name", "phoneNumber"처럼 일일이 String값을 입력해서 호출한다. 
    • 일일이 String값을 입력해주는 일을 줄이기 위해 이후에 리팩토링을 하게 된다. 
    import UIKit
    import CoreData
    
    class ViewController: UIViewController {
    //ViewController의 프로퍼티인 container가 Appdelegate에 있는 persistentConatiner를 참조하게 되는 과정
        var container: NSPersistentContainer!
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            self.container = appDelegate.persistentContainer
            
            createData(name: "Jean", phoneNumber: "010-1111-2222")
            readAllDate()
        }
        
     //CoreDataPractice에 데이터 CRUD 중 Create를 하는 과정 *Entity 형태를 지켜서 삽입
        func createData(name: String, phoneNumber: String) {
            guard let entity = NSEntityDescription.entity(forEntityName: "PhoneBook", in: self.container.viewContext) else { return }
            //새롭게 저장할 PhoneBook entity 객체 생성
            let newPhoneBook = NSManagedObject(entity: entity, insertInto: self.container.viewContext)
            //newPhoneBook의 Attribute(속성) 지정
            newPhoneBook.setValue(name, forKey: "name")
            newPhoneBook.setValue(phoneNumber, forKey: "phoneNumber")
            //데이터에 변조(CoreDate에 데이터 생성)가 일어났으므로 Appdelegate의 saveContext 메서드를 사용해야한다.
            do {
                try self.container.viewContext.save()
                print("문맥 저장 성공")
            } catch {
                print("문맥 저장 실패")
            }
            
        }
        
        //CoreDataPractice에서 데이터 CRUD 중 Read를 하는 과정
        func readAllDate() {
            do {
                let phoneBooks = try self.container.viewContext.fetch(PhoneBook.fetchRequest())
                
                for phoneBook in phoneBooks as [NSManagedObject] {
                    if let name = phoneBook.value(forKey: "name") as? String,
                       let phoneNumber = phoneBook.value(forKey: "phoneNumber") as? String {
                        print("name:\(name), phoneNumber: \(phoneNumber)")
                    }
                }
            } catch {
                 print("데이터 읽기 실패")
            }
        }
    
    }

     

    디스크에 저장했으므로, 데이터를 저장한 코드를 삭제한 뒤 앱을 다시켜도 데이터가 남아있음을 확인 가능

     

     

    리팩토링 전 PhoneBook+CoreDataClass.swift

    import Foundation
    import CoreData
    
    @objc(PhoneBook)
    public class PhoneBook: NSManagedObject {
    
    }

     

    리팩토링 후 PhoneBook+CoreDataClass.swift

    import Foundation
    import CoreData
    
    @objc(PhoneBook)
    public class PhoneBook: NSManagedObject {
        public static let className = "PhoneBook"
        public enum Key {
            static let name = "name"
            static let phoneNumber = "phoneNumber"
        }
    }

     

    → static 프로퍼티는 그 타입에 대고 호출을 할 수 있는 프로퍼티이다.

    → PhoneBook.className → PhoneBook 이라는 클래스 타입에 대고 점을찍고 호출

     

    왜 하는 것일까?

     

    1. 자동완성 기능을 사용할 수 있다.

    2. 직접 String 값인 "phoneNumber"라고 치지 않아도 되니 오타와 같은 휴먼에러를 줄인다. 

    3. 값을 수정할 일이 생길 때 일일이 찾아가지 않아도 PhoneBook+CoreDataClass.swift 한 곳에서 수정할 수 있다. 

    4. 성격이 같은 프로퍼티끼리 모아서 관리할 수 있다. 

     

     

    리팩토링 후 ViewController.swift

    import UIKit
    import CoreData
    
    class ViewController: UIViewController {
    //ViewController의 프로퍼티인 container가 Appdelegate에 있는 persistentConatiner를 참조하게 되는 과정
        var container: NSPersistentContainer!
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            self.container = appDelegate.persistentContainer
            
            createData(name: "Jean", phoneNumber: "010-1111-2222")
            readAllDate()
        }
        
     //CoreDataPractice에 데이터 CRUD 중 Create를 하는 과정 *Entity 형태를 지켜서 삽입
        func createData(name: String, phoneNumber: String) {
            guard let entity = NSEntityDescription.entity(forEntityName: PhoneBook.className, in: self.container.viewContext) else { return }
            //새롭게 저장할 PhoneBook entity 객체 생성
            let newPhoneBook = NSManagedObject(entity: entity, insertInto: self.container.viewContext)
            //newPhoneBook의 Attribute(속성) 지정
            newPhoneBook.setValue(name, forKey: PhoneBook.Key.name)
            newPhoneBook.setValue(phoneNumber, forKey: PhoneBook.Key.phoneNumber)
            //데이터에 변조(CoreDate에 데이터 생성)가 일어났으므로 Appdelegate의 saveContext 메서드를 사용해야한다.
            do {
                try self.container.viewContext.save()
                print("문맥 저장 성공")
            } catch {
                print("문맥 저장 실패")
            }
            
        }
        
        //CoreDataPractice에서 데이터 CRUD 중 Read를 하는 과정
        func readAllDate() {
            do {
                let phoneBooks = try self.container.viewContext.fetch(PhoneBook.fetchRequest())
                
                for phoneBook in phoneBooks as [NSManagedObject] {
                    if let name = phoneBook.value(forKey: PhoneBook.Key.name) as? String,
                       let phoneNumber = phoneBook.value(forKey: PhoneBook.Key.phoneNumber) as? String {
                        print("name:\(name), phoneNumber: \(phoneNumber)")
                    }
                }
            } catch {
                 print("데이터 읽기 실패")
            }
        }
    }

     

    EntityName, forKey를 위해 이전처럼 String값이 아닌 클래스를 호출한다.

     

     

     

    Back to CRUD

     

    2. CoreData 를 통해 CRUD 의 U 하는 과정

        //CoreDataPractice에서 데이터 CRUD 중 name에 대해 Update를 하는 과정
        func updateData(currentName: String, updateName: String) {
            //업데이트를 위해 데이터를 찾는 과정
            let fetchRequest = PhoneBook.fetchRequest()
            fetchRequest.predicate = NSPredicate(format: "name == %@", currentName)
            
            do {
                let result = try self.container.viewContext.fetch(fetchRequest)
                
                for data in result as [NSManagedObject] {
                    // data 중 name을 updateName으로 update 한다.
                    data.setValue(updateName, forKey: PhoneBook.Key.name)
                }
                // 데이터 변조가 일어났으므로 save해준다.
                try self.container.viewContext.save()
                
                print("데이터 수정 성공")
            } catch {
                print("데이터 수정 실패")
            }
        }

     

    3. CoreData 를 통해 CRUD 의 D 를 하는 과정

        //CoreDataPractice에서 데이터 CRUD 중 Delete를 하는 과정 * name으로 데이터를 추적해서 삭제한다.
        func deleteData(name: String) {
            //삭제를 위해 데이터를 찾는 과정
            let fetchRequest = PhoneBook.fetchRequest()
            fetchRequest.predicate = NSPredicate(format: "name == %@", name)
            
            do {
                let result = try self.container.viewContext.fetch(fetchRequest)
                
                for data in result as [NSManagedObject] {
                    self.container.viewContext.delete(data)
                }
                // 데이터 변조가 일어났으므로 save해준다.
                try self.container.viewContext.save()
                
                print("데이터 삭제 성공")
                
            }
            catch {
                print("데이터 삭제 실패")
            }
        }

     

    CRUD별 호출 모습

    1. Create와 Read

    import UIKit
    import CoreData
    
    class ViewController: UIViewController {
        //ViewController의 프로퍼티인 container가 Appdelegate에 있는 persistentConatiner를 참조하게 되는 과정
        var container: NSPersistentContainer!
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            self.container = appDelegate.persistentContainer
            
            createData(name: "Jean", phoneNumber: "010-1111-2222")
            
            readAllData()
        }

     

    2. Update

    import UIKit
    import CoreData
    
    class ViewController: UIViewController {
        //ViewController의 프로퍼티인 container가 Appdelegate에 있는 persistentConatiner를 참조하게 되는 과정
        var container: NSPersistentContainer!
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            self.container = appDelegate.persistentContainer
            
            createData(name: "Jean", phoneNumber: "010-1111-2222")
         
            updateData(currentName: "Jean", updateName: "Yejin") // 아래에서 updateData라는 func를 만든 후 호출.
                    
            readAllData()
        }

     

    3. Delete

    import UIKit
    import CoreData
    
    class ViewController: UIViewController {
        //ViewController의 프로퍼티인 container가 Appdelegate에 있는 persistentConatiner를 참조하게 되는 과정
        var container: NSPersistentContainer!
        override func viewDidLoad() {
            super.viewDidLoad()
            
            let appDelegate = UIApplication.shared.delegate as! AppDelegate
            self.container = appDelegate.persistentContainer
            
    //       createData(name: "Jean", phoneNumber: "010-1111-2222")
    //        
    //        updateData(currentName: "Jean", updateName: "Yejin") // 아래에서 updateData라는 func를 만든 후 호출.
            
            deleteData(name: "Yejin")
            
            readAllData()
        }