기어가더라도 제대로

[SwiftUI-기초] @State가 쓸모 없어지는 지점에 대하여(class, struct) 본문

SwiftUI - 기초

[SwiftUI-기초] @State가 쓸모 없어지는 지점에 대하여(class, struct)

Damagucci-juice 2022. 11. 1. 15:34

스위프트의 특성인 구조체와 클래스의 차이점에 대해서 이야기를 해볼까합니다. 

둘의 차이점을 이해하기 위해서는 Identity라는 개념을 이해하는것이 중요합니다. 

뷰들 간에 데이터를 전달할 때, Struct로 전달하게 되면 뷰마다 고유한 Struct가 생기게 되서 서로 다른 값을 보여줄 가능성이 있습니다. 

뷰들간에 공통된 데이터를 보여주고자 할 때 그 타입을 Class로 선언하는데요. 왜 이런 차이가 발생하게 되었는지 알아보도록 하죠.

 

표지판과 교통 경찰의 관계

예를 들어 도로에서 교통 관리를 해주는 경찰관교통 표지판의 차이라고 할까요?

표지판의 경우에는 이해하기 쉽게 구조체에 은유를 하겠습니다. 결론부터 말하자면

표지판에 글자가 달라지면, 그 표지판은 전에 있던 표지판과 같은 표지판이 아닙니다. 

이 말이 어렵겠지만, 예를 들어 "서울 100KM" 가 쓰여있는 표지판을 그대로 똑 떼어다가 다른 위치에 설치한다고 가정해보겠습니다. 

"인천 20KM"로 내용을 바꿨을 때, 우리가 바라볼 땐 같은 표지판에 내용만 바뀐 거라고 생각이 듭니다. 하지만 Swift에선 다릅니다. 

내용이 바뀐 표지판은 같은 자리에 있더라도 전혀 다른 표지판이 됩니다. 이를 데이터 중심 identity 라고 요약할 수 있습니다. 

 

그렇다면 교통경찰을 서울의 주요 IC에 배치한 경우를 보겠습니다. 여기서 그는 음주운전 단속이라는 업무를 합니다. 그리고 시간이 지나서

그가 서울 외곽에 인천으로 향하는 고속도로에 배치된 경우를 생각해보죠. 여기서 그는 얌체 끼어들기 단속을 하고 있습니다. 

그가 하는 업무, 장소, 혹은 전하는 메시지는 다를 지언정 교통 경찰관이라는 사람 자체는 변하지 않았습니다. 

다른 데이터를 보여주더라도 같은 동일성을 내재한 경우입니다. 

 

이론적인 내용이라 이해하는 것이 어렵지만, 이 예시가 Struct와 Class 의 차이를 이해하는데 큰 도움이 됩니다. 

 

Struct 는 프로퍼티가 바뀌면 새로운 Struct 가 된다. - @State가 활약하는 지점

struct User {
    var firstName = "Bilbo"
    var lastName = "Baggins"
}

이런 예시가 시스템 내부적으로도 정확한 비유인지는 모르겠으나, 이해하기에 수월해서 좀 더 설명을 해보겠습니다. 

위와 같은 코드가 있습니다. 표지판에 내용이 바뀌면 새로운 표지판이 되 듯, Struct의 인스턴스도 값이 바뀌면 새로운 인스턴스가 된다고

생각을 해보겠습니다. 

struct ContentView: View {
    @State private var user = User()

    var body: some View {
        VStack {
            Text("Your name is \(user.firstName) \(user.lastName).")

            TextField("First name", text: $user.firstName)
            TextField("Last name", text: $user.lastName)
        }
    }
}
  • TextField 에서 값을 입력하면 user 라는 인스턴스의 프로퍼티가 업데이트가 되게 됩니다. 
  • @State Property Wrapper 는 아주 똑똑하게도 Struct의 값이 변하는 것을 인식할 수 있습니다. 
    • 예를 들어 String, Array, Int 프로퍼티가 값이 변하는 것을 인식할 수 있는 것처럼요. (이들은 모두 Struct)
  • 전체를 이루는 컨텐트 뷰도 struct인데, 이 안에서 프로퍼티를 바꾸는 함수를 선언한다고 하면 mutating 키워드를 씁니다.

struct 가 let으로 인스턴스가 선언될 때와 var로 선언될 때 를 비교하는 글이 있는데 한번 보시는 것도 좋겠습니다.

(2022.09.28 - [Swift - 기초] - [10일차] struct)

결론부터 말하면 위의 코드를 실행해서 텍스트 필드의 값을 업데이트하면 user 라는 프로퍼티는 한글자 한글자 바뀔 때마다 새로운  Struct의 인스턴스가 되는 것입니다. 업데이트된 String, Int, Array 등을 생각하면 좋습니다. 

ContentView 의 프로퍼티가 @State로 선언이 되어있고 그 값이 바뀌면, 그 변화를 인식해서 SwiftUI가 body를 새로 그립니다. 

좀 비효율적이라고 생각이 들 수도 있는데, 화면으로 그려지는 내용은 아주 빠르게 업데이트 되기 때문에 큰 차이가 없습니다. 

User 구조체를 Class 로 선언하면 어떤 일이 일어날까요? 

 

Class User 와 @State

class User {

}

 

이렇게 변경해도 문제없이 빌드가 됩니다. 그러나 TextField에 타이핑을 해보면 뭔가 달라지는 것을 느낄 수 있습니다.

Text Field 에 값을 바꿨는데도, Text 에 적혀있는 User의 이름은 변하지 않았습니다. 

아까 Class 의 인스턴스는 그 값이 변하더라도 고유한 identity를 지킨다고 했죠.

그래서 @State가 달려있어도, SwiftUI는 "으흠 ~ 같은 객체군, 바뀐게 없네" 라고 인식을 합니다.

자, 구조체의 경우에는 프로퍼티가 변경되어도 감지를 했는데 class의 경우에는 감지하지 못합니다.

그래서 뷰를 업데이트하지 않는 것이고요.(하지만 user 인스턴스 내부의 프로퍼티는 값이 변화했습니다.)

이 때 도와줄 새로운 property wrapper 가 있는데 그것은 @StateObject 입니다. 

 

결론

  1. Struct는 값이 바뀌면 identity도 바뀐다.
  2. Class는 값이 바뀌어도 identity가 동일하다. 
  3. Class를 다룰 때 @State 대신 @StateObject 를 사용한다. 

 

감사합니다.

 

참조 사이트

https://www.hackingwithswift.com/books/ios-swiftui/why-state-only-works-with-structs

 

Why @State only works with structs - a free Hacking with iOS: SwiftUI Edition tutorial

Was this page useful? Let us know! 1 2 3 4 5

www.hackingwithswift.com

https://www.hackingwithswift.com/articles/227/which-swiftui-property-wrapper

 

Which SwiftUI property wrapper to choose in any situation

Decide which property wrapper is the right choice for your needs.

www.hackingwithswift.com

 

Comments