반응형
SwiftUI는 선언형(Declarative) 방식의 UI 프레임워크로, UIKit과 다르게 데이터 변경에 따라 뷰가 자동으로 업데이트됩니다.
하지만 SwiftUI의 뷰 렌더링 순서를 명확하게 이해하지 않으면 예측하지 못한 동작이 발생할 수 있습니다.
이번 글에서는 SwiftUI의 뷰 렌더링 과정과 호출되는 순서를 정리해 보겠습니다.
1. SwiftUI 뷰 렌더링 과정 개요
SwiftUI의 뷰는 다음과 같은 과정으로 렌더링됩니다.
- 뷰 구조체(Struct) 인스턴스화: View 프로토콜을 준수하는 구조체가 생성됩니다.
- body 프로퍼티 평가: body가 호출되어 SwiftUI 뷰 트리가 구성됩니다.
- 뷰 업데이트 감지: @State, @Binding, @ObservedObject, @EnvironmentObject 등의 값 변경을 감지합니다.
- body 재평가(Re-evaluation): 감지된 변경 사항에 따라 body가 다시 호출됩니다.
- Diffing 및 업데이트: 변경된 부분만 찾아서 UI를 업데이트합니다.
- 렌더링(Rendering): 최종적으로 화면이 다시 그려집니다.
이 과정을 거쳐 SwiftUI는 최소한의 리소스로 효율적인 UI 업데이트를 수행합니다.
2. SwiftUI 뷰 렌더링 순서
2.1. 초기 렌더링(Initial Rendering)
초기 뷰가 화면에 렌더링될 때 호출되는 순서는 다음과 같습니다.
- 뷰 인스턴스 생성: struct 형태의 뷰가 생성됨.
- body 호출: SwiftUI가 뷰 계층을 구성하기 위해 body를 평가함.
- 뷰 변경 감지 및 State 적용: @State 등의 상태가 초기화됨.
- Diffing 및 최적화: SwiftUI는 변경된 부분만 탐색하여 업데이트할 준비를 함.
- 렌더링 완료: 화면에 첫 번째 UI가 표시됨.
2.2. 상태 변화로 인한 리렌더링(State-driven Re-rendering)
@State, @Binding, @ObservedObject 등의 값이 변경될 경우, SwiftUI는 다음과 같은 순서로 뷰를 다시 렌더링합니다.
- 상태 변경 감지: @State 또는 @ObservedObject가 변경됨.
- 뷰 트리 업데이트: SwiftUI가 관련된 body를 다시 호출함.
- Diffing 및 최소 업데이트: 변경된 부분만 찾아 UI 업데이트 수행.
- 렌더링 완료: 변경된 UI가 화면에 반영됨.
2.3. 부모-자식 뷰 렌더링 관계
부모 뷰의 상태가 변경되면, SwiftUI는 다음 규칙에 따라 자식 뷰를 업데이트합니다.
- 부모 뷰의 body가 변경되면, SwiftUI는 그 안에 있는 자식 뷰들도 다시 평가합니다.
- 하지만, @State나 @Binding을 사용하여 특정 자식 뷰에서만 변경이 발생하면, SwiftUI는 해당 뷰만 다시 그립니다.
- EquatableView를 활용하면 특정 값이 변경되지 않으면 리렌더링을 방지할 수 있습니다.
3. onAppear와 onDisappear 호출 시점
뷰의 생명 주기와 관련된 onAppear와 onDisappear는 다음과 같은 순서로 호출됩니다.
- 뷰가 처음 화면에 나타날 때 → onAppear 호출됨.
- 뷰가 사라질 때 → onDisappear 호출됨.
- 탭 전환 등으로 다시 화면에 나타날 때 → onAppear 다시 호출됨.
4. 렌더링 최적화 방법
4.1. @State 최소화
@State는 뷰를 다시 렌더링하는 주요 원인이므로, 필요한 부분에서만 사용하도록 설계해야 합니다.
4.2. EquatableView 활용
EquatableView를 사용하면 값이 변경되지 않는 경우 렌더링을 방지할 수 있습니다.
struct CustomView: View, Equatable {
var text: String
var body: some View {
Text(text)
}
static func == (lhs: CustomView, rhs: CustomView) -> Bool {
return lhs.text == rhs.text
}
}
4.3. @ViewBuilder를 활용한 뷰 분리
뷰를 작은 단위로 나누면 불필요한 렌더링을 줄일 수 있습니다.
struct ParentView: View {
@State private var count = 0
var body: some View {
VStack {
Button("Increment") {
count += 1
}
CounterView(count: count)
}
}
}
struct CounterView: View {
let count: Int
var body: some View {
Text("Count: \(count)")
}
}
이렇게 하면 CounterView만 다시 렌더링되며, 부모 뷰의 불필요한 업데이트를 방지할 수 있습니다.
5. 결론
SwiftUI의 렌더링 순서를 이해하면 예측 가능한 UI 동작을 만들 수 있으며, 성능 최적화에도 도움이 됩니다.
- SwiftUI는 데이터 변화를 감지하고 최소한의 렌더링만 수행한다.
- body는 상태 변화가 감지될 때마다 다시 평가된다.
- 부모-자식 관계에 따라 렌더링이 전파될 수 있으므로, 적절한 상태 관리를 해야 한다.
- 최적화를 위해 EquatableView나 @ViewBuilder 등을 활용하면 불필요한 업데이트를 줄일 수 있다.
반응형
'클라이언트' 카테고리의 다른 글
[Swift] Swift Concurrency 개념과 사용법 간단 정리 (0) | 2025.02.15 |
---|---|
[SwiftUI] SwiftUI 성능 최적화: View Rendering을 줄이는 방법 (0) | 2025.02.15 |
[SwiftUI] SDWebImage SwiftUI로 WebP 이미지 보여주기 (0) | 2025.02.15 |
Date 특정 월 - 주 별 시작과 끝 날짜 쌍 목록 구성하기 (0) | 2022.04.27 |
Mysteries of AutoLayout (0) | 2021.03.01 |