@EnvironmentObject는 SwiftUI의 프로퍼티 래퍼로, 초기화자에서 명시적으로 전달하지 않고 전체 뷰 계층을 통해 ObservableObject를 자동으로 전달합니다. 자식 뷰는 프로퍼티를 선언하기만 하면 환경 객체에 접근할 수 있고, 부모는 .environmentObject() 메서드를 통해 이를 제공합니다. Apple Developer Documentation(2025)에 따르면 SwiftUI는 환경 수준에서 의존성 주입 메커니즘을 사용하여 중간 뷰의 초기화자를 통해 데이터를 전달할 필요가 없습니다. @EnvironmentObject는 인증 모델, 쇼핑 카트 또는 전역 설정과 같이 애플리케이션의 여러 화면에서 필요한 객체에 특히 유용합니다.
핵심 포인트
.environmentObject() 메서드를 통해 수행되며, 객체는 모든 자식 요소에서 사용 가능해집니다@Environment를 통해 새 매크로에서도 계속 작동합니다@EnvironmentObject는 SwiftUI 프레임워크에서 선언된 프로퍼티 래퍼로, 뷰가 환경에 저장된 객체에 접근할 수 있게 합니다. @State나 @StateObject와 달리 @EnvironmentObject는 객체를 생성하지 않으며, 뷰 계층의 조상 중 하나가 제공한 기존 인스턴스만 읽습니다.
이 메커니즘은 SwiftUI 환경을 기반으로 합니다. 루트 뷰에서 모든 자식 뷰로 전달되는 암시적 사전입니다. 부모가 .environmentObject(someObject) 메서드를 호출하면 SwiftUI는 환경에 someObject에 대한 참조를 배치합니다. 하위 트리의 모든 뷰는 @EnvironmentObject var model: ViewModel을 선언하고 동일한 인스턴스를 가져올 수 있습니다.
Apple WWDC 2021 세션 “Demystify SwiftUI”에 따르면 환경은 성능 저하 없이 깊은 계층을 통해 데이터를 전달하도록 최적화되어 있으며, 객체 접근은 유형 기반 조회를 통해 O(1)로 발생합니다. 이는 복잡성이 계층 깊이에 따라 선형적으로 증가하는 초기화자를 통한 수동 전달과 대조됩니다.
애플리케이션의 여러 수준에서 필요한 전역 상태에는 @EnvironmentObject를 사용하세요. 일반적인 예는 인증 모델, 네비게이션 관리자, 쇼핑 카트 및 네트워크 데이터 제공자입니다.
@EnvironmentObject는 환경 기반 의존성 주입이라는 SwiftUI 메커니즘을 사용합니다. SwiftUI가 계층을 렌더링할 때 각 수준에서 읽고 쓸 수 있는 내부 사전 EnvironmentValues를 유지합니다. @EnvironmentObject 프로퍼티 래퍼는 변경 사항을 구독하기 위해 ObservableObject 프로토콜의 objectWillChange를 사용하여 이 사전에서 유형별로 읽습니다.
프로세스는 세 단계로 구성됩니다. 첫째, 계층의 어딘가에서 ObservableObject를 생성합니다. 일반적으로 부모 뷰에서 @StateObject 또는 @ObservedObject를 통해 생성합니다. 둘째, 해당 뷰에서 .environmentObject(object)를 호출하여 객체를 환경에 배치합니다. 셋째, 자식 뷰에서 @EnvironmentObject를 선언하면 자동으로 동일한 인스턴스를 받고 구독합니다.
SwiftUI는 객체 내부의 @Published 프로퍼티가 변경될 때마다 이 유형으로 @EnvironmentObject를 선언한 모든 뷰가 다시 렌더링되도록 보장합니다. Donny Wals(2024)의 기사에 따르면 구독 메커니즘은 @ObservedObject와 동일하며, 차이는 인스턴스를 얻는 방식에만 있고 업데이트 메커니즘에는 없습니다.
객체가 가능한 한 높은 수준에서 제공되도록 계층을 설계하세요. 이렇게 하면 코드 중복 없이 객체가 필요한 모든 뷰에 접근 권한이 보장됩니다.
두 프로퍼티 래퍼 — @EnvironmentObject와 @ObservedObject — 모두 ObservableObject를 구독하고 변경 시 뷰를 다시 렌더링합니다. 주요 차이점은 객체를 얻는 방식에 있습니다. @ObservedObject는 뷰 초기화자를 통한 인스턴스의 명시적 전달이 필요하지만, @EnvironmentObject는 환경에서 자동으로 가져옵니다.
세 수준의 계층을 고려해보세요: ParentView → MiddleView → ChildView. ChildView에 UserSettings 객체가 필요한 경우 @ObservedObject를 사용하면 MiddleView가 이 객체를 사용하지 않더라도 MiddleView를 통해 전달해야 합니다:
struct MiddleView: View {
@ObservedObject var settings: UserSettings // only needed to pass down
var body: some View {
ChildView(settings: settings)
}
}
@EnvironmentObject를 사용하면 MiddleView는 객체의 존재를 알 필요가 없습니다:
struct MiddleView: View {
var body: some View {
ChildView()
}
}
struct ChildView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
Text(settings.username)
}
}
Swift by Sundell(2024)에 따르면 @EnvironmentObject는 계층의 여러 수준에서 객체가 필요할 때 선호되며, @ObservedObject는 객체가 부모에서 단일 직접 자식으로 직접 전달될 때 더 좋습니다. 로컬의 일회성 전달에는 @ObservedObject를, 전역 의존성에는 @EnvironmentObject를 선택하세요.
@Environment와 @EnvironmentObject는 모두 SwiftUI 환경에서 데이터를 읽지만 다른 소스로 작업합니다. @Environment는 EnvironmentValues에서 기본 제공 또는 사용자 정의 값을 읽습니다. 이는 색상, 글꼴, 크기, 달력, 레이아웃 방향과 같은 단순한 데이터입니다. @EnvironmentObject는 ObservableObject를 준수하는 참조 유형을 읽습니다.
주요 차이점은 업데이트 메커니즘입니다. @Environment는 개별 값 수준에서 publish-subscribe를 사용합니다. 환경이 변경되면 해당 값을 읽는 뷰만 다시 렌더링됩니다. @EnvironmentObject는 ObservableObject의 objectWillChange를 구독하여 어떤 특정 프로퍼티가 변경되었는지와 관계없이 이 유형을 구독하는 모든 뷰가 다시 렌더링될 수 있습니다.
Hacking with Swift(Paul Hudson, 2025)에 따르면 @Environment는 구성 매개변수(색상 구성표, 동적 글꼴 크기, 기기 방향)에 적합합니다. @EnvironmentObject는 비즈니스 로직과 상태(데이터 모델, 서비스, 관리자)를 위한 것입니다. 정적이거나 거의 변경되지 않는 매개변수에는 @Environment를, 반응성이 필요한 동적 데이터에는 @EnvironmentObject를 사용하세요.
실제로 이 두 메커니즘은 종종 함께 사용됩니다. @EnvironmentObject가 데이터를 제공하고 @Environment가 표시 컨텍스트를 제공합니다.
가장 흔한 실수는 접근 시 환경에 객체가 없는 경우입니다. 뷰가 @EnvironmentObject var model: ViewModel을 선언했지만 어떤 조상도 .environmentObject(model)을 호출하지 않은 경우 SwiftUI는 “ViewModel 유형의 ObservableObject를 찾을 수 없습니다”라는 메시지와 함께 fatal error를 발생시킵니다. 이는 컴파일 시간이 아닌 렌더링 시간에 발생하므로 오류는 런타임에만 나타날 수 있습니다.
두 번째 흔한 문제는 동일한 유형의 여러 인스턴스입니다. SwiftUI는 환경에서 조회를 위한 키로 객체 유형을 사용합니다. 두 개의 다른 조상이 .environmentObject를 통해 ViewModel의 다른 인스턴스를 제공한 경우 자식 뷰는 계층에서 가장 가까운 인스턴스를 받아 예기치 않은 동작이 발생할 수 있습니다. 해결책은 각 유형이 환경에 정확히 한 번만 존재하도록 설계하는 것입니다.
세 번째 실수는 하나 또는 두 개의 뷰만 필요한 데이터에 @EnvironmentObject를 과도하게 사용하는 것입니다. 이 경우 초기화자를 통한 명시적 전달이 있는 @ObservedObject가 더 투명한 데이터 흐름을 제공하고 테스트를 단순화합니다. Point-Free(2025)에 따르면 환경에 객체가 과도하게 많으면 뷰 의존성을 이해하기 어렵고 코드의 예측 가능성이 떨어집니다.
각 @EnvironmentObject가 계층의 올바른 수준에서 제공되는지 확인하고 중요한 객체의 경우 onAppear에서 폴백 검사를 추가하여 누락을 조기에 발견하세요.
전역 인증 상태를 가진 애플리케이션의 전체 예제를 살펴보겠습니다. 사용자 로그인 상태를 저장하는 ObservableObject AuthManager를 만들고 @EnvironmentObject를 통해 모든 화면에 제공합니다:
import SwiftUI
import Combine
class AuthManager: ObservableObject {
@Published var isLoggedIn = false
@Published var username: String = ""
func login(user: String) {
username = user
isLoggedIn = true
}
func logout() {
username = ""
isLoggedIn = false
}
}
루트 뷰가 환경을 통해 AuthManager를 제공합니다:
@main
struct MyApp: App {
@StateObject private var authManager = AuthManager()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(authManager)
}
}
}
자식 뷰가 명시적 전달 없이 AuthManager를 받습니다:
struct ProfileView: View {
@EnvironmentObject var authManager: AuthManager
var body: some View {
VStack {
if authManager.isLoggedIn {
Text("Hello, \(authManager.username)")
Button("Log Out") {
authManager.logout()
}
} else {
Button("Log In") {
authManager.login(user: "user")
}
}
}
}
}
세 번째 예제는 여러 ObservableObject와 @EnvironmentObject와 @Environment의 조합에 관한 것입니다. 애플리케이션이 쇼핑 카트용 CartManager와 색상 구성표용 ThemeManager를 사용한다고 가정합니다. 둘 다 최상위 수준에서 제공되며 초기화자를 통한 전달 없이 모든 화면에서 사용할 수 있습니다. 이는 특히 깊게 중첩된 화면이나 모달 프레젠테이션에서 유용하며, 생성자를 통한 데이터 전달이 기술적으로 어렵기 때문입니다.
자주 묻는 질문
@ObservedObject는 뷰 초기화자를 통한 인스턴스의 명시적 전달이 필요하지만 @EnvironmentObject는 SwiftUI 환경에서 자동으로 객체를 가져옵니다. @EnvironmentObject는 계층의 여러 수준에서 필요한 데이터에 편리하며 @ObservedObject는 부모와 자식 간의 직접 전달에 선호됩니다.
SwiftUI는 런타임에 fatal error를 발생시킵니다: “X 유형의 ObservableObject를 찾을 수 없습니다.” 오류는 @EnvironmentObject를 선언한 뷰를 렌더링할 때 발생하며, 어떤 조상도 이 유형의 객체로 .environmentObject()를 호출하지 않은 경우입니다. 컴파일러는 이 상황에 대해 경고하지 않습니다.
네, @EnvironmentObject는 iOS 13.0, macOS 10.15, tvOS 13.0 및 watchOS 6.0부터 사용 가능합니다. 2019년 Apple이 SwiftUI와 함께 도입한 최초의 프로퍼티 래퍼 중 하나이며 @Observable 매크로를 포함한 iOS 17 및 18을 포함한 모든 후속 버전에서 작동합니다.
객체 수는 무제한입니다 — 각 유형이 고유 키로 작용합니다. AuthManager, CartManager, NavigationManager 및 기타 서비스를 각각에 대해 .environmentObject()를 별도로 호출하여 전달할 수 있습니다. 환경에 동일한 유형의 객체가 두 개 있으면 안 됩니다. 이는 정의되지 않은 동작을 초래합니다.
테스트에서 ObservableObject의 인스턴스를 만들고 Preview Provider 또는 XCTest에서 .environmentObject(obj)를 통해 전달하세요. 뷰 주입의 단위 테스트의 경우 구체적인 클래스 대신 프로토콜을 사용하는 것이 편리합니다. 실제 계층을 변경하지 않고 종속성을 mock 객체로 대체할 수 있습니다.
요약
.environmentObject()를 통해 환경에 배치되고 유형별로 검색됨턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.