기어가더라도 제대로

[iOS-Swift] CoreData, NSManagedObject Subclass 에서 옵셔널 제거하기 본문

Swift - 데이터베이스

[iOS-Swift] CoreData, NSManagedObject Subclass 에서 옵셔널 제거하기

Damagucci-juice 2022. 12. 2. 02:12

CoreData 옵셔널의 의미

CoreData는 기본적으로 Entity에 프로퍼티가 값이 옵셔널 타입이다.
여기서 옵셔널의 의미는 Swift 에서 사용하는 옵셔널의 의미와 조금 다르다. 

  • Swift: 이 변수에 값이 있을수도 있고, 값자체가 없을 수도 있음
  • CoreData: 인스턴스가 생성이 되서 프로퍼티에 값을 할당하기 전에 빈 공간을 만들어준다는 의미

이러한 차이가 있다. 보통 엔티티를 만들고 Codegen을 통해서 파일을 만들면 Swift 파일 두개가 생성이 되는데, 잘 보면 그 안에  형식이 다음과 같다. 

Entity의 name 프로퍼티가 String 타입이지만, 옵셔널을 달고 있는데, 위에서 말한 것 처럼 swift 적 옵셔널이 아니라 CoreData 적인 옵셔널이다. 

@NSManaged는 Property wrapper 가 아니다. 이 프로퍼티는 CoreData에 의해서 '관리되어 저장되고 읽힐 수 있음'을 표시하는 것이다. 물론 Wizard 엔티티에 저장안되는 변수를 선언할 수 있다. 

옵셔널이 아닌 @NSManaged 프로퍼티가 있을 수 있는데, 이는 기본값이 주어지는 변수에 해당한다. 

옵셔널을 지우고 싶어요. 

  • 타입에서 '?'를 지우는 방법
  • 연산 프로퍼티를 추가하는 방법

이 글이 길어진다면 전적으로 타입에서 '?'를 지우는 방법을 설명하다가 길어질 것이다. 그냥 단순하고 직관적으로 지우면 될 거 같은데, 그게 아니라 설명할 것이 조금 있다.

만약에 name 프로퍼티에 타입을 String 이라고 주면, name 프로퍼티에 값을 넣지 않고 Wizard를 생성하는 것이 가능하다. 

근데 이래서는 안된다. 왜냐면 비옵셔널이라는 것은 언제나 항상 값을 들고 있어야 한다. 논리적으로 값을 들고 있지 않는 순간이 생겨서는 안되는 것이다. 이 때문에 일반적인 Swift의 옵셔널과 CoreData 에서 말하는 옵셔널이 다르다고 말하는 것이다.

여기서 @NSmanaged가 마법을 부린다. 이것들은 실제 존재하는 프로퍼티가 아니고, 그 덕분에 @NSManaged 키워드가 해서는 안되는 일을 할 수 있도록 도와주는 것이다. 여기서 해서 안되는 일이란, 비옵셔널 프로퍼티에 값을 넣지 않고 인스턴스를 생성하는 것을 말한다. 

그럼 타입에서 '?'를 지우면 해결되는 일이라고 물어볼 수도 있겠다. 하나 문제가 있는데, CoreData가 Lazy 하다는 것이다. Lazy는 실제로 그 작업을 하기 전까지는 그 일을 하지 않는다는 키워드인데, (설명이 어렵다ㅠ) 코어 데이터는 기본적으로 Lazy 다. 무슨 말이냐면 코어데이터에 값에 접근하기 전에 우리가 그 값을 볼 수 있는 것처럼 느껴지지만, 실제로 접근을 하는 그 순간에야 값을 읽어 온다. 근데 엔티티를 만들기만 하고 값을 프로퍼티에 넣어주지 않게 되면, 특별한 위험에 처하는 것이다. 다시 말하면, 우리가 nil이 아니라고 선언한 타입이 어느 특정한 순간에는 nil일 수도 있다는 것이다. 와우... 단순히 물음표 하나 지우는게 아니였다니... 물음표라는 혹을 때려다가 앱이 강제 종료 크러시가 나는 혹을 붙인 꼴이다. 그래서 안전한 방법을 사용하자. 그게 두번째 방법인 연산 프로퍼티를 추가하는 것이다.

이렇게 하면, 코드가 엔티티가 사용되는 곳에서 옵셔널 언래핑을 하느라 지저분해지지 않고, extension 한 곳에서 언래핑을 전부 관장하니까 훨씬 관리하기가 수월하다. 런타임 에러가 날 가능성도 없어 안전하다. 

결론

CoreData 옵셔널 타입 언래핑은 안전한 연산 프로퍼티 extension을 사용하자.

Comments