SwiftUI에서 @Environment는 시스템 환경에서 값을 읽는 페이지 그림과 같이 View 계층 구조를 통해 자동으로 분포되는 property wrapper입니다. 이 컴포넌트는 색상 체계, 로케일, 폰트 크기, managedObjectContext 및 수십 개의 다른 시스템 매개변수에 액세스를 제공합니다. Apple Developer Documentation (2025)에 따르면, @Environment는 환경 값의 변경이 구독한 모든 View의 재그림을 유발하여 수동 호출 없이 반응형 인터페이스 업데이트를 제공함을 보장합니다.
주요 포인트
@Environment는 시스템 환경에서 값을 읽도록 설계된 SwiftUI property wrapper입니다. 환경은 SwiftUI가 부모 View에서 자식 View로 자동 분배하는 값의 계층 컨테이너입니다. 각 환경 값은 EnvironmentKey 프로토콜에 따르는 타입인 키로 식별됩니다.
환경 메커니즘은 프레임워크 레벨의 의존성 주입(dependency injection)과 유사합니다: 시스템은 미리 정의된 값의 셋을 제공합니다 — 색상 체계(밝음/어두움), 로케일, 폰트 크기, Core Data용 managedObjectContext, 화면을 닫기 위한 dismiss, 그리고 많은 다른 것들. 특정 키를 가진 @Environment를 선언한 View는 자동으로 현재 값을 받고 변경시 재그림됩니다.
SwiftUI 환경 아키텍처는 EnvironmentValues 프로토콜에 기반합니다 — 모든 시스템 값을 포함하는 구조체입니다. 각 값은 getter와 setter가 있는 이 구조체의 포함된 속성으로 저장됩니다. @Environment는 특정 속성에 액세스하기 위해 key path를 사용합니다: @Environment(\.colorScheme) — 색상 체계 액세스, @Environment(\.locale) — 로케일 액세스.
Property wrapper @Environment는 두 가지 핵심 메커니즔을 구현합니다: 환경에서 값을 읽고 그 변경에 구독하는 것입니다. View가 생성되면 SwiftUI는 모든 @Environment 속성을 통해 현재 컨텍스트의 해당 값과 연결합니다. 부모 View가 .environment() 수식자를 통해 값을 변경하면, 해당 값을 읽는 모든 자식 View가 자동으로 재그림됩니다.
중요한 기능: @Environment는 옵션변수 값을 지원합니다. 계층에서 값이 설정되지 않았으면 EnvironmentKey에 정의된 기본값이 반환됩니다. 시스템 키의 경우 기본값은 허수 적절합니다 — 예를 들어, 기본 색상 체계는 .light입니다. 커스텀 키의 경우, 개발자는 EnvironmentKey 프로토콜의 defaultValue 메서드에서 기본값을 정의합니다.
struct EnvironmentReaderView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.locale) var locale
@Environment(\.sizeCategory) var sizeCategory
var body: some View {
VStack {
Text("Current scheme: \(colorScheme == .dark ? "Dark" : "Light")")
Text("Locale: \(locale.identifier)")
Text("Font size: \(sizeCategory)")
}
}
}
예시에서 View는 세 가지 시스템 환경 값을 읽습니다. colorScheme이 변경될 때 — 예를 들어, 사용자가 설정에서 어두운 모드를 활성화했을 때 — View는 새 값으로 자동 재그림됩니다. 같은 방식으로 지역 또는 폰트 크기(Dynamic Type)가 변경될 때도 마찌가지입니다. View는 알림에 구독하거나 refresh를 호출할 필요가 없습니다 — SwiftUI가 이를 자동으로 관리합니다.
SwiftUI는 인터페이스와 동작의 다양한 측면을 다루는 수십 개의 시스템 환경 값을 제공합니다. 색상 체계 (\.colorScheme)는 가장 많이 사용되는 값 중 하나로, 밝은 맞고 어두운 테마에 맞게 인터페이스를 조절할 수 있게 합니다. 로케일 (\.locale)에는 날짜, 숫자 및 통화 포맷을 위한 사용자의 지역 설정이 포함됩니다.
Core Data의 경우, managedObjectContext (\.managedObjectContext)가 사용됩니다 — 퍼시스티스 커네이너에서 환경을 통해 전달되는 컨텍스트입니다. 네비게이션의 경우, 현재 화면을 닫기 위한 dismiss (\.dismiss) 및 모달 표시를 위한 isPresented (\.isPresented)가 제공됩니다. 캘린더와 시간대의 경우 — 각각 calendar와 timeZone이 있습니다.
| Key Path | 타입 | 목적 |
|---|---|---|
| \.colorScheme | ColorScheme | 밝은/어두운 테마 |
| \.locale | Locale | 지역 설정 |
| \.sizeCategory | ContentSizeCategory | Dynamic Type 폰트 크기 |
| \.managedObjectContext | NSManagedObjectContext | Core Data 컨텍스트 |
| \.dismiss | DismissAction | 화면 닫기 |
| \.calendar | Calendar | 현재 캘린더 |
| \.timeZone | TimeZone | 시간대 |
| \.horizontalSizeClass | UserInterfaceSizeClass | 가로 화면 크기 |
시스템 값에 액세스하려면 점이 든 key path를 사용하세요: @Environment(\.dismiss) var dismiss. 컴파일러는 EnvironmentValues에서 key path의 존재를 확인하뭐로, 올바르지 않은 키는 컴파일 타임 오류를 유발합니다. Apple은 iOS 버전마다 새로운 시스템 값을 추가합니다 — 전체 목록은 EnvironmentValues 문서에서 확인할 수 있습니다.
비슷한 이름에도 불구하고, @Environment와 @EnvironmentObject는 서로 다른 목적을 가집니다. @Environment는 EnvironmentKey를 통해 등록된 시스템 또는 커스텀 값을 읽습니다. @EnvironmentObject는 명시적인 키 없이 타입에 의해 환경을 통해 전달되는 ObservableObject용 property wrapper입니다.
@EnvironmentObject는 의존성 주입(dependency injection)에 사용됩니다: 부모 View가 객체(예: ViewModel)를 생성하고 .environmentObject() 수식자를 통해 자식 View에 전달합니다. 자식 View는 @EnvironmentObject를 통해 이를 수신하고 그 속성을 읽거나 수정할 수 있습니다. 반면, @Environment는 시스템 값에 대해 읽기 전용이며 피드백을 지원하지 않습니다.
| 매개변수 | @Environment | @EnvironmentObject |
|---|---|---|
| 목적 | 시스템 및 커스텀 값 | ObservableObject 주입 |
| 키 | EnvironmentValues key path | 객체 타입에 따라 |
| 쓰기 | 읽기 전용 | 읽기 및 쓰기 |
| 커스텀 값 | EnvironmentKey를 통해 | ObservableObject 클래스를 통해 |
| 기본값 | 있음 (defaultValue) | 없음 (전달해야 함) |
실제로: 시스템 매개변수(테마, 로케일, 폰트 크기) 및 런타임에 변경되지 않는 커스텀 구성에 액세스하려면 @Environment를 사용하세요. 상태를 자식 컴포넌트에서 수정해야 할 때 View 계층을 통해 ViewModel 또는 서비스를 전달하려면 @EnvironmentObject를 사용하세요.
커스텀 환경 값을 만드는 것을 살펴보겠습니다. 이를 위해 EnvironmentKey 프로토콜에 따르는 구조체를 정의하고 EnvironmentValues를 새 속성으로 확장해야 합니다. 이를 통해 props 없이 전체 View 트리에 테마 구성 또는 앱 설정을 전달할 수 있습니다.
struct AppThemeKey: EnvironmentKey {
static let defaultValue: AppTheme = .system
}
extension EnvironmentValues {
var appTheme: AppTheme {
get { self[AppThemeKey.self] }
set { self[AppThemeKey.self] = newValue }
}
}
enum AppTheme { case system, light, dark }
EnvironmentKey 프로토콜은 정적 defaultValue 속성의 구현이 필요합니다 — 부모 View가 커스텀 환경을 설정하지 않은 경우 사용될 값입니다. EnvironmentValues를 확장하면 키가 있는 subscript를 사용하여 계산된 속성 appTheme이 추가됩니다. 그 후, 어떤 View든 @Environment(\.appTheme)를 통해 값을 읽을 수 있습니다.
struct ThemedView: View {
@Environment(\.appTheme) var appTheme
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
if appTheme == .dark || (appTheme == .system && colorScheme == .dark) {
Text("Dark mode active")
.foregroundStyle(.white)
.background(Color.black)
} else {
Text("Light mode active")
.foregroundStyle(.black)
.background(Color.white)
}
}
}
}
struct ContentView: View {
@State private var selectedTheme = AppTheme.system
var body: some View {
ThemedView()
.environment(\.appTheme, selectedTheme)
}
}
ThemedView는 두 가지 환경을 읽습니다: 커스텀 appTheme와 시스템 colorScheme. 이 조합은 유연한 테마 구성을 가능하게 합니다: 사용자는 밝은 것, 어두운 것, 또는 시스템 테마를 선택할 수 있습니다. 시스템이 선택된 경우, 값은 iOS 설정에서 테마가 전환될 때 자동으로 변경되는 colorScheme에서 가져옵니다. 부모 View(ContentView)는 .environment() 수식자를 통해 appTheme 값을 설정합니다.
struct ModalView: View {
@Environment(\.dismiss) var dismiss
@State private var name = ""
var body: some View {
NavigationStack {
Form {
TextField("Your name", text: $name)
Button("Save") { dismiss() }
}
.navigationTitle("Edit Profile")
}
}
}
이 예시는 dismiss의 실용적인 사용을 보여줍니다 — 환경에서 DismissAction의 인스턴스입니다. 함수로서 dismiss()를 호출하면 모달 화면이 닫히거나 NavigationLink가 돌아갑니다. 유일한 요구사항은 View가 모달로 표시되거나 NavigationStack 내에 있어야 한다는 것입니다. dismiss는 컨텍스트에서 자동으로 결정됩니다: View가 sheet로 열린 경우 — sheet이 닫히고, popover로 열린 경우 — popover가 닫힉니다.
자주 묻는 질문
아니요, @Environment는 읽기 전용입니다. 값을 변경하려면 ObservableObject와 함께 @EnvironmentObject 또는 @Binding을 사용하세요. 커스텀 EnvironmentKeys는 확장에서 setter를 가질 수 있지만, 이를 통한 변경은 UI 업데이트를 유발하지 않습니다 — 기술적으로 가능하지만 권장되지 않습니다.
@Binding은 진리의 원천(State, StateObject, ObservableObject)과의 양방향 연결을 만듭니다. @Environment는 계층 컨텍스트에서 단방향 읽기입니다. @Binding은 자식 View에 데이터를 전달하는 데 적합하고, @Environment는 시스템 또는 전역 설정에 액세스하는 데 적합합니다.
정적 defaultValue를 가진 EnvironmentKey 프로토콜을 구현하는 구조체를 정의하세요. 그런 다음 subscript[key]를 통한 getter/setter로 속성을 가진 EnvironmentValues를 확장하세요. 등록 후, 읽기에는 @Environment(\.yourKey)를, 설정에는 .environment(\.yourKey, value)를 사용하세요.
SwiftUI는 50개 이상의 시스템 값을 제공합니다: colorScheme, locale, sizeCategory, managedObjectContext, dismiss, calendar, timeZone, horizontalSizeClass, verticalSizeClass, accessibilityEnabled, layoutDirection, legibilityWeight 그리고 기타. 전체 목록은 EnvironmentValues 문서에서 확인할 수 있습니다.
네, @Environment은 Preview에서 작동하지만 기본값이 시뮬레이터와 다른 수 있습니다. Preview에서 테스트하려면 Preview 코드에서 바로 .environment() 수식자를 사용하세요: ThemedView().environment(\.colorScheme, .dark). 이를 통해 다양한 환경 상태를 시각적으로 확인할 수 있습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.