iOS의 CoreData와 CRUD

호씨·2024년 12월 4일
2

CoreData와 UserDefaults 이해하기

CRUD 개념

CRUD는 일반적인 개발론에서 자주 사용되는 용어로, 다음과 같은 의미를 가진다:

  • Create: 데이터 생성
  • Read: 데이터 읽기
  • Update: 데이터 업데이트 (쓰기)
  • Delete: 데이터 삭제

예를 들어, 전화번호 앱에서의 CRUD는 다음과 같이 적용된다:

  • C: 새로운 전화번호를 등록한다
  • R: 저장된 전화번호 데이터를 조회한다
  • U: 저장된 전화번호를 수정한다
  • D: 전화번호를 삭제한다

데이터 CRUD는 네이티브(앱) 내부에서도, 서버에서도 일어날 수 있다.

CoreData

CoreData는 앱에서 기기의 디스크에 데이터를 읽고 쓸 수 있게 돕는 프레임워크이다. Swift로 기기 내 디스크에 데이터를 저장할 수 있는 대표적인 방법으로는 CoreData와 UserDefaults가 있다.

1. CoreData 프로젝트 생성

  • 프로젝트 생성 시 Storage → CoreData를 선택한다.

2. Entity 생성

  • Add Entity 버튼을 클릭해서 Entity를 만든다. Entity는 저장될 데이터들의 집합이다.
  • PhoneBook이라는 Entity를 만들고, Attribute로 name과 phoneNumber를 추가한다.

Codegen 개념

  • Code Generator의 줄임말로, Entity를 어떤 형식의 코드로 생성할지 선택하는 속성이다.
  • Manual/None: Entity의 서브 클래스를 자동으로 생성하지 않고 개발자가 클래스 작성
  • Class Definition: Entity의 서브 클래스를 자동으로 생성
  • Category/Extension: Entity 클래스와 함께 extension을 위한 파일까지 생성

3. Code Generate

Editor → Create NSManagedObject Subclass를 클릭해서 코드를 생성한다.

PhoneBook+CoreDataClass.swift

import Foundation
import CoreData

@objc(PhoneBook)
public class PhoneBook: NSManagedObject {

}

PhoneBook+CoreDataProperties.swift

import Foundation
import CoreData

extension PhoneBook {
    @nonobjc public class func fetchRequest() -> NSFetchRequest<PhoneBook> {
        return NSFetchRequest<PhoneBook>(entityName: "PhoneBook")
    }

    @NSManaged public var name: String?
    @NSManaged public var phoneNumber: String?
}

extension PhoneBook : Identifiable {
}

4. NSPersistentContainer 생성

NSPersistentContainer는 CoreData에서 데이터를 저장하고 관리하는 데 필요한 핵심 객체이다.

5. CoreData를 활용한 CRUD

Create & Read 예제

import UIKit
import CoreData

class ViewController: UIViewController {
    var container: NSPersistentContainer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        self.container = appDelegate.persistentContainer
        
        createData(name: "Adam", phoneNumber: "010-1111-2222")
        readAllData()
    }

    // Create
    func createData(name: String, phoneNumber: String) {
        guard let entity = NSEntityDescription.entity(forEntityName: PhoneBook.className, 
              in: self.container.viewContext) else { return }
        let newPhoneBook = NSManagedObject(entity: entity, insertInto: self.container.viewContext)
        newPhoneBook.setValue(name, forKey: PhoneBook.Key.name)
        newPhoneBook.setValue(phoneNumber, forKey: PhoneBook.Key.phoneNumber)
        
        do {
            try self.container.viewContext.save()
            print("문맥 저장 성공")
        } catch {
            print("문맥 저장 실패")
        }
    }
    
    // Read
    func readAllData() {
        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("데이터 읽기 실패")
        }
    }
}

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.setValue(updateName, forKey: PhoneBook.Key.name)
            try self.container.viewContext.save()
            print("데이터 수정 완료")
        }
    } catch {
        print("데이터 수정 실패")
    }
}

Delete 예제

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)
            print("삭제된 데이터: \(data)")
        }
        
        try self.container.viewContext.save()
        print("데이터 삭제 완료")
    } catch {
        print("데이터 삭제 실패: \(error)")
    }
}

UserDefaults

UserDefaults 또한 디스크에 데이터를 저장할 수 있게 돕는 도구이다. CoreData보다 사용성이 간단하며, key와 value를 이용해서 값을 저장한다.

UserDefaults의 CRUD

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Create
        UserDefaults.standard.set("010-1111-2222", forKey: "phoneNumber")
        
        // Read
        let phoneNumber = UserDefaults.standard.string(forKey: "phoneNumber")
        print("저장된 전화번호: \(phoneNumber)")
        
        // Update
        UserDefaults.standard.set("010-6666-7777", forKey: "phoneNumber")
        let newPhoneNumber = UserDefaults.standard.string(forKey: "phoneNumber")
        print("바뀐 전화번호: \(newPhoneNumber)")
        
        // Delete
        UserDefaults.standard.removeObject(forKey: "phoneNumber")
        print("전화번호가 남아있는가: \(UserDefaults.standard.string(forKey: "phoneNumber"))")
    }
}

UserDefaults를 활용한 포스트잇 앱 예제

import UIKit
import SnapKit

class ViewController: UIViewController {
    private let label: UILabel = {
        let label = UILabel()
        label.text = "포스트잇"
        label.font = .boldSystemFont(ofSize: 30)
        label.textColor = .black
        return label
    }()
    
    private let textView: UITextView = {
        let textView = UITextView()
        textView.text = UserDefaults.standard.string(forKey: "memo")
        textView.layer.cornerRadius = 10
        textView.backgroundColor = UIColor(red: 75/255, green: 253/255, blue: 30/355, alpha: 1.0)
        textView.textColor = .black
        textView.font = .boldSystemFont(ofSize: 30)
        return textView
    }()
    
    private lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("적용", for: .normal)
        button.backgroundColor = .red
        button.setTitleColor(.white, for: .normal)
        button.titleLabel?.font = .boldSystemFont(ofSize: 20)
        button.layer.cornerRadius = 10
        button.addTarget(self, action: #selector(buttonTapped), for: .touchDown)
        return button
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        configureUI()
    }

    private func configureUI() {
        [label, textView, button].forEach { view.addSubview($0) }
        
        view.backgroundColor = .white
        
        label.snp.makeConstraints {
            $0.top.equalToSuperview().offset(100)
            $0.centerX.equalToSuperview()
        }
        
        textView.snp.makeConstraints {
            $0.top.equalTo(label.snp.bottom).offset(100)
            $0.centerX.equalToSuperview()
            $0.height.width.equalTo(200)
        }
        
        button.snp.makeConstraints {
            $0.top.equalTo(textView.snp.bottom).offset(50)
            $0.width.equalTo(60)
            $0.height.equalTo(40)
            $0.centerX.equalToSuperview()
        }
    }
 
    @objc
    private func buttonTapped() {
        UserDefaults.standard.set(textView.text, forKey: "memo")
        print("저장 완료")
    }
}
profile
이것저것 많이 해보고싶은 사람

0개의 댓글

관련 채용 정보