본문 바로가기
Swift

Swift_ CoreData Model , extension , Struct

by JunsC 2024. 8. 2.
728x90

코어 데이터 모델과 일반 Struct 과 같이 사용하려고 생각했다. 

AOS 에서는 한 모델을 기준으로 Sqlite 도 같은 형변환이 될 수 있도록 치환이 가능했기 때문이다.

그 부분때문에 당연히 IOS 에서도 가능하겠지 하고 계속 시도해보았지만 잘 안됐다. 흠...

 

그럼 CoreData / Extension / Struct 란 무엇인지 한번 알아보자 !!

 

1. CoreData Model

✅ CoreData란?

CoreDataiOS/macOS 앱에서 데이터를 저장하고 관리하는 프레임워크입니다.
데이터베이스(SQL)처럼 사용 가능하지만, 직접 SQL을 다루지 않아도 됨

✅ CoreData Model 정의

CoreData에서 데이터를 저장하려면 **엔티티(Entity)**를 정의해야 합니다.
Swift에서는 NSManagedObject를 상속받아 모델을 생성합니다.

✅ CoreData Model 예제

import CoreData

@objc(User) // CoreData에서 사용할 엔티티(User)
class User: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var age: Int16
}

 

📌 설명

  • NSManagedObject: CoreData에서 사용되는 객체
  • @NSManaged: CoreData가 관리하는 속성 (자동 저장/로드됨)
  • @objc(User): CoreData에서 인식할 수 있도록 클래스 등록

 

2. Extension (확장)

✅ Extension이란?

**Extension(확장)**은 기존 클래스, 구조체, 열거형, 프로토콜에 새로운 기능을 추가하는 기능입니다.
(원본 코드를 수정하지 않고도 기능을 확장 가능!)

✅ Extension 예제 1 - CoreData Model 확장하기

CoreData의 User 모델에 추가 기능을 확장해보겠습니다.

extension User {
    func userInfo() -> String {
        return "Name: \(name), Age: \(age)"
    }
}

 

📌 설명

  • extension User → 기존 User 모델을 확장하여 userInfo() 메서드 추가

✅ Extension 예제 2 - Swift 기본 타입 확장

extension Int {
    func squared() -> Int {
        return self * self
    }
}

let number = 5
print(number.squared()) // 출력: 25
 

📌 설명

  • Int 타입에 squared() 메서드 추가
  • number.squared() 호출 시 number * number 결과 반환

 

3. Struct (구조체)

✅ Struct란?

**Struct(구조체)**는 값 타입(Value Type)을 가지는 데이터 구조입니다.
→ 클래스(Class)는 참조 타입(Reference Type)

📌 클래스(Class) vs 구조체(Struct) 차이점

클래스 (Class)구조체 (Struct)

메모리 할당 Heap Stack
값 변경 참조(Reference) 값(Value) 복사
상속 가능 여부 ✅ 가능 ❌ 불가능
사용 용도 복잡한 데이터 관리 가벼운 데이터 관리

✅ Struct 예제

struct User {
    var name: String
    var age: Int
    
    func userInfo() -> String {
        return "Name: \(name), Age: \(age)"
    }
}

let user1 = User(name: "Alice", age: 25)
print(user1.userInfo()) // 출력: Name: Alice, Age: 25
 

📌 설명

  • struct User → 사용자 정보를 저장하는 구조체
  • userInfo() 메서드로 정보 반환
  • user1을 생성해도 값이 복사되므로 원본이 변경되지 않음

 

4. CoreData Model + Struct + Extension 함께 사용하기

CoreData와 Struct를 함께 사용하는 경우도 많습니다.
→ CoreData는 NSManagedObject를 사용하지만, Struct로 변환하여 사용하면 더 편리

✅ CoreData Model을 Struct로 변환

import CoreData

// CoreData 모델 (NSManagedObject)
@objc(UserEntity)
class UserEntity: NSManagedObject {
    @NSManaged var name: String
    @NSManaged var age: Int16
}

// Struct 변환
struct User {
    var name: String
    var age: Int
}

// CoreData → Struct 변환 Extension
extension User {
    init(userEntity: UserEntity) {
        self.name = userEntity.name
        self.age = Int(userEntity.age)
    }
}
 

📌 설명

  • UserEntity: CoreData에서 사용하는 모델
  • User: Struct로 변환하여 값 타입으로 사용 가능
  • init(userEntity:)을 사용하여 CoreData 데이터를 Struct로 변환

 

이렇게 기본적인 각 요소들의 의미를 알아보았으니 이제 내가 하려는 부분에 대해 깊이 이해해보자 !!

enum Gender: String, Codable {
    case M = "M"
    case F = "F"
    case NULL = "NULL"
}


enum CurrentStatus: String, Codable {
    case IN = "IN"
    case OUT = "OUT"
    case NULL = "NULL"
}

enum Time: String, Codable {
    case afternoon = "afternoon"
    case NULL = "NULL"
    case night = "night"
}

enum Grade: String, Codable {
    case A = "A"
    case B = "B"
    case C = "C"
    case D = "D"
    case E = "E"
    case NULL = "NULL"
}

struct MemberStruct: Codable, Comparable {
    var clubName: String
    var name: String
    var gender: Gender
    var memberOwnId: String
    var currentStatus: CurrentStatus
    var age: Int
    var grade: Grade
    var time: Time
    
    enum CodingKeys: String, CodingKey {
        case memberOwnId = "m_Id"
        case name
        case gender
        case currentStatus
        case clubName
        case grade
        case age
        case time
    }
    
    
    init(clubName: String = "", name: String = "", gender: Gender = .NULL, memberOwnId: String = "", currentStatus: CurrentStatus = .NULL, age: Int = 0, grade: Grade = .NULL, time: Time = .NULL) {
        self.clubName = clubName
        self.name = name
        self.gender = gender
        self.memberOwnId = memberOwnId
        self.currentStatus = currentStatus
        self.age = age
        self.grade = grade
        self.time = time
    }
    
    init() {
        self.clubName = ""
        self.name = ""
        self.gender = .NULL
        self.memberOwnId = ""
        self.currentStatus = .NULL
        self.age = 0
        self.grade = .NULL
        self.time = .NULL
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        clubName = try container.decode(String.self, forKey: .clubName)
        name = try container.decode(String.self, forKey: .name)
        gender = try container.decode(Gender.self, forKey: .gender)
        memberOwnId = try container.decode(String.self, forKey: .memberOwnId)
        currentStatus = try container.decode(CurrentStatus.self, forKey: .currentStatus)
        age = try container.decode(Int.self, forKey: .age)
        grade = try container.decodeIfPresent(Grade.self, forKey: .grade) ?? .NULL
        time = try container.decode(Time.self, forKey: .time)
    }
    
    static func < (lhs: MemberStruct, rhs: MemberStruct) -> Bool {
        return lhs.name < rhs.name
    }
    
    static func == (lhs: MemberStruct, rhs: MemberStruct) -> Bool {
        return lhs.name == rhs.name
    }
    
    func toMembers(context: NSManagedObjectContext) -> Members {
        let members = Members(context: context)
        members.clubName = self.clubName
        members.name = self.name
        members.gender = self.gender.rawValue
        members.memberOwnId = self.memberOwnId
        members.currentStatus = self.currentStatus.rawValue
        members.age = String(self.age)
        members.grade = self.grade.rawValue
        members.time = self.time.rawValue
        return members
    }
    
}

 

이 일반적인 Struct 를 하나로 전부 이용하고 싶었지만,
CoreData 에서는 CoreDataProperties.swift 파일은 Core Data의 속성 확장만을 포함해야 해서 일반적으로 이 파일에 메서드 확장보다는 속성 관련 코드만 넣는다고 한다.

따라서, 별도의 extension으로 정의하는 것이 좋다고 하는 ChatGpt 의 말에 따라 하는 수 없이 별도 분리를 하는 수밖에 없었다.

 

//
//  Members+CoreDataProperties.swift
//
//
//  Created by macbookpro on 7/24/24.
//
//

import Foundation
import CoreData


extension Members  {
    
    @nonobjc public class func fetchRequest() -> NSFetchRequest<Members> {
        return NSFetchRequest<Members>(entityName: "Members")
    }
    
    @NSManaged public var clubName: String?
    @NSManaged public var name: String?
    @NSManaged public var gender: String?
    @NSManaged public var memberOwnId: String?
    @NSManaged public var currentStatus: String?
    @NSManaged public var age: String?
    @NSManaged public var grade: String?
    @NSManaged public var time: String?
    
    
    
    
    func update(from member: MemberStruct) {
        self.clubName = member.clubName
        self.name = member.name
        self.gender = member.gender.rawValue
        self.memberOwnId = member.memberOwnId
        self.currentStatus = member.currentStatus.rawValue
        self.age = String(member.age)
        self.grade = member.grade.rawValue
        self.time = member.time.rawValue
    }
    
    
    func toMemberStruct() -> MemberStruct? {
        guard
            let clubName = self.clubName,
            let name = self.name,
            let gender = self.gender,
            let memberOwnId = self.memberOwnId,
            let currentStatus = self.currentStatus,
            let ageString = self.age,
            let age = Int(ageString),
            let grade = self.grade,
            let time = self.time
        else {
            return nil
        }
        return MemberStruct(
            clubName: clubName,
            name: name,
            gender: Gender(rawValue: gender) ?? .NULL,
            memberOwnId: memberOwnId,
            currentStatus: CurrentStatus(rawValue: currentStatus) ?? .NULL,
            age: age,
            grade: Grade(rawValue: grade) ?? .NULL,
            time: Time(rawValue: time) ?? .NULL
        )
    }
    
    
    override public var description: String {
        return """
            Member{
                clubName: \(String(describing: clubName)),
                name: \(String(describing: name)),
                gender: \(String(describing: gender)),
                memberOwnId: \(String(describing: memberOwnId)),
                currentStatus: \(String(describing: currentStatus)),
                age: \(String(describing: age)),
                grade: \(String(describing: grade)),
                time: \(String(describing: time))
            }
            """
    }
    
}

 

위의 코드는 이제 잘못된 코드이다.. 

저런 부가적인 함수들은 따로 extension 파일을 만들어 정리하도록 해야겠다.

 

그렇다면 내가 이해한 바로는 , 

코어데이터와 일반적인 Struct 는 각 독립적인 모델로 생각해야 하고 서로 상호호환을 하려면 형변환을 해주어야 한다는 의미로 생각되었당

그래서 우선 코어데이터로 저장하는건 코어데이터 모델을 사용하고 그리고 그 데이터를 가져올땐 Struct 로 형변환 후 사용하는 것이 깔끔한듯 하다

AOS IOS 각각의 장단점이 있는 듯 하다..

 

 

 

'Swift' 카테고리의 다른 글

Swift_ 변수의 종류  (0) 2025.02.11
Swift_ intrinsicContentSize / Frame / bounds  (0) 2025.02.08
Swift_ CoreData 적용기...  (0) 2024.07.24
"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."