ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Swift] SwiftUI 프로퍼티 래퍼 뿌시기 2: @Published 찍먹하기 (feat. ObservableObject)
    테크 2024. 3. 6. 21:28

     

    하... 사실 앞의 @State랑 @Binding은 빌드업이었던 것임ㅠ

    Observ... 어쩌고저쩌고만 봐도 머릿 속에 물음표만 생겼던 나 자신

    더 이상 미룰 수 없다. (왕진지)

     

    일단 ObservableObject 이 친구가 무엇인지 알아야할 것 같은 느낌이 강하게 들어서

    이 친구부터 먼저 도장깨보도록 하겠음!!!

     

    ※ Combine을 모르는 사람들을 위한 글입니다... (왜냐? 제가 모르기 때문에 ㅎㅋ)

    ObservableObject

    protocol ObservableObject : AnyObject

     

    • Combine을 사용하는 것으로 이벤트 방출(emit)이 가능 (무슨 소린지 모르겠어도 포기하지 말고 계속 읽어주세요!)
    • 클래스에서만 사용 가능
    • 일반적으로 ViewModel 구현 시 사용 (ViewModel이란 View가 필요한 데이터를 가지고 있는 Model)
    • ObservableObject 프로토콜을 따르는 클래스로 만들어진 인스턴스를 구독한다면 이벤트 변경을 실시간 트래킹 가능

    아래처럼 선언이 가능한데요!

    class Counter: ObservableObject {
        var count: Int = 0
    }

     

    참고로 ObservableObject를 공부하다보면 Combine이라는 말이 계속 등장합니다...

    근데 Combine이 뭐냐구요?ㅠ

    나도 잘 모르기 때문에 나중에 종권님 블로그 보면서 공부해야할 듯...

     

    일단 대애충 이야기 해보자면

    값을 내보내는/방출하는 Publisher와 이를 수신하는 Subscriber가 있고

    이를 통해서 비동기 처리를 할 수 있다는 겁니다.

    이 모든 걸 가능하게 하는 Framework가 Combine이라는 것이죠.

     

    어쨌든 이것만 기억해주시면 됩니다.

    Subscriber들은 방출된 값/이벤트 받아볼 수 있고
    Publisher가 방출한 값/이벤트 구독자가 받아볼 수 있음

     

    ObservableObject는 이벤트 방출이 가능하기 때문에 일종의 Publisher 역할을 하는 겁니다!

    이를 Subscriber가 구독한다면 방출된 이벤트를 계속 트래킹할 수 있다는 것!!!

     

    ※ Subcriber가 되려면 @ObservedObject라는 프로퍼티 래퍼로 감싸줄 수 있습니다. (물론 다른 방법도 있음)

    관련해서는 다음 시간에 좀 더 하도록 할게요...!

     

    그런데 ObservableObject를 사용한다고 해서 이벤트 방출이 자동으로 되는 것은 아닙니다. (미쵸ㅠ)

    어떻게 이벤트를 방출할 수 있느냐?

    두 가지 방법이 있는데요.

     

    첫번째는 objectWillChange.send()를 사용하는 것입니다.

    objectWillChange.send()는 이벤트 변경을 Subscriber에게 알려주는 함수인데요.

     

    아래 코드를 보면 increaseCounter()를 할 때마다 objectWillChange.send()를 호출하고 있습니다.

    이 말은 즉슨 increaseCounter()가 수행되면 이벤트 변경 시그널을 Subscriber에게 주는 것입니다!

    이를 구독하고 있는 PlayerView의 counter는 (@ObservedObject로 감싸진 애)

    increaseCounter()가 호출될 때마다 이벤트 변경을 실시간으로 받게 되고

    결과적으로 Text("\(counter.count)")의 값이 자동으로 변경됩니다. (View가 자동으로 업데이트됨!)

    import SwiftUI
    
    class Counter: ObservableObject {
        var count: Int = 0
    
        func increaseCounter() {
            count = count + 1
            objectWillChange.send() // Emit event!
        }
    }
    
    struct PlayerView: View {
        
        @ObservedObject var counter = Counter() // Subscriber
    
        var body: some View {
            VStack {
                Button("Increase Counter") {
                    counter.increaseCounter()
                }
                Text("\(counter.count)")
            }
        }
    }

     

    만약 objectWillChange.send()를 사용하지 않는다면

    View 입장에서는 counter의 이벤트 변경 시그널을 받지 못했기 때문에

    View가 업데이트 되지 않습니다.

    Publisher가 방출한 값이 없으니 Subscriber 입장에서는 어떠한 값들도 받아볼 수 없는 것이죠!

     

    한 가지 주의해야할 점은 @ObservedObject 명시인데요.

    만약 PlayerView의 counter를 @ObservedObject로 감싸지 않는다면

    counter는 Subscriber가 아니게 되기 때문에

    Publisher에서 objectWillChange.send()가 실행이 된다고 하더라도

    counter는 이벤트 변경 시그널을 받지 못해서

    Text("\(counter.count)")에서는 값 업데이트가 일어나지 않습니다.

     

    Subscriber 이벤트 변경을 받아볼 수 있다!!!

    기억해주세요!!!

     

    @Published

    @propertyWrapper
    struct Published<Value>

     

    두번째로 ObservableObject의 이벤트 방출을 하는 방법은 @Published를 사용하는 것입니다.

    정리를 해보자면,

    • 이벤트 방출 대상임 ← 누군가가 이 값을 구독한다면 변경을 계속 트래킹할 수 있음!
    • 클래스 내부 프로퍼티에서만 사용이 가능함 (반드시 클래스여야 함!!!)

    바로 예제 코드 들어갑니다!

    아까와는 다르게 increaseCounter()에서 objectWillChange.send()를 호출하지 않습니다.

    하지만 count라는 녀석을 @Published로 감싸줌으로써

    이 녀석의 변경이 실시간으로 Subscriber에게 전달이 되는겁니다! (간!단!)

    그.래.서.

    PlayerView에서 Increase Counter 버튼을 누르게 되면

    View는 자동으로 변경된 counter 값을 렌더링해줍니다.

    import SwiftUI
    
    class Counter: ObservableObject {
        @Published var count: Int = 0
    
        func increaseCounter() {
            count = count + 1
        }
    }
    
    struct PlayerView: View {
        @ObservedObject var counter = Counter()
    
        var body: some View {
            VStack {
                Button("Increase Counter") {
                    counter.increaseCounter()
                }
                Text("\(counter.count)")
            }
        }
    }

     

    마찬가지로 Counter 모델에서 @Published를 없앤다면 이벤트 방출 대상이 없어지기 때문에

    Increase Counter 버튼을 눌러도 Subscriber는 이벤트 방출 시그널을 받지 못해서

    증가된 count는 View에 보이지 않습니다.

     

    Subscriber들만 이벤트를 실시간으로 받아볼 수 있고

    Publisher가 방출한 이벤트 Subscriber들이 읽어볼 수 있는 것...

    다시 한 번 꼭 기억해주세요!

     

    @ObservedObject와 관련된 건 다음 시간에 다루도록 할게요!

     

    Property Wrapper에 대해서 함께 공부하고 싶다면
    #PropertyWrapper 태그로 들어가기!
Designed by Tistory.