SwiftUI @State와 @StateObject를 언제 어디에 선언해야 하는가

이재원·7일 전
0

SwiftUI

목록 보기
10/11
post-thumbnail

이 글을 읽기 전, State와 StateObject에 대한 이해가 필요합니다. State와 StateObject가 무엇인지에 대한 내용은 아래 글을 참고해 주세요!ㅎㅎ

State와 StateObject를 어디에서 사용하지??

SwiftUI를 사용하다보면 언제 State와 StateObject를 언제 사용해야 하는지 고민 되는 순간이 있습니다. 결론부터 얘기하면 ViewModel은 절대로 State로 선언하면 안됩니다. 반드시 @StateObject@ObservedObject를 선언해야 합니다.

왜 그럴까요??

@State 는 값 타입 전용이다.

@State는 값 타입 전용 상태 프로퍼티에 쓰라고 만든 속성이기 때문입니다. ViewModel 처럼 참조 타입(class)을 관리하도록 만들어진 속성이 아닌 것이죠.

@State는 상태를 값 복사로 관리한다.

@State는 내부적으로 뷰가 상태를 직접 소유하고 재생성합니다. 즉, ViewModel 인스턴스가 뷰의 라이프사이클과 함께 자주 초기화 될 수 있습니다. 그렇게 되면 다음과 같은 문제가 발생합니다.

  • ViewModel의 상태가 초기화 된다.
  • API 호출 재실행된다.
  • @Published 값 변경이 UI에 반영되지 않는다.

MVVM 아키텍처를 깨드린다

또 다른 문제로는 MVVM 아키텍처를 깨뜨리는 것입니다.

MVVM에서 ViewModelView에 종속되지 않고, 독립적인 객체로 상태를 관리해야 합니다. 그런데 @State로 선언하면 ViewViewModel을 직접 “가지고” 있기 때문에 View가 사라질 때마다 ViewModel도 메모리에서 자동으로 해제됩니다. 이는 위에서 언급한데로 ViewModel이 상태를 유지하지 못하게 되고, View에 종속적이게 됩니다.

즉, View가 사라질 때 ViewModel도 함께 사라지기 때문에 상태 저장 책임이 View로 넘어가게 되어 아키텍처가 흐트러지는 것입니다.

그럼 어떻게 선언해야 할까요? 표로 정리하면 다음과 같습니다.

선언 방식언제 쓰나특징
@StateObjectView가 ViewModel의 소유권을 갖는 경우View가 처음 만들어질 때 단 한번 생성되고 이후 유지됨
@ObservedObjectViewModel을 외부에서 주입받는 경우부모 뷰나 상위 객체에서 전달받음. ViewModel을 소유하진 않음.
@EnvironmentObject앱 전체 또는 특정 트리에서 공유하는 경우전역 상태 공유용

아래는 가장 일반적인 방식에 대한 예제입니다.

struct SignupView: View {
    @StateObject private var viewModel = SignupViewModel()
    
    var body: some View {
        // 바인딩 사용
    }
}

View가 소유하지만, 로직은 ViewModel이 담당

그럼 여기서 @StateObjectViewViewModel에 대한 소유권을 갖는데, 그럼 이 경우도 ViewModelView에 종속 되는 것이 아닌가 하는 의문이 생길 수 있습니다.

여기서 가장 결정적인 차이점이 있는데요.

바로, @StateObject는 뷰가 처음 생성될 때 ViewModel을 단 한번만 초기화 한다는 점입니다. 반면에 @State는 값 복사 기반이라 View가 리렌더링될 때마다 ViewModel이 계속 새로 생성되기 때문인 것입니다.

사실 위에서 계속 언급해왔던 사실이죠.

정리하면, @State@StateObject는 둘 다 ViewViewModel을 소유하는 것은 맞지만 @State는 렌더링 될 때마다 계속 생성이 되고, @StateObject는 뷰 생성 시 한번만 초기화 되는 것입니다. 즉, MVVM에서는 ViewModelView에 소유되는 것 자체가 문제가 아니라 상태를 간접적으로 업데이트를 “지속적”으로 할 수 있느냐가 더 중요한 관점인 것입니다.

또한, @StateObject로 선언함으로써 ViewModelView의 생명주기에 따라 존재할 뿐, ViewViewModel의 로직을 직접 다루거나, 상태를 바꾸지는 않습니다. 이렇게 함으로써 생성 책임은 View가 가져갈 수 있지만, “로직 책임”은 ViewModel이 하게 되어 역할을 명확히 분리할 수 있게 되는 것이죠.

MVVM을 깨는 예시

그렇다면 MVVM을 깨는 구조는 어떤 것이 있을까요?

  1. View에서 상태를 직접 가짐

    // 뷰에서 프로퍼티 값을 수정 및 저장
    @State var email = ""
  2. ViewModel 없이 API 호출이나 유효성 검사 직접 처리

    // 뷰에서 직접 유효성 검사 처리
    if email.contains("@") { ... }
  3. View가 ViewModel의 내부 상태를 바꾸는 등 로직 침범

SwiftUI에서 상태를 선언할 때 가장 중요한 것은 ‘누가 상태를 소유하고, 누가 그것을 관리하는가’입니다.

@StateObjectViewViewModel을 “소유”하되, 로직에는 “침범”하지 않기 때문에 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

profile
20학번 새내기^^(였음..)

0개의 댓글

관련 채용 정보