안녕하세요, iOS 개발하는 루피입니다!
오늘은 SwiftUI의 선언적 UI 패러다임의 핵심인 상태 관리에 대해 알아보겠습니다. 특히 가장 기본이 되는 State 프로퍼티 래퍼를 중심으로 공식 문서를 바탕으로 정리해보았습니다.
바로 시작합니다.
@State란 무엇인가?
@State는 SwiftUI에서 제공하는 Property Wrapper 타입의 하나로, 변수 앞에 위치하며 해당 프로퍼티 데이터 값에 접근할 수 있고 또한 최신 값을 반영합니다. 이름 그대로 상태(State) 자체를 나타내며, 상태가 변하면 UI도 이에 맞게 변합니다.
struct ContentView: View {
@State private var name = "World"
var body: some View {
VStack {
Text("Hello, \\\\(name)!")
.padding()
Button(
action: { self.switchName() },
label: { Text("Switch") }
)
}
}
func switchName() {
if name == "World" {
name = "Universe"
} else {
name = "World"
}
}
}
위 예제는 버튼을 누를 때마다 name 프로퍼티의 값을 바꾸는데, 이에 따라 View도 자동으로 내용이 업데이트되는 것을 확인할 수 있습니다. @State로 명시된 프로퍼티인 name과 UI가 아주 긴밀하게 움직이는 것을 볼 수 있습니다.
Combine 식으로 표현하자면, @State로 표기된 퍼블리셔의 변화에 따라 서브스크라이버인 View가 그 변화에 대응하고 있다고 할 수 있습니다.
Property Wrapper란?
Property Wrapper는 프로퍼티의 저장 방식과 사용 방식 사이에 분리 계층을 추가하는 Swift의 기능입니다.
@State는 SwiftUI에서 제공하는 Property Wrapper 중 하나로, 프로퍼티에 적용하면 해당 프로퍼티는 SwiftUI가 소유하고 관리하게 됩니다. 이 값은 뷰의 내부 상태를 나타내며, 뷰와 밀접하게 연결되어 있습니다.
@State의 내부 동작 원리
@State를 선언하면 내부적으로 다음과 같이 변환됩니다.
// 이 코드는
@State private var flag = false
// 내부적으로 이렇게 변환됩니다
private var _flag: State<Bool> = State(initialValue: false)
private var $flag: Binding<Bool> { return _flag.projectedValue }
private var flag: Bool {
get { return _flag.wrappedValue }
nonmutating set { _flag.wrappedValue = newValue }
}
이처럼 @State는 실제로 State<Value> 타입의 인스턴스를 생성하고, 이를 통해 값을 관리합니다.
해당 내용은 다른 개발자분의 github 내용을 참고한 내용입니다.
Re-implementation of @Binding and @State (from SwiftUI) myself to better understand it
Re-implementation of @Binding and @State (from SwiftUI) myself to better understand it - Bindings.swift
gist.github.com
@State의 특징과 사용법
뷰 계층 구조에서 특정 값 타입을 저장할 때, @State를 해당 값의 SSOT(Single Source of Truth)로 사용하는 것이 좋습니다. 이때, 저장된 값은 영구 저장소에 저장되는 것이 아닌, 임시 저장소에 저장되기에 앱이 종료되거나 View 생명 주기가 종료되면, 사라질 수 있다는 점을 기억해야 합니다.

App, Scene, 또는 View에서 @State 속성을 프로퍼티 선언에 적용하고, 초기 값을 제공하여 상태 값을 생성합니다.
상태는 SwiftUI가 제공하는 저장소 관리와 충돌을 피하기 위해 private로 선언하는 것이 좋습니다.
private 선언의 중요성
@State는 해당 View 내에서만 액세스 가능합니다. 따라서 @State로 적용할 프로퍼티는 가급적 private로 선언하는 것이 좋습니다. 다른 말로 표현하면, @State는 결국 특정 View의 특징 혹은 소유물이라는 의미로도 해석할 수 있습니다. 그래서 해당 프로퍼티를 소유하고 있는 View는 마음대로 액세스할 수 있다면, 외부의 다른 View는 액세스하지 못하거나 혹은 읽기만 가능해지는 디자인이 만들어집니다.
또한, private 선언은 memberwise 초기화에서 상태를 설정하는 것을 방지합니다.
memberwise 초기화란?
Swift의 구조체는 기본적으로 모든 저장 프로퍼티를 매개변수로 받는 초기화 함수를 자동으로 생성합니다. 이를 memberwise 초기화라고 합니다. @State 프로퍼티를 private으로 선언하면, 이 자동 생성된 이니셜라이저에서 해당 프로퍼티가 제외됩니다.
struct PlayButton: View {
@State private var isPlaying: Bool = false // 상태 생성
var body: some View {
Button(isPlaying ? "Pause" : "Play") { // 상태 읽기
isPlaying.toggle() // 상태 쓰기
}
}
}
SwiftUI는 프로퍼티의 저장소를 관리합니다. 값이 변경되면 SwiftUI는 해당 값에 의존하는 뷰 계층의 일부를 자동으로 업데이트합니다. 상태의 실제 값에 접근하려면 wrappedValue 프로퍼티를 사용하지만, Swift는 상태 인스턴스를 직접 참조하여 wrappedValue에 접근하는 간편한 방법을 제공합니다. 위 예에서는 isPlaying 프로퍼티를 직접 참조하여 상태의 값을 읽고 씁니다.
초기화 관련 추가 정보
때로는 init 메서드에서 조건부로 @State 변수를 초기화해야 할 때가 있습니다. 이 경우 _를 사용하여 State 인스턴스 자체에 접근할 수 있습니다:
struct ConditionalView: View {
@State private var counter: Int
init(startFromZero: Bool) {
// @State 변수 초기화 방법
_counter = State(initialValue: startFromZero ? 0 : 10)
}
var body: some View {
Text("Counter: \\\\(counter)")
}
}
상태 프로퍼티는 값을 필요로 하는 뷰 계층의 최상위 뷰에서 private으로 선언하세요. 그런 다음, 해당 상태를 읽기 전용으로 직접 공유하거나, 읽기-쓰기 접근을 위해 바인딩(Binding)으로 하위 뷰와 공유할 수 있습니다. 상태 프로퍼티는 어떤 스레드에서도 안전하게 변경할 수 있습니다.
https://developer.apple.com/documentation/swiftui/state
State | Apple Developer Documentation
A property wrapper type that can read and write a value managed by SwiftUI.
developer.apple.com
오늘도 화이팅입니다!
'iOS > SwiftUI' 카테고리의 다른 글
| [SwiftUI] StateObject (0) | 2025.04.21 |
|---|---|
| [SwiftUI] Bindable (1) | 2025.04.21 |
| [SwiftUI] 상태 관리 - PropertyWrapper (2) (0) | 2025.04.17 |
| [SwiftUI] UI 상태 관리 (Managing user interface state) (0) | 2025.04.15 |
| [SwiftUI] Model Data (0) | 2025.04.15 |