@StateObject: 개요, ObservableObject 생성 및 관리

저자: IT Sectr 게시일: 2026-06-26 읽는 시간: 9 분

@StateObject는 SwiftUI의 property wrapper로, View의 전체 수명 주기 동안 ObservableObject 인스턴스를 생성하고 소유합니다. View가 처음 화면에 나타날 때 @StateObject는 객체를 초기화하고 View가 메모리에서 제거될 때까지 저장합니다. 이는 인터페이스 재구축 시(예: 테마 변경 또는 부모 View 업데이트) 데이터가 재설정되지 않도록 보장합니다. Apple Developer Documentation(2025)에 따르면, @StateObject는 SwiftUI 계층 구조에서 ObservableObject의 기본 정보 소스(source of truth)로 사용되어야 하며, 자식 View는 @ObservedObject 또는 @EnvironmentObject를 통해 이미 생성된 객체를 받습니다.

주요 포인트

  • @StateObject — View 내에서 ObservableObject를 생성하고 소유하기 위한 property wrapper입니다.
  • 단일 생성 — 객체는 View의 수명 동안 한 번만 초기화되며 재구축 시 다시 생성되지 않습니다.
  • 정보 소스 — @ObservedObject와 달리 @StateObject는 계층 구조에서 정보 소스(source of truth)입니다.
  • 수명 주기 — 객체는 View가 메모리에 존재하는 한 살아 있으며 View와 함께 소멸됩니다.
  • 초기화 — @StateObject는 생성 시 초기 값이 필요하며, 일반적으로 매개변수가 있는 init을 통해 수행됩니다.

SwiftUI에서 @StateObject란

@StateObject는 iOS 14에서 도입된 property wrapper로, View가 ObservableObject 프로토콜을 준수하는 클래스의 인스턴스를 생성하고 소유할 수 있게 합니다. 값 유형(struct)으로 작동하는 @State와 달리, @StateObject는 참조 유형 — 속성 변경 사항을 SwiftUI에 알릴 수 있는 클래스 — 을 위해 설계되었습니다.

View가 @StateObject var viewModel: MyViewModel을 사용하면 SwiftUI는 View가 처음 표시될 때 MyViewModel의 인스턴스를 자동으로 생성하고 프레임워크의 특별한 저장소에 보관합니다. View가 업데이트될 때마다(예: 부모 상태가 변경될 때) SwiftUI는 객체를 다시 생성하지 않고 View가 계층 구조에서 제거될 때까지 기존 인스턴스를 사용합니다.

Apple WWDC Session 10137(2024)에 따르면, @StateObject는 iOS 13에서 View가 재구축될 때 존재했던 데이터 손실 문제를 해결합니다. 이전에는 개발자가 부모 View에서 ObservableObject를 생성하고 초기화자를 통해 전달해야 했으며, 이로 인해 코드 중복과 실수로 객체가 다시 생성될 위험이 있었습니다.

swift
import SwiftUI

class CounterViewModel: ObservableObject {
    @Published var count: Int = 0
    
    func increment() {
        count += 1
    }
}

struct CounterView: View {
    @StateObject var viewModel = CounterViewModel()
    
    var body: some View {
        VStack {
            Text("Count: \(viewModel.count)")
            Button("Increment", action: viewModel.increment)
        }
    }
}

@StateObject의 작동 방식

@StateObject 메커니즘은 SwiftUI와 Combine 프레임워크의 통합에 기반합니다. ObservableObject가 속성을 @Published 속성으로 표시하면 SwiftUI는 ObservableObject 프로토콜에 내장된 퍼블리셔를 통해 변경 사항을 자동으로 구독합니다. 게시된 속성이 변경되면 객체는 objectWillChange 퍼블리셔를 통해 신호를 보내고, 이 객체를 관찰하는 모든 View의 다시 그리기를 트리거합니다.

SwiftUI는 ObservableObject 인스턴스를 특정 View 인스턴스에 연결된 특별한 저장소에 보관합니다. 이 저장소는 첫 번째 렌더링 시 한 번 생성되며 View가 소멸될 때까지 존재합니다. 이것이 @StateObject가 참조 안정성을 보장하는 이유입니다 — SwiftUI는 View의 초기화자에 의존하지 않고 메모리를 자동으로 관리합니다.

objc.io — Thinking in SwiftUI(2025)에 따르면, @StateObject의 내부 구현은 @State와 유사한 메커니즘을 사용하지만 참조 유형용입니다. SwiftUI는 객체 주위에 박싱 래퍼를 만들고 빈번한 View 계층 구조 재구축에 최적화된 자체 할당자를 통해 수명 주기를 관리합니다.

@StateObject 수명 주기

  • 생성 — View가 처음 화면에 나타날 때 SwiftUI는 객체의 초기화자를 호출하고 참조를 저장합니다.
  • 재구축 — 부모 View가 업데이트될 때 객체는 다시 생성되지 않으며 기존 인스턴스가 사용됩니다.
  • 소멸 — View가 화면을 떠나 계층 구조에서 제거될 때 SwiftUI는 객체의 deinit을 호출합니다.

@StateObject vs @ObservedObject: 주요 차이점

@StateObject@ObservedObject의 주요 차이점은 누가 객체를 소유하는지에 있습니다. @StateObject는 객체를 생성하고 저장합니다 — 그것이 소유자입니다. @ObservedObject는 다른 곳에서 생성되어 초기화자나 속성을 통해 전달된 객체를 관찰만 합니다.

특성@StateObject@ObservedObject
소유권객체를 생성하고 소유함관찰만 함
초기화View 내에서 init/기본값을 통해외부에서 매개변수를 통해 전달됨
수명 주기View의 수명 주기에 연결됨View에 의해 제어되지 않음
재생성업데이트 시 다시 생성되지 않음외부에서 교체될 수 있음
iOS 버전iOS 14+iOS 13+

규칙은 간단합니다. View가 ObservableObject를 생성하는 경우 @StateObject를 사용합니다. View가 부모로부터 이미 생성된 객체를 받기만 하는 경우 @ObservedObject를 사용합니다. 이 규칙을 위반하면 데이터 손실(소유권에 @ObservedObject 사용) 또는 과도한 객체 생성(관찰에 @StateObject 사용)이 발생합니다.

@StateObject 사용 시기

@StateObject는 특정 데이터 집합의 정보 소스 역할을 하는 View에서 사용해야 합니다. 일반적인 시나리오에는 자체 view model이 있는 화면, 네비게이션 스택의 루트 화면 및 자체 상태를 관리하는 모달 프레젠테이션이 포함됩니다.

  • View model이 있는 화면 — 자체 데이터와 로직을 관리하는 각 화면은 @StateObject를 통해 view model을 생성해야 합니다.
  • 루트 View — NavigationStack 또는 TabView 계층 구조에서 루트 요소가 데이터를 생성하고 자식 요소는 @ObservedObject를 통해 데이터를 받습니다.
  • 모달 창 — .sheet 및 .fullScreenCover는 종종 폼이나 프로세스를 관리하기 위해 자체 @StateObject가 필요합니다.
  • 편집 가능한 목록 — 편집 폼이 포함된 각 목록 행은 자체 @StateObject를 가져야 합니다.
swift
struct ProfileView: View {
    @StateObject var viewModel = ProfileViewModel()
    
    var body: some View {
        NavigationStack {
            Form {
                TextField("Name", text: $viewModel.name)
                TextField("Email", text: $viewModel.email)
                Button("Save") {
                    viewModel.saveProfile()
                }
            }
            .navigationTitle("Profile")
        }
    }
}

매개변수로 @StateObject 초기화

@StateObject를 매개변수로 초기화하려면 특별한 구문이 필요합니다. SwiftUI가 객체 생성을 자체적으로 관리하기 때문입니다. 초기화자에 단순히 매개변수를 전달할 수 없습니다 — 이스케이핑 클로저나 별도의 팩토리 메서드를 사용해야 합니다.

Swift by Sundell(2024)에 따르면, 가장 깔끔한 방법은 SwiftUI가 객체를 처음 생성할 때 호출할 팩토리 메서드 또는 클로저를 사용하는 것입니다. 대안적인 접근 방식은 부모 View에서 ObservableObject를 초기화하고 표준 초기화자를 사용하여 @StateObject를 통해 전달하는 것입니다.

swift
class UserViewModel: ObservableObject {
    @Published var user: User
    
    init(user: User) {
        self.user = user
    }
}

struct UserDetailView: View {
    @StateObject var viewModel: UserViewModel
    
    init(user: User) {
        _viewModel = StateObject(wrappedValue: UserViewModel(user: user))
    }
    
    var body: some View {
        Text(viewModel.user.name)
    }
}

@StateObject가 있는 View 초기화자는 property wrapper 자체에 접근하기 위해 속성 이름 앞에 밑줄(_viewModel)을 사용해야 합니다. 이는 초기화자에서 property wrapper로 작업하기 위한 표준 Swift 패턴입니다.

@StateObject 관련 일반적인 실수

가장 흔한 실수는 객체를 소유해야 하는 View에서 @StateObject 대신 @ObservedObject를 사용하는 것입니다. 이 경우 부모가 재구축될 때마다 객체가 다시 생성되어 축적된 모든 데이터가 손실됩니다. 이 실수는 NavigationStack 또는 TabView가 있는 복잡한 계층 구조에서 특히 교묘합니다.

  • 네비게이션 중 데이터 손실 — 자식 화면이 자체 view model에 @ObservedObject를 사용하는 경우 뒤로 이동했다가 다시 열면 데이터가 재설정됩니다.
  • 메모리 누수 — 제거되지 않는 부모 View에서 @StateObject를 생성하면 각 자식 화면도 제어 없이 @StateObject를 생성하는 경우 객체가 축적될 수 있습니다.
  • 객체 중복 — 단일 ObservableObject를 여러 View의 여러 @StateObject에 전달하면 서로 동기화되지 않는 여러 독립 인스턴스가 생성됩니다.

이러한 문제를 피하려면 간단한 규칙을 따르세요. 정보 소스당 하나의 @StateObject. 데이터를 여러 화면에서 공유해야 하는 경우 루트 View에서 @StateObject를 한 번 생성하고 @ObservedObject 또는 @EnvironmentObject를 통해 자식 요소에 전달합니다.

swift
// ❌ Wrong: @ObservedObject for owning an object
struct BadView: View {
    @ObservedObject var vm = ViewModel() // will be recreated on each update!
}

// ✅ Correct: @StateObject for owning
struct GoodView: View {
    @StateObject var vm = ViewModel() // created once for View lifetime
}

자주 묻는 질문

@StateObject와 @State의 차이점은 무엇인가요?

@State는 값 유형(struct, 문자열, 숫자)으로 작동하며 값을 SwiftUI 저장소에 직접 저장합니다. @StateObject는 참조 유형 — ObservableObject를 준수하는 클래스 — 으로 작동합니다. @State는 간단한 로컬 상태에 적합하며, @StateObject는 로직과 게시된 속성이 있는 복잡한 객체에 적합합니다.

iOS 13에서 @StateObject를 사용할 수 있나요?

아니요, @StateObject는 iOS 14 이상에서만 사용할 수 있습니다. iOS 13의 경우 @ObservedObject를 사용하고 수동 수명 주기 관리와 함께 @State를 통해 부모 View에서 ObservableObject를 생성하세요. 대안으로 참조 의미 체계가 필요하지 않은 데이터에는 class 대신 struct와 함께 @State를 사용할 수 있습니다.

부모에서 전달된 객체를 자식 View에서 @StateObject로 사용하면 어떻게 되나요?

자식 View는 부모와 완전히 독립적인 ObservableObject의 자체 복사본을 생성합니다. 한쪽의 변경 사항은 다른 쪽에 영향을 미치지 않습니다. 이는 거의 항상 오류입니다. 부모로부터 객체를 받으려면 @ObservedObject를 사용하고 View 내에서 새 객체를 생성할 때만 @StateObject를 사용하세요.

@StateObject를 통해 생성된 객체는 언제 소멸되나요?

객체를 생성한 View가 SwiftUI 계층 구조에서 완전히 제거될 때 객체가 소멸됩니다. NavigationStack의 화면의 경우 네비게이션 스택에서 팝될 때 발생합니다. 모달 창의 경우 닫힐 때입니다. TabView의 경우 View가 캐시되지 않은 경우 탭을 전환할 때입니다.

초기화 시 @StateObject에 매개변수를 전달하려면 어떻게 해야 하나요?

밑줄을 통해 property wrapper에 접근하는 사용자 정의 init을 사용하세요: _viewModel = StateObject(wrappedValue: MyViewModel(param: value)). 이 패턴을 사용하면 View 수명 동안 단일 객체 생성이 보장되면서 ObservableObject에 모든 매개변수를 전달할 수 있습니다.

요약

  • @StateObject — View 내에서 ObservableObject를 생성하고 소유하기 위한 property wrapper, iOS 14부터 사용 가능.
  • 단일 생성 보장 — 객체는 한 번 초기화되며 View 재구축 시 다시 생성되지 않습니다.
  • 정보 소스 — @StateObject는 정보 소스이며 @ObservedObject는 단지 관찰자입니다.
  • 수명 주기 — 객체는 View가 SwiftUI 계층 구조에 존재하는 한 살아 있으며 떠날 때 소멸됩니다.
  • 매개변수로 초기화 — _viewModel과 StateObject(wrappedValue:)를 통한 property wrapper 접근이 필요합니다.
  • 소유권 오류 — 객체 생성에 @ObservedObject를 사용하면 재구축 시 데이터 손실이 발생합니다.
  • 하나의 객체 — 하나의 @StateObject — 공유 데이터의 경우 루트 View에서 @StateObject를 생성하고 @ObservedObject를 통해 자식에 전달합니다.

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기