[SwiftUI / MVI 패턴] MVI 패턴의 변화 ( iOS 17+ ) - 2탄
2025.06.07 - [SwiftUI] - [SwiftUI / MVI 패턴] MVI 패턴의 변화 ( iOS 17+ )
[SwiftUI / MVI 패턴] MVI 패턴의 변화 ( iOS 17+ )
MVI 패턴에 익숙하지 않지만 그래도 공부의 목적으로 MVI 패턴에 대해서 공부했던걸 끄적여봄.우선 MVI 패턴에 대해서는 단방향 아키텍쳐가 핵심이다. 자세한건 다른 블로그 글들 많으니 참고 바
slowsure.tistory.com
이 글에 이어서 적겠음. 위의 이전 글을 읽고 오는걸 추천
그러니깐 @Observable과 ObservableObject는 어떻게 차이가 나길래? MVI 에서 Containter를 걷어낼 수 있었을까?
@Observable과 ObservableObject 모두 코드를 볼 수 있음 바로 알겠지만. 볼 수 없으니 여기서부턴 유추임.
ObservableObject
우리는 함수명을 통해 유추할 수 있다.
ObservableObject를 채택하면 @Publisher(propertyWrapper)를 통해 선언된 객체가 변동시에 무언갈 진행한다는 것을 유추 할 수 있음.
propertyWrapper 속 wrapperedValue가 set 될 때 objectWillChange 를 호출 하지 않을까 싶음.
그럼 objectWillChange은 Publisher인데 어떤 값을 방출하는 것일까?
정답은 아무것도 방출하지 않음.
그저 Object가 변했다 라는 것을 알려주기 위한 Publisher임.
그 말인 즉, ObservableObject 내 어떤 Publihser가 objectWillChange를 호출해서 Publhser를 작동시키면
objectWillChange로 무언가 작동하는 객체는 객체의 어떤 Publisher가 값이 변동되었는지 모름.
그럼 View에서 어떻게 해야함? 모르면 멍청비용 지불해야지.
해당 객체를 @StateObject로 들고 있는 View는 body 전체를 다시 계산할 수 밖에.
protocol TestClassStateDataProtocol: ObservableObject {
var text: String { get }
}
final class TestClass: ObservableObject, TestClassStateDataProtocol {
@Published var text: String = "Hello, World!"
}
struct ContentView: View {
@StateObject private var testClass: TestClassStateDataProtocol = TestClass()
var body: some View {
VStack {
Text(testClass.text)
.padding()
Button("Change Text") {
testClass.text = "Text Changed!"
}
}
}
}
그럼 여기서 이렇게 하면 안되냐고 생각이 가능함.
물론 안됨. @StateObject의 객체는 생명주기를 SwiftUI에서 관리한다.
근데 protocol로 선언되어 있으면 객체 생명주기를 관리하지 못함. 그래서 컴파일 자체가 안됨.
그래서 Container를 통한 MVI 패턴이 필요했던 거임.
@Observable
iOS 17 이상부터 도입된 @Observable Migration을 보면
Migrating from the Observable Object protocol to the Observable macro | Apple Developer Documentation
Update your existing app to leverage the benefits of Observation in Swift.
developer.apple.com
Updating views based on changes to the observable properties that a view’s body reads instead of any property changes that occur to an observable object, which can help improve your app’s performance.
관찰 가능한 객체에 발생하는 속성 변경 대신 뷰의 본문이 읽는 관찰 가능한 속성에 대한 변경 사항을 기반으로 뷰를 업데이트하면 앱의 성능을 향상시킬 수 있습니다.
라는 문구가 있음.
객체에서 발생하는 속성 변경 대신, 관찰 가능한 속성에 대한 변경으로 View를 업데이트 하겠다는 이야기임.
또 하나 Remove the ObservedObject property wrapper를 살펴보면
Next, remove the ObservedObject property wrapper from the book variable in the BookView. This property wrapper isn’t needed when adopting Observation. That’s because SwiftUI automatically tracks any observable properties that a view’s body reads directly. For example, SwiftUI updates BookView when book.title changes.
다음으로 BookView의 book 변수에서 ObservedObject property wrapper를 제거합니다. 이 속성 래퍼는 Observation을 채택할 때 필요하지 않습니다. SwiftUI는 뷰의 본문이 직접 읽는 관찰 가능한 속성을 자동으로 추적하기 때문입니다. 예를 들어, Book.title이 변경되면 SwiftUI는 BookView를 업데이트합니다.
라는 문구가 나옴.
즉 ObservedObject가 없어도, SwiftUI View에 사용되면 직접 읽기 가능한 속성을 자동으로 추적한다. 라고 적혀 있다.
@Observable로 매크로 객체 내에서는 SwiftUI에게 자동으로 객체를 추적할 수 있도록 어느 코드가 숨어 있다는 것이다.
또한 읽기만 가능한 라는 문구에 눈여겨 보면 Protocol에서 { get } 으로만 열어주면 된다는 의미기도 하다.
그렇기에 @Observable에서는 객체의 변화가 아닌, 프로퍼티의 변화를 SwiftUI에서 추적하기에 객체 자체는 어찌됐든 상관 없다.
그렇기에 @Stateobject처럼 SwiftUI의 객체 생명주기를 SwiftUI프레임워크에 안 맞겨도 되기에 Protocol로 선언해도 된다.
결론
여기까지가 나의 추론
ObservableObject + @StateObject는
- 객체 자체의 변화를 감지. (어떤 @Publish 프로퍼티가 변화하는 것인지 알 수 없음)
- 객체 자체를 감지해야하기에 @StateObject property wrapper를 통해서 생명주기를 관리 및 감지
@Observable
- View에서 사용하는 Property 변화를 감지.
- 사용하는 Property의 변화를 감지하는건 SwiftUI 프레임 워크.
- 그렇기에 객체 자체의 형태는 상관없음. -> Protocol 타입도 가능
위 @Observable과 ObservableObject 차이 때문에 MVI 패턴에서 Container가 없이도 작동 가능한 이유가 아닐까 생각함.