@ObservedObject: 개념, 작동 방식 및 예제

저자: IT Sectr 게시일: 2026-06-19 읽는 시간: 7 분

@ObservedObject는 외부에서 전달된 ObservableObject 인스턴스를 관찰하기 위한 SwiftUI의 Property Wrapper입니다. @StateObject와 달리 @ObservedObject는 객체를 생성하지 않으며, 이미 존재하는 객체의 변경 사항을 구독합니다. Apple Developer Documentation(2025)에 따르면, @ObservedObject는 부모에 속한 데이터를 추적해야 하는 자식 뷰에서 사용됩니다. @ObservedObject는 객체의 수명 주기를 관리하지 않고 반응형 연결을 제공합니다.

핵심 요점

  • @ObservedObject — 소유권 없이 ObservableObject를 관찰하기 위한 Property Wrapper
  • 생성 없음 — 객체는 부모 뷰 또는 환경에서 전달됩니다
  • @Published — ObservableObject 내부의 속성으로, SwiftUI가 변경 사항을 추적합니다
  • 다시 그리기 — @Published 속성이 변경되면 SwiftUI가 구독 중인 모든 뷰를 업데이트합니다
  • @StateObject와 혼동하지 마세요 — @ObservedObject는 단일 인스턴스를 보장하지 않습니다

SwiftUI에서 @ObservedObject란?

@ObservedObject는 뷰를 ObservableObject의 변경 사항에 구독시키는 Property Wrapper입니다. ObservableObject는 Combine 프레임워크의 프로토콜로, objectWillChange 퍼블리셔를 구현해야 합니다. @Published로 표시된 속성이 ObservedObject 내부에서 변경되면 퍼블리셔가 신호를 보내고 SwiftUI는 @ObservedObject를 통해 구독 중인 모든 뷰를 다시 그립니다.

@ObservedObject의 주요 특징은 소유권이 없다는 것입니다. 뷰는 객체를 생성하거나 파괴할 책임이 없습니다. 객체는 부모 뷰에서(@StateObject를 통해) 생성되거나 @EnvironmentObject를 통해 주입됩니다. 자식 뷰는 변경 사항만 관찰하고 업데이트를 받습니다. 부모에서 객체가 교체되면 @ObservedObject는 새 인스턴스로 전환됩니다.

@ObservedObject는 데이터 공유 시나리오에 적합합니다: 사용자 모델, 공통 설정, 서버 연결 상태 등. 계층 구조의 여러 수준에 있는 여러 뷰가 동일한 데이터를 표시해야 하는 경우, 각 뷰의 @ObservedObject는 단일 소스에 대해 독립적이지만 일관된 구독을 생성합니다.

@ObservedObject vs @StateObject: 주요 차이점

@ObservedObject와 @StateObject의 차이는 SwiftUI 인터뷰에서 가장 자주 묻는 질문 중 하나입니다. 주요 규칙: @StateObject는 객체를 생성하고 소유하며, @ObservedObject는 이미 존재하는 객체를 관찰합니다. 이 규칙을 위반하면 예기치 않은 데이터 손실이나 이중 초기화가 발생합니다.

특성@StateObject@ObservedObject
객체 생성예, 뷰 초기화 시아니요, 이미 만들어진 것을 받습니다
소유권현재 뷰부모 컴포넌트
단일 인스턴스예, 전체 수명 주기 동안아니요, 교체될 수 있습니다
렌더링 시 재생성아니요, 유지됩니다부모에 따라 다릅니다
사용 위치루트 뷰 소유자자식 뷰

@StateObject는 객체가 한 번 생성되고 뷰 구조의 반복적인 초기화에서 살아남음을 보장합니다. @ObservedObject는 외부에서 객체를 받아 부모 구조 초기화 시마다 다시 생성됩니다. 부모가 객체에 @StateObject를 사용하는 경우, 자식 뷰는 안전하게 @ObservedObject를 적용할 수 있습니다 — 객체는 전체 계층 구조에서 고유합니다.

@ObservedObject가 변경 사항을 추적하는 방법

@ObservedObject의 추적 메커니즘은 Combine과 ObservableObject 프로토콜을 기반으로 합니다. 초기화 중에 SwiftUI는 objectWillChange 퍼블리셔를 호출합니다 — 객체는 @Published 속성을 변경하기 전에 신호를 방출해야 합니다. Combine은 신호를 SwiftUI 의존성 그래프로 전달하고, 의존하는 모든 뷰를 업데이트가 필요한 것으로 표시합니다. 이는 값이 변경되기 전에 동기적으로 발생합니다.

swift
class WeatherService: ObservableObject {
    @Published var temperature: Double = 22.0
    @Published var city: String = "Moscow"
}

struct WeatherView: View {
    @ObservedObject var weather: WeatherService

    var body: some View {
        VStack {
            Text("\(weather.city)")
            Text("\(weather.temperature)°C")
        }
    }
}

목록에서 WeatherService는 두 개의 @Published 속성을 가진 ObservableObject입니다. WeatherView는 @ObservedObject var weather: WeatherService를 선언하고 부모로부터 인스턴스를 받습니다. temperature가 변경되면 새 값을 설정하기 전에 objectWillChange가 실행되고, SwiftUI가 WeatherView를 다시 그려 실제 온도를 표시합니다. 구독은 SwiftUI에 의해 자동으로 관리되므로 개발자가 sink나 dispose를 호출할 필요가 없습니다.

@ObservedObject 사용 패턴

첫 번째 패턴은 초기화자를 통한 모델 전달입니다. 부모는 @StateObject를 통해 ObservableObject를 생성하고 자식 뷰에 @ObservedObject로 전달합니다. 이는 루트 뷰가 모델의 수명 주기를 관리하고 모든 중첩 컴포넌트가 변경 사항을 구독하는 표준 계층적 데이터 전송입니다.

두 번째 패턴은 EnvironmentObject로, SwiftUI 환경을 통한 @ObservedObject의 전역 버전입니다. 객체는 씬 또는 루트 뷰 수준에서 주입되며, 초기화자를 통한 명시적 전달 없이 모든 자식 컴포넌트에서 자동으로 사용할 수 있습니다. 자식 뷰 내에서 @EnvironmentObject는 @ObservedObject와 유사하게 작동하지만 환경에서 객체를 받습니다.

세 번째 패턴은 여러 ObservableObject의 구성입니다. 복잡한 애플리케이션에서 뷰는 여러 객체를 관찰할 수 있습니다: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. 이렇게 하면 서비스 간에 책임이 분리되고 각 컴포넌트의 테스트 가능성이 유지됩니다.

swift
struct DashboardView: View {
    @ObservedObject var user: UserViewModel
    @ObservedObject var network: NetworkMonitor

    var body: some View {
        VStack {
            Text("Welcome, \(user.name)")
            HStack {
                Circle()
                    .fill(network.isConnected ? Color.green : Color.red)
                    .frame(width: 10, height: 10)
            }
        }
    }
}

DashboardView는 UserViewModel과 NetworkMonitor를 관찰합니다. 각 객체는 자체 데이터 도메인을 처리하고 독립적으로 뷰에 변경 사항을 알립니다. 네트워크가 연결 해제되면 NetworkMonitor가 isConnected를 변경하고 SwiftUI가 DashboardView를 다시 그려 표시기 색상을 업데이트합니다. ObservableObject의 구성은 SwiftUI 애플리케이션에서 데이터를 구성하는 선호되는 방법입니다.

@Published: ObservableObject와 SwiftUI의 연결

@Published는 Combine의 Property Wrapper로, ObservableObject 내부의 속성에 자동으로 퍼블리셔를 추가합니다. @Published 속성이 변경되면 Combine이 objectWillChange 퍼블리셔를 통해 이벤트를 생성합니다. @ObservedObject 또는 @StateObject를 사용할 때 SwiftUI는 이 퍼블리셔를 구독하고 각 새 값마다 뷰를 다시 그립니다.

@Published는 옵셔널, 컬렉션 및 사용자 정의 구조체를 포함한 모든 유형을 지원합니다. 그러나 컬렉션(배열, 딕셔너리)의 경우 SwiftUI는 참조 교체만 추적하고 내용 변경은 추적하지 않습니다. 요소 추가 또는 제거를 감지하려면 전체 컬렉션을 재할당하거나 수동 objectWillChange.send()와 함께 ObservableObject를 사용해야 합니다.

중요한 세부 사항: @Published는 ObservableObject를 구현하는 클래스 내에서만 사용해야 합니다. ObservableObject 외부에서 @Published를 사용하면 컴파일 오류가 발생합니다. 또한 @Published를 지연 초기화 속성(lazy var)이나 계산된 속성에는 적용할 수 없습니다.

@ObservedObject의 일반적인 실수

가장 심각한 실수는 객체를 생성하기 위해 @ObservedObject를 사용하는 것입니다. 부모 뷰에서 @ObservedObject var model = UserViewModel()이라고 작성하면 렌더링할 때마다 새 UserViewModel 인스턴스가 생성됩니다. 데이터가 손실되고 @Published 구독이 다시 생성됩니다. 생성에는 항상 @StateObject를 사용하고, @ObservedObject는 이미 만들어진 객체를 받는 데만 사용하세요.

두 번째 실수는 기본 스레드 외부에서 @Published 속성을 수정하는 것입니다. ObservableObject는 Combine을 사용하며, 기본 스레드(main actor)에서 변경 사항을 보내야 합니다. 백그라운드 큐에서 @Published를 변경하면 SwiftUI가 부적절한 시점에 뷰를 다시 그려 경합 조건을 유발할 수 있습니다. 업데이트에는 DispatchQueue.main.async 또는 @MainActor를 사용하세요.

세 번째 문제는 순환 업데이트입니다. @Published 변경이 다시 @Published를 변경하는 부작용을 유발하면 무한 다시 그리기 루프가 발생합니다. 해결책: 가드 플래그(isUpdating)를 사용하거나 명확한 책임 경계를 가진 서로 다른 ObservableObject로 로직을 분리하세요.

자주 묻는 질문

@ObservedObject를 옵셔널로 만들 수 있나요?

네, SwiftUI는 @ObservedObject var model: UserViewModel?을 지원합니다. 그러나 객체가 nil인 동안 뷰는 변경 사항을 구독하지 않습니다. 값이 할당되면 구독이 자동으로 활성화됩니다.

@ObservedObject와 @EnvironmentObject의 차이점은 무엇인가요?

@ObservedObject는 초기화자를 통해 객체를 받고, @EnvironmentObject는 SwiftUI 환경을 통해 받습니다. @EnvironmentObject는 생성자를 통한 명시적 전달이 필요하지 않지만, 객체는 계층 구조의 최상위 수준에서 주입되어야 합니다.

ObservableObject 변경을 수동으로 SwiftUI에 알리려면 어떻게 하나요?

속성을 변경하기 전에 objectWillChange.send()를 호출하세요. 이는 @Published가 적합하지 않은 경우(예: 계산된 속성 또는 변경 전에 알려야 하는 컬렉션 작업)에 유용합니다.

배열 내부 변경 시 @ObservedObject가 뷰를 다시 그리지 않는 이유는 무엇인가요?

@ObservedObject와 @Published는 참조 교체를 추적하며 컬렉션 내용 변경은 추적하지 않습니다. 다시 그리기를 트리거하려면 배열을 재할당해야 합니다: items.append(newItem) → items = items 또는 변경 전에 objectWillChange.send()를 사용하세요.

View를 구현하지 않는 구조체에서 @ObservedObject를 사용할 수 있나요?

아니요, @ObservedObject는 View 프로토콜을 구현하는 유형 내에서만 사용할 수 있는 SwiftUI Property Wrapper입니다. 일반 구조체의 경우 ObservableObjectPublisher와 함께 Combine을 직접 사용하세요.

요약

  • @ObservedObject — 소유권 없이 ObservableObject를 관찰하기 위한 Property Wrapper
  • @StateObject — 객체 생성, @ObservedObject — 기존 객체 관찰
  • @Published — ObservableObject 속성을 위한 자동 퍼블리셔
  • 구독 — @ObservedObject 사용 시 SwiftUI가 자동으로 Combine 구독 관리
  • 구성 — 뷰는 여러 ObservableObject를 동시에 관찰 가능
  • Main actor — @Published 속성은 기본 스레드에서만 변경해야 함
  • EnvironmentObject — 환경을 통한 암시적 전달을 위한 @ObservedObject의 대안

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

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

프로젝트 논의

더 읽어보기