반응형

SwiftUI에서 많이 하는 실수 - @ObservedObject와 @StateObject를 잘못 사용해 ViewModel이 계속 초기화되는 실수

 

ObservableObject를 사용할 때 이 객체의 라이프사이클을 누가 책임지느냐에 따라

@ObservedObject와 @StateObject 선택이 달라져야 합니다.

  • @StateObject : 이 View가 생성과 소멸까지 책임지는 소유자
  • @ObservedObject: 다른 곳에서 생성·소유한 객체를 그저 관찰만

이 둘을 반대로 사용하면 View가 다시 그려질 때마다 ViewModel이 계속 새로 만들어져,

네트워크 요청이 반복되거나 상태가 초기화되는 문제가 생깁니다.


1. 문제 원인

  • “ObservableObject니까 그냥 @ObservedObject”라는 식의 습관적 사용
  • 화면 루트/탭 루트 View에서도 @ObservedObject를 사용
  • 부모/자식 관계 고려 없이 아무 View에서나 @StateObject 선언

2. 나타나는 증상

  • 탭을 바꿨다 돌아오면, 리스트가 매번 새로 로드됨
  • 입력하던 폼 값이 화면 전환 후 다시 돌아오면 모두 초기화
  • init() 에 print를 넣어보면 생각보다 자주 호출되는 것을 발견

3. 잘못된 코드 예시

final class CardsViewModel: ObservableObject {
    @Published var cards: [String] = []

    init() {
        print("🔥 CardsViewModel init")
        // 네트워크/디스크 로딩
    }
}

struct CardsView: View {
    // ❌ 진입점 View인데 @ObservedObject 사용
    @ObservedObject var viewModel = CardsViewModel()

    var body: some View {
        List(viewModel.cards, id: \.self) { card in
            Text(card)
        }
    }
}
  • 탭 전환/화면 재표시 때마다 init()이 여러 번 찍히게 됩니다.

4. 올바른 코드 예시

4-1. 루트 View에서의 사용

struct CardsView: View {
    // ✅ 이 View가 라이프사이클 소유 → @StateObject
    @StateObject private var viewModel = CardsViewModel()

    var body: some View {
        List(viewModel.cards, id: \.self) { card in
            Text(card)
        }
    }
}

4-2. 부모에서 생성해 자식으로 넘길 때

struct ParentView: View {
    @StateObject private var viewModel = CardsViewModel() // ✅ 소유자

    var body: some View {
        ChildView(viewModel: viewModel) // ✅ 주입
    }
}

struct ChildView: View {
    @ObservedObject var viewModel: CardsViewModel // ✅ 관찰만

    var body: some View {
        List(viewModel.cards, id: \.self) { card in
            Text(card)
        }
    }
}

5. 정리 및 팁

  • 원칙: “처음 만들어지는 곳 = @StateObject, 그걸 넘겨받는 곳 = @ObservedObject”
  • init에 print를 넣고 언제 호출되는지 확인해보면 구조 설계의 문제가 금방 드러납니다.
init() {
    print("init:", Self.self)
}

이 패턴만 잘 지켜도, “왜 ViewModel이 자꾸 초기화되지?” 문제를 대부분 막을 수 있습니다.

반응형
Posted by 까칠코더
,