클라이언트

[SwiftUI] SwiftUI View 렌더링 순서 관련 정리

애기공룡훈련병 2025. 2. 15. 16:37
반응형

SwiftUI는 선언형(Declarative) 방식의 UI 프레임워크로, UIKit과 다르게 데이터 변경에 따라 뷰가 자동으로 업데이트됩니다.

하지만 SwiftUI의 뷰 렌더링 순서를 명확하게 이해하지 않으면 예측하지 못한 동작이 발생할 수 있습니다.

이번 글에서는 SwiftUI의 뷰 렌더링 과정과 호출되는 순서를 정리해 보겠습니다.

1. SwiftUI 뷰 렌더링 과정 개요

SwiftUI의 뷰는 다음과 같은 과정으로 렌더링됩니다.

  1. 뷰 구조체(Struct) 인스턴스화: View 프로토콜을 준수하는 구조체가 생성됩니다.
  2. body 프로퍼티 평가: body가 호출되어 SwiftUI 뷰 트리가 구성됩니다.
  3. 뷰 업데이트 감지: @State, @Binding, @ObservedObject, @EnvironmentObject 등의 값 변경을 감지합니다.
  4. body 재평가(Re-evaluation): 감지된 변경 사항에 따라 body가 다시 호출됩니다.
  5. Diffing 및 업데이트: 변경된 부분만 찾아서 UI를 업데이트합니다.
  6. 렌더링(Rendering): 최종적으로 화면이 다시 그려집니다.

이 과정을 거쳐 SwiftUI는 최소한의 리소스로 효율적인 UI 업데이트를 수행합니다.

2. SwiftUI 뷰 렌더링 순서

2.1. 초기 렌더링(Initial Rendering)

초기 뷰가 화면에 렌더링될 때 호출되는 순서는 다음과 같습니다.

  1. 뷰 인스턴스 생성: struct 형태의 뷰가 생성됨.
  2. body 호출: SwiftUI가 뷰 계층을 구성하기 위해 body를 평가함.
  3. 뷰 변경 감지 및 State 적용: @State 등의 상태가 초기화됨.
  4. Diffing 및 최적화: SwiftUI는 변경된 부분만 탐색하여 업데이트할 준비를 함.
  5. 렌더링 완료: 화면에 첫 번째 UI가 표시됨.

2.2. 상태 변화로 인한 리렌더링(State-driven Re-rendering)

@State, @Binding, @ObservedObject 등의 값이 변경될 경우, SwiftUI는 다음과 같은 순서로 뷰를 다시 렌더링합니다.

  1. 상태 변경 감지: @State 또는 @ObservedObject가 변경됨.
  2. 뷰 트리 업데이트: SwiftUI가 관련된 body를 다시 호출함.
  3. Diffing 및 최소 업데이트: 변경된 부분만 찾아 UI 업데이트 수행.
  4. 렌더링 완료: 변경된 UI가 화면에 반영됨.

2.3. 부모-자식 뷰 렌더링 관계

부모 뷰의 상태가 변경되면, SwiftUI는 다음 규칙에 따라 자식 뷰를 업데이트합니다.

  • 부모 뷰의 body가 변경되면, SwiftUI는 그 안에 있는 자식 뷰들도 다시 평가합니다.
  • 하지만, @State@Binding을 사용하여 특정 자식 뷰에서만 변경이 발생하면, SwiftUI는 해당 뷰만 다시 그립니다.
  • EquatableView를 활용하면 특정 값이 변경되지 않으면 리렌더링을 방지할 수 있습니다.

3. onAppearonDisappear 호출 시점

뷰의 생명 주기와 관련된 onAppearonDisappear는 다음과 같은 순서로 호출됩니다.

  1. 뷰가 처음 화면에 나타날 때onAppear 호출됨.
  2. 뷰가 사라질 때onDisappear 호출됨.
  3. 탭 전환 등으로 다시 화면에 나타날 때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 등을 활용하면 불필요한 업데이트를 줄일 수 있다.
반응형