안녕하세요! 보노입니다 🦦
제가 데이터의 정체성(identity of the data) 라는 말을 처음 접한 건
class
와 struct
의 선택 기준에 대하여 다룬 공식문서를 읽은 이후 입니다.
Choosing Between Structures and Classes
공식 문서엔 아래와 같은 구절이 존재합니다.
모델링하는 데이터의
identity
를 컨트롤해야 할 때는class
를 사용하라
여기서 데이터의 아이덴티티 (identity of the data) 란 무엇을 의미할까요?
본 포스팅을 통해 class의 선택 기준이 되는 데이터의 고유성(Identity) control 이란 무엇인지 이해하고자 합니다.
저는 함수형 프로그래밍을 좋아하여 값 타입 활용 방안에 관심이 많고,
그렇기에 SwiftUI가 struct를 다루는 방식에도 관심이 많습니다.
불변성
의 가치에 대해 논하자니 고유성
에 대한 이해도 구체화하고 싶어 포스팅을 기획하게 되었습니다.
현재의 이해 수준을 기록한다!에 의미를 두고 작성하였습니다.
주관이 많이 섞인 것 같아 조심스럽지만, 어쩌면 당연한 내용을 다룹니다.
늘 그렇듯 누군가에겐 도움이 되는 기록이길 바랍니다.
감사합니다!
AppleDeveloper_Choosing Between Structures and Classes
identity 라는 키워드를 어디서 마주할 수 있는가.
애플 공식 문서는 class
와 struct
선택 기준에 대해 아래와 같아 말한다.
struct
를 사용하라class
를 사용하라identity
를 컨트롤해야 할 때는 class
를 사용하라protocol
과 함께 struct
를 사용하라첫 번째 문장이 강렬하다.
기본적으로
struct
를 사용하라
지난 WWDC 2016 포스팅에서,
나는 어떤 데이터 타입이 필요한 지 고려할 줄 알아야 겠다- 는 깨달음으로 포스팅을 마무리 지었다.
그러나 머쓱하게도 공식 문서에선 일단 struct 사용을 권고한다.
그도 그럴 것이 swift는 타 언어와 달리 struct에 많은 기능을 부여하였기 때문이다.
swift를 사용하며 class의 필요를 느낄 상황은 흔치 않다.
그렇다면 class를 선택할 상황은 정말 없는 걸까?
(명쾌하게도) 위의 세번째 문장에서 class
를 선택해야 할 상황이 언급된다.
모델링하는 데이터의
identity
를 컨트롤해야 할 때는class
를 사용하라.
위 문장을 통해 나는 identity 라는 말을 처음 접했다.
identity의 사전적 뜻은 고유성
, 독자성
, 신원
이다.
아이디 비밀번호 할 때 그 id가 바로 identity의 약어이기도 하다.
무엇이 데이터를 고유하도록, 식별 가능하도록 할까?
더 나아가 데이터의 식별성을 조절 한다는 말이 무슨 뜻 일까?
먼저 Identity
란 무엇일까 고민하며 자연스레 변수 / 상수 개념을 돌아보게 되었다.
변하지 않는 정체성- 에 대해 고민하자면 변하지 않는 값, 상수
(let)가 떠오르기 때문이다.
그렇게 var
랑 let
이 뭐였는지 생각하다 새삼스레 발견하게 된 것이 있다.
var test: String = ""
바로 위 변수(또는 상수)의 이름인test
를 부르는 명칭이 Identifier
, 식별자
라는 것이다.
매번 '이름'이라고 불렀기에 Identifier
라는 명칭이 있었음을 완전히 잊고 있었다.
(흥미롭긴 한데 중요한 내용은 아니다)
아무튼 변수 상수의 이름,
즉 Identifier가 데이터를 식별하는 기준은 뭘까?
struct
와 class
의 근본적인 차이라 하면, 값 타입이냐 참조 타입이냐이다.
struct 데이터의 식별자는 스택에 저장된 값을 바라보고,
class의 식별자는 힙에 저장된 인스턴스 주소를 가지고 값을 바라본다.
자연스레 얻는 깨달음이 있다.
아. 구조체에겐
원본
개념이 없구나
struct는 값타입
으로 데이터 자체에 특별한 정체성이 존재하지 않는다.
스택 내부에서 생성되고, 사라지며 누군가 보고 있다는 이유로 유지되지 않기 때문이다. (참조 카운트 증가 개념이 없음)
+)
변수(var
)로 선언된 struct 인스턴스를 변경하는 것을 원본을 변경하는 일이라 착각할 수 있지만 이는 원본을 변경하는 상황이 아니다.
바뀌어도 괜찮은 그릇(변수)에 새 값을 담는 행위이다.
(변수 상수는 값을 담는 그릇으로, 자신이 담은 값의 정체성을 대변할 수 없다)
struct, 값타입
은 특정 이벤트의 결과일 뿐이고,
복사되면 새로운 메모리 공간에 기존 값으로부터 독립된 형태로 생성된다. (깊은 복사)
즉 값 타입에는,
원본
을 변경한다-란 개념이 존재하지 않는다.
struct의 인스턴스에는 원본 개념이 없다. 라고 말하려 들면 자연스레 이런 질문이 떠오른다
값 타입도 inout을 활용하면 원본변경 아닌가?
내가 inout 개념을 주소값
을 들고 들어간다 - 로 학습하였기 때문에 이런 혼란이 생긴 것 같다.
코드 예시를 들어보자
var test: Int = 1
func addOne(_ value: inout Int) { value = value + 1 }
test // 1
addOne(test)
test // 2
이 상황이 1이라는 Int 인스턴스 값의 원본 값을 변경하는 상황일까?
아니다.
위 코드는 동작상 아래와 같다.
var test: Int = 1
test // 1
test = test + 1
test // 2
inout이 알게되는 파라미터의 주소값
이란 변수가 선언된 메모리의 주소
이다.
inout은 식별자
에 접근하는 주소를 통해 test라는 식별자에 담길 값을 변경하고 있다.
즉 기존의 값을 유지(identity)하면서 변경하는 행위가 아니다.
test라는 식별자는 test = test + 1 의 실행 전과 후에 전혀 다른 값을 가지게 된다.
그게 값 타입의 특징이다.
프로그램에게는 struct로 생성된 값이 유지
되고 있음을 판별할 기준이 없다.
변경은 곧 갱신이다.
다시 돌아가 아래 문장을 보자.
모델링하는 데이터의
identity
를 컨트롤해야 할 때는class
를 사용하라.
identity
란 데이터의 정체성을 대변하는 요소로, 문맥상 데이터의 주소를 의미하는 것으로 보인다.
그러니 identity
컨트롤이란 주소가 유지되는 상황 전제를 통한 여러 접근을 의미하는 게 아닐까 추측할 수 있다.
여기까지 생각을 좁히자 자연스럽게 뒤따르는 또 다른 질문이 있었다.
Identity와 Identifiable은 무슨 차이지 ?
Identifiable은 swift의 프로토콜이다.
인스턴스(class)가 안정된 identity를 가진 엔티티의 값을 보유함을 보증한다
설명 상으론 객체로 상황을 제한하는 것 같지만, AnyObject라는 제한이 걸려있지 않다.
더불어 struct 타입의 데이터를 만들다 보면 Identifiable이라는 채택할 경우가 제법 흔한 것 같다.
떠오르는 예시를 들자면, SwiftUI에서는 ForEach를 돌리기 위해서 Identifiable한 데이터나 hashable한 내부 속성값을 요구한다.
그러니 나는 익숙히 struct에 Identifiable을 채택시켜 주어 id를 정의해주었다.
위의 이해를 토대로 바라보니 이해가 된다.
컴퓨터 기준으로 값 타입에는 원본 개념이 없다.
그러나 우리의 프로젝트는 이전 값과 이후의 값이 같은 데이터를 기준으로 탄생했음을 식별해야 할 일이 생긴다.
즉, 값 타입에게 식별 기준을 요구하게 된다.
이때 Identifiable은 식별성을 보증하는 프로토콜로, 값 타입에게 명시적으로 구분자를 제공하는 역할로 활용될 수 있다.
이런저런 생각을 하던 차에 내 생각을 뒷받침해 줄 아티클을 하나 읽게 되었다.
Identifiable protocol in SwiftUI explained with code examples
두둥.
! class에 Identifiable 채택 시 Id 구현이 필수적이지 않다.
객체의 Id값은 자동으로 ObjectIdentifier
타입으로 할당된다.
즉, 따로 id
를 만들어주지 않아도 객체의 주소값이 데이터의 식별 요소가 된다.
이제껏 너무 당연한 고민을 했나? 싶었던 것은 Identity Operators의 존재를 알게 된 이후다.
Identity Operators는 객체의 고유성을 판별하기 위한 연산자이다.
해당 연산자는 객체의 Identity를 비교하기 위해 참조(주소)를 비교한다.
만약 struct 인스턴스의 비교를 수행하고자 한다면,
클래스 타입이 아니라는 경고가 출력된다.
identifiable을 채택한 구조체라도 마찬가지다.
명시적으로 식별성을 심어준다 한들 값 타입에겐 원본 개념이 없다.
편의를 위해 심어준 식별성은, 데이터의 식별성(참조)을 대변해주지 못한다.
값 타입에게는 컴퓨터가 그 정체성을 식별한 만한 요인이 없다.
일반적으로 정체성(identity)이란 객체(클래스 인스턴스)의 주소를 의미한다.
모델링하는 데이터의
identity
를 컨트롤해야 할 때는class
를 사용하라.
아래는 내 나름 구축한 생각이다.
identity
를 컨트롤해야 할 때 란,
유지가능한 데이터에- 여러 접근이 이루어져야 할 때- 를 의미한다.
덧붙여 struct는 본질적으로 식별이 불가능하다.
Identifiable을 채택하여 구조체에게도 Id(id)를 심어주는 것은 서로다른 값이라도 id를 통해 '같다'고 판별할 수 있도록 식별 요소를 제공할 뿐,
데이터가 자체가 유지되는-는 참조타입의 특성을 주입하는 것 아니다
공식 문서에서의 Identity는
식별성이 존재하는, 유지되는 데이터만이 가지는 객체의 주소를 의미한다.
하지만 프로그래밍에 한정되지 않은 단어인 Identity, 실생활에서도 사용되는 단어에 개발자로서 엄밀한 정의를 입히고자 하는 건 위험할 수 있다.
내 스스로 중요하다 생각하는 것은
프로그램이 데이터를 구분 짓는 기준과,
우리가 데이터를 구분 짓는 기준이 다를 수 있음을 염두에 두는 것이다.
본 포스팅에서 다루며 정리한 바를 옮기자면 아래와 같다
사용단의 주기보다 오래 유지되어야 하는 데이터는 그 정체성(identity) 이 유지되어야 하므로 class로 두어야 겠다
값 타입(struct)은 원본이 없다. 즉 식별할 수 없다.
하지만 우리의 필요에 의해 명시적으로 식별성을 심어줄 수 있다. 그러나 이는 데이터를 구분하기 위한 사용자의 수단으로 그 값이 유지된다는 의미는 아니다.
유지된다- 는 원본 개념은 오직 참조 타입에만 존재한다
적절히 판단하여 활용하자
최근엔 PointFree 영상을 보며 함수형 프로그래밍에 대한 재미를 더욱이 찾고 있습니다.
아직은 풀리지 않은 궁금증이 많아 다루지 못하지만, 언젠가는 SwiftUI를 좋아하는 이유에 대해서도 다루어볼 수 있다면 좋겠습니다.
댓글은 언제나 확인 중이며, 피드백이나 질문 남겨주시면 빠르게 확인하겠습니다.
감사합니다! 즐거운 개발 되세요!
처음 swiftUI를 접했을때
forEach를 쓰며 id에 .self를 썼을때 identity에 대한 이해가 부족했어서 원하는대로 UI가 그려지지않았던 기억이나네요 ㅎㅎ
사실 구조체와 클래스중에 어떤걸선택해야할지에대해 처음고민하게되는 상황은 viewModel을 구조체로 해야하나 class로해야하나에 대한 부분일텐데 저도 이부분을 고민하면서 보노가고민했던부분을 함께고민해봤던것같습니다!
제가 당시에 해당부분을 공부할때
기본적으로 struct를 사용하라에서 아마 모든경우에 기본적으로 struct를사용하라는것이기보다는 해당부분의 subtitle을 보면 struct 를 기본적으로 사용하라고 이야기한 내용은 data model 에 관한 이야기라는걸 알수있습니다! 그래서 제네럴하게 struct를기본으로써라!기보다는 데이터 model을 만들때는 struct를 기본으로쓰고 나머지경우는 identifier를 고려해서 선택해라~라고 받아들이는게맞지않을까라는생각이들었거든요 ㅎㅎ
https://showcove.medium.com/swift-struct-vs-class-1-68cf9cbf87ca
제가 참고했던 블로그인데 읽어보시면 좋을거같습니다!