Observation이 뭘까?
Observation은 프로퍼티의 변화를 추적하는 Swift의 새로운 기능입니다.
보통 데이터 모델 타입을 작성할 때는 여러가지 프로퍼티가 들어갑니다.
class FoodTruckModel {
var orders: [Order] = []
var donuts = Donut.all
}
여기에 @Observable을 추가하기만 해도 UI가 데이터 모델의 변화에 대응할 수 있게 됩니다.
@Observable class FoodTruckModel {
var orders: [Order] = []
var donuts = Donut.all
}
@Observable은 Swift 컴파일러에게 명령을 내려서 우리가 작성한 코드를 확장형 observable 타입으로 바꿉니다.
Observable 타입으로 SwiftUI 뷰를 작동시킬 수 있는 거죠.
(다른 프로퍼티 래퍼가 없어도 작동합니다)
@Observable class FoodTruckModel {
var orders: [Order] = []
var donuts = Donut.all
}
struct DonutMenu: View {
let model: FoodTruckModel
var body: some View {
List {
Section("Donuts") {
ForEach(model.donuts) { donut in
Text(donut.name)
}
Button("Add new donut") {
model.addDonut()
}
}
}
}
}

도넛을 보여주는 간단한 뷰를 보면서 설명을 해보겠습니다.
SwiftUI는 이 모델이 본문 요청을 실행할 때 특정 프로퍼티에 접근한다는 걸 알고 있습니다.
이 코드에서는 도넛 메뉴 뷰 본문을 실행할 때 'Donut'프로퍼티에 접근이 발생한다는 것을 감지합니다.
본문이 실행될 때 SwiftUI는 Observable 타입에서 사용된 프로퍼티의 모든 타입을 접근합니다.
그리고 이 추적 정보를 이용해서 특정 인스턴스에서 프로퍼티에 다음 변화가 언제 일어날지를 예측합니다.
@Observable class FoodTruckModel {
var orders: [Order] = []
var donuts = Donut.all
var orderCount: Int { orders.count }
}
struct DonutMenu: View {
let model: FoodTruckModel
var body: some View {
List {
Section("Donuts") {
ForEach(model.donuts) { donut in
Text(donut.name)
}
Button("Add new donut") {
model.addDonut()
}
}
Section("Orders") {
LabeledContent("Count", value: "\(model.orderCount)")
}
}
}
}
이렇게 코드에 연산 프로퍼티를 사용해도 마찬가지로 프로퍼티에 변화가 생기면 UI는 업데이트됩니다.
새로 추가된 콘텐츠에서는 모델의 orderCount를 호출해서 orders 프로퍼티에 접근합니다.
즉, orders가 변경되면 orderCount가 orders 프로퍼티에 접근했기 때문에 Text가 업데이트된다는 것입니다.
@Observable 매크로를 쓰면 타입이 확장되어서 Observation을 지원할 수 있게 됩니다.
그렇게 되면 이제 SwiftUI는 프로퍼티 접근을 추적할 수 있고 Observation에서 다음 프로퍼티가 언제 변할지 관찰할 수 있습니다. 그런 추적이 가능해지면 프로퍼티에 변화가 생겼을 때 UI는 뷰 본문을 재계산만 하면 됩니다.
(Apple에 따르면 이 부분에서 성능이 엄청나게 향상되었다고 합니다.)
SwiftUi property wrappers
SwftUI 작업의 핵심 프로퍼티 래퍼 3가지는 State, Environment, Bindable이 있습니다.
모델 안에 뷰 전용 상태를 저장해야 할 때 @State 프로퍼티를 사용합니다.
코드로도 살펴보겠습니다.
struct DonutListView: View {
var donutList: DonutList
@State private var donutToAdd: Donut?
var body: some View {
List(donutList.donuts) { DonutView(donut: $0) }
Button("Add Donut") { donutToAdd = Donut() }
.sheet(item: $donutToAdd) {
TextField("Name", text: $donutToAdd.name)
Button("Save") {
donutList.donuts.append(donutToAdd)
donutToAdd = nil
}
Button("Cancel") { donutToAdd = nil }
}
}
}
Observable 모델 객체인 도넛을 시트로 표현한 모습입니다.
시트 표현에서 donutToAdd 상태 변수는 편집 필드의 값을 바인딩 하는데 쓰입니다.
'donutToAdd' 프로퍼티는 소속된 뷰의 수명 동안만 관리할 수 있습니다.
다음은 @Envrionment입니다.
Envrionment는 각 값을 어디서든 접근할 수 있게 합니다. 즉, 여러 곳에서 공유가 가능해지죠.

접근을 기반으로 업데이트가 생성되기 때문에 Observable과 잘 어울립니다.
푸드 트럭 메뉴 뷰의 본문을 불러올 때는 Account 객체의 userName 프로퍼티에 접근이 이루어집니다.
userName이 바뀌면 메뉴 뷰도 업데이트됩니다.
다음은 @Bindable입니다.
애플은 @Bindable은 해당 타입으로부터 바인딩이 생성되게 하는 기능만 있기 때문에 굉장히 가볍다고 말합니다.
래핑된 Bindable 프로퍼티에서 바인딩을 만드는 건 간단합니다. $ 구문을 사용해서 프로퍼티에 바인딩을 만들기만 하면 됩니다.

지금 DonutView에서 name이 Text로 되어 있는데 실제로 사용하려면 이름을 수정해야 합니다. Text 대신에 TextField를 사용하면 됩니다.

이렇게 하면 이 TextField에서 바인딩이 일어납니다. 그 바인딩을 읽어서 TextField 값을 채우는 거지만 사용자가 값을 변경하면 바인딩에 회신하기도 합니다.
Donut에 바인딩을 만드려면 @Bindable 프로퍼티 래퍼를 붙여주면 됩니다.

이 모델이 뷰 상태로 있어야 하는가? -> 그렇다면 @State 사용
이 모델이 응용 프로그램의 전역 환경의 일부여야 하나? -> 그렇다면 @Environment 사용
이 모델에는 바인딩만 필요한가? -> 그렇다면 @Bindable 사용
감사합니다.
잘못된 내용이 있거나 더 좋은 내용 피드백은 언제나 환영합니다!
궁금하신 부분은 댓글로 질문 부탁드립니다!
'Apple > SwiftUI' 카테고리의 다른 글
| [SwiftUI] ViewBuilder 알아보기 (1) | 2026.03.31 |
|---|---|
| [SwiftUI] NavigationLink & NavigationStack (0) | 2025.10.22 |
| [SwiftUI] 앱에 Face ID 잠금 기능 적용하기 (8) | 2025.07.08 |
| [SwiftUI] SwiftUI 상태 동기화 트러블슈팅 (1) | 2025.06.25 |
| [SwiftUI] CADisplayLink를 활용한 부드러운 ProgressView 애니메이션 구현 트러블슈팅 (6) | 2025.06.06 |
