[SwiftUI] @StateObject와 @ObservedObject는 뭐가 다를까?
지난번에는 EnvironmentObject에 대해서 알아봤지만 사실 더 먼저 알아야하는 것이 이 두 Wrapper인 @StateObject와 @ObservedObject입니다.
@StateObject와 @ObservedObject: 차이점 설명
@StateObject
와 @ObservedObject
속성 래퍼는 Observed 객체의 변경에 대한 응답으로 SwiftUI 뷰를 업데이트하도록 지시합니다. 두 속성 래퍼 계층으로 유사해 보이지만 SwiftUI에서 앱을 구축할 때 알아야 할 중요한 차이점이 있습니다.
처음에는 왜 항상 @ObservedObject
를 잘 사용을 안했는지 이유를 몰랐습니다. (그냥 써야한다고해서 그냥 썼던 기억밖에.. ㅎㅎ)
그러다 두개의 차이에 대한 의문과, 공식문서를 연구하면서 SwiftUI에서 @StateObject
의 목적을 알게 되면서 일부 경우에는 @StateObject
가 얼마나 필요한지 깨닫게 되었습니다.
@ObservedObject란?
@StateObject
와 @ObservedObject
의 차이점을 알아보기 전에 @ObservedObject
가 무엇인지 이해하는 것이 좋습니다. 두 속성 래퍼 모두 객체가 ObservableObject
프로토콜을 준수하도록 요구합니다. 이 프로토콜은 객체가 변경되기 전에 발행자(@Published 변수)가 있음을 나타내며, SwiftUI에게 뷰 다시 그리기를 트리거하도록 알려줍니다.
예를 들면 다음과 같은 카운터 뷰가 있습니다:
final class CounterViewModel: ObservableObject {
@Published var count = 0
func incrementCounter() {
count += 1
}
}
struct CounterView: View {
@ObservedObject var viewModel = CounterViewModel()
var body: some View {
VStack {
Text("Count is: \(viewModel.count)")
Button("Increment Counter") {
viewModel.incrementCounter()
}
}
}
}
@StateObject란?
@StateObject 속성 래퍼는 @ObservedObject와 유사하게 작동합니다.
struct CounterView: View {
@StateObject var viewModel = CounterViewModel()
var body: some View {
VStack {
Text("Count is: \(viewModel.count)")
Button("Increment Counter") {
viewModel.incrementCounter()
}
}
}
}
이러한 차이점을 이해하는 것은 다른 뷰가 현재 뷰를 포함하고 있는 경우 중요합니다.
그래서 차이가 뭔데요??
이 작동 방식을 보여주기 위해 이전에 사용했던 카운터 뷰를 다른 뷰 안에 포함시켜 보겠습니다:
struct RandomNumberView: View {
@State var randomNumber = 0
var body: some View {
VStack {
Text("랜덤 숫자는: \(randomNumber)")
Button("숫자 무작위로") {
randomNumber = (0..<1000).randomElement()!
}
}.padding(.bottom)
//카운터뷰 안에 @ObservedObject가 들어가 있습니다.
CounterView()
}
}
랜덤 숫자 뷰를 사용하면 '숫자 무작위로' 버튼을 눌러 랜덤 숫자를 생성할 수 있습니다. @State 속성 래퍼에 의해 표시된 randomNumber 속성은 뷰를 다시 그리게 되어 자식뷰인 CounterView도 다시 생성됩니다.
새로운 랜덤 숫자가 생성될 때 CounterView 내에서 @ObservedObject를 사용하면 @Published의 변수인 카운터가 초기화되는 것을 볼 수 있습니다.
이 문제를 해결하는 방법은 CounterView의 뷰 모델 속성을 @ObservedObject 대신 @StateObject로 변경하는 것만큼 간단합니다. 위에서 설명했듯이, state 객체는 뷰 모델이 뷰 다시 그리기 사이에 유지되도록 보장하고 우리의 카운터 값이 동일하게 유지되도록 합니다.
언제 @StateObject를 사용해야 하나요?
뷰가 언제든지 생성되거나 다시 생성될 수 있으므로 뷰 내에서 @ObservedObject를 생성하는 것은 안전하지 않습니다. @ObservedObject를 의존성으로 주입하지 않는 한 뷰를 다시 그릴 때 일관된 결과를 보장하기 위해 @StateObject 래퍼를 사용하려고 합니다.
모든 뷰에 @StateObject를 사용해야 하나요?
동일한 @StateObject 인스턴스를 사용하는 모든 뷰에 대해 이 속성 래퍼를 사용할 필요는 없습니다. 객체의 생명주기를 두 곳에서 유지 및 관리하도록 요청하기 때문에 이를 피해야 합니다.
결론
@StateObject와 @ObservedObject는 유사한 특징을 가지고 있지만 SwiftUI가 생명주기를 관리하는 방식에서 차이가 있습니다. 현재 뷰가 관찰된 객체를 생성할 때 일관된 결과를 보장하기 위해 상태 객체 속성 래퍼를 사용하십시오. 의존성으로 관찰된 객체를 주입할 때 @ObservedObject를 사용할 수 있습니다.