이 글을 읽기 전, State와 StateObject에 대한 이해가 필요합니다. State와 StateObject가 무엇인지에 대한 내용은 아래 글을 참고해 주세요!ㅎㅎ
SwiftUI를 사용하다보면 언제 State와 StateObject를 언제 사용해야 하는지 고민 되는 순간이 있습니다. 결론부터 얘기하면 ViewModel은 절대로 State로 선언하면 안됩니다.
반드시 @StateObject
와 @ObservedObject
를 선언해야 합니다.
왜 그럴까요??
@State
는 값 타입 전용 상태 프로퍼티에 쓰라고 만든 속성이기 때문입니다. ViewModel
처럼 참조 타입(class)을 관리하도록 만들어진 속성이 아닌 것이죠.
@State
는 내부적으로 뷰가 상태를 직접 소유하고 재생성합니다. 즉, ViewModel
인스턴스가 뷰의 라이프사이클과 함께 자주 초기화 될 수 있습니다. 그렇게 되면 다음과 같은 문제가 발생합니다.
ViewModel
의 상태가 초기화 된다.@Published
값 변경이 UI에 반영되지 않는다.또 다른 문제로는 MVVM
아키텍처를 깨뜨리는 것입니다.
MVVM
에서 ViewModel
은 View
에 종속되지 않고, 독립적인 객체로 상태를 관리해야 합니다. 그런데 @State
로 선언하면 View
가 ViewModel
을 직접 “가지고” 있기 때문에 View
가 사라질 때마다 ViewModel
도 메모리에서 자동으로 해제됩니다. 이는 위에서 언급한데로 ViewModel
이 상태를 유지하지 못하게 되고, View
에 종속적이게 됩니다.
즉, View
가 사라질 때 ViewModel
도 함께 사라지기 때문에 상태 저장 책임이 View
로 넘어가게 되어 아키텍처가 흐트러지는 것입니다.
그럼 어떻게 선언해야 할까요? 표로 정리하면 다음과 같습니다.
선언 방식 | 언제 쓰나 | 특징 |
---|---|---|
@StateObject | View가 ViewModel의 소유권을 갖는 경우 | View가 처음 만들어질 때 단 한번 생성되고 이후 유지됨 |
@ObservedObject | ViewModel을 외부에서 주입받는 경우 | 부모 뷰나 상위 객체에서 전달받음. ViewModel을 소유하진 않음. |
@EnvironmentObject | 앱 전체 또는 특정 트리에서 공유하는 경우 | 전역 상태 공유용 |
아래는 가장 일반적인 방식에 대한 예제입니다.
struct SignupView: View {
@StateObject private var viewModel = SignupViewModel()
var body: some View {
// 바인딩 사용
}
}
그럼 여기서 @StateObject
도 View
가 ViewModel
에 대한 소유권을 갖는데, 그럼 이 경우도 ViewModel
이 View
에 종속 되는 것이 아닌가 하는 의문이 생길 수 있습니다.
여기서 가장 결정적인 차이점이 있는데요.
바로, @StateObject
는 뷰가 처음 생성될 때 ViewModel
을 단 한번만 초기화 한다는 점입니다. 반면에 @State
는 값 복사 기반이라 View
가 리렌더링될 때마다 ViewModel
이 계속 새로 생성되기 때문인 것입니다.
사실 위에서 계속 언급해왔던 사실이죠.
정리하면, @State
와 @StateObject
는 둘 다 View
가 ViewModel
을 소유하는 것은 맞지만 @State
는 렌더링 될 때마다 계속 생성이 되고, @StateObject
는 뷰 생성 시 한번만 초기화 되는 것입니다. 즉, MVVM
에서는 ViewModel
이 View
에 소유되는 것 자체가 문제가 아니라 상태를 간접적으로 업데이트를 “지속적”으로 할 수 있느냐가 더 중요한 관점인 것입니다.
또한, @StateObject
로 선언함으로써 ViewModel
이 View
의 생명주기에 따라 존재할 뿐, View
가 ViewModel
의 로직을 직접 다루거나, 상태를 바꾸지는 않습니다. 이렇게 함으로써 생성 책임은 View
가 가져갈 수 있지만, “로직 책임”은 ViewModel
이 하게 되어 역할을 명확히 분리할 수 있게 되는 것이죠.
그렇다면 MVVM을 깨는 구조는 어떤 것이 있을까요?
View에서 상태를 직접 가짐
// 뷰에서 프로퍼티 값을 수정 및 저장
@State var email = ""
ViewModel 없이 API 호출이나 유효성 검사 직접 처리
// 뷰에서 직접 유효성 검사 처리
if email.contains("@") { ... }
View가 ViewModel의 내부 상태를 바꾸는 등 로직 침범
SwiftUI에서 상태를 선언할 때 가장 중요한 것은 ‘누가 상태를 소유하고, 누가 그것을 관리하는가’입니다.
@StateObject
는 View
가 ViewModel
을 “소유”하되, 로직에는 “침범”하지 않기 때문에 MVVM
원칙을 지키는 것입니다. 반면, @State
는 값 타입에 적합한 속성으로, ViewModel
처럼 참조 타입인 객체에는 절대 사용해서는 안 됩니다.
따라서 ViewModel
은 항상 @StateObject
또는 @ObservedObject
로 관리하고, @State
는 값 타입의 간단한 View
내부 상태에만 사용하는 것이 좋습니다.
출처
https://developer.apple.com/documentation/swiftui/state
https://developer.apple.com/documentation/swiftui/stateobject
https://www.dhiwise.com/post/swiftui-state-vs-stateobject-how-they-differ-in-usage
https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject