SwiftUI 상태 관리 마스터하기: @State부터 @EnvironmentObject까지의 깊은 고찰

1. SwiftUI 상태 관리의 핵심: Source of Truth

SwiftUI 아키텍처의 대원칙은 ‘단일 진실 공급원(Single Source of Truth)’입니다. 데이터가 이곳저곳에 흩어져 복사본으로 남아있지 않도록, 정확히 한 곳에서 관리하고 나머지는 이를 참조하는 구조를 만드는 것이 중요합니다.

SwiftUI Source of Truth Concept
중앙 집중식 데이터 관리와 뷰 계층으로의 데이터 전파

Pro-tip: `@State`는 뷰 내부의 비영구적인 로컬 상태(예: 버튼 토글 state)를 위해서만 사용하세요. 비즈니스 로직이 담긴 데이터는 별도의 `ObservableObject`로 분리하는 것이 정석입니다.

2. Property Wrapper 위계와 데이터 흐름

값 타입(Struct)을 위한 `@State`, 참조 타입(Class)을 위한 `@StateObject`와 `@ObservedObject`, 그리고 전역적인 주입을 위한 `@EnvironmentObject`. 이들의 차이를 명확히 아는 것이 성능 최적화의 핵심입니다.

SwiftUI Property Wrappers Hierarchy
상태 성격에 따른 적절한 Property Wrapper 선택 가이드

The ‘Bad’ Way: Multi-copy of Data (Consistency Issue)

// 하위 뷰에 데이터를 복사해서 전달 (상태 불일치 발생 위함)
struct ParentView: View {
  @State var name = "User"
  var body: some View {
    ChildView(name: name) // Binding이 아닌 일반 값 전달
  }
}

The ‘ELITE’ Way: Using @Binding for Synchronization

// Binding을 통해 단일 진실 공급원 유지
struct ChildView: View {
  @Binding var name: String
  var body: some View {
    TextField("Enter name", text: $name)
  }
}

3. 뷰 재생성 주기와 성능 최적화 (Body Re-evaluation)

SwiftUI는 상태가 바뀌면 뷰의 `body`를 다시 계산합니다. 이때 `@ObservedObject`의 무분별한 사용은 불필요한 뷰 재생성을 유발할 수 있습니다. 특히 뷰 초기화 시점에 Object를 생성한다면 `@StateObject`를 사용하여 뷰의 생명주기와 객체의 생명주기를 일치시켜야 합니다.

SwiftUI Rendering Cycle
상태 변화에 따른 효율적인 View Diffing 및 업데이트 프로세스

4. @EnvironmentObject: 의존성 주입의 양날의 검

`@EnvironmentObject`는 깊은 뷰 계층에서 데이터를 쉽게 공유하게 해주지만, 주입되지 않은 채 접근하면 앱이 크래시(Crash)됩니다. 따라서 프리뷰(Preview) 환경에서도 항상 목(Mock) 객체를 주입하는 습관을 들여야 합니다.

Lesson Learned: 대규모 앱에서는 TCA(The Composable Architecture)와 같은 엄격한 상태 관리 프레임워크 도입을 고려해 보세요. 예측 가능하고 테스트 가능한 코드를 작성할 수 있습니다.

5. 시니어의 조언: ‘왜’ 바뀌어야 하는지에 집중하라

단순히 화면을 바꾸는 것이 아니라, 어떤 사용자 액션이 데이터의 변화를 일으키고 그 결과가 어떻게 화면에 투영되는지 ‘데이터의 흐름’을 설계하십시오. SwiftUI는 도구일 뿐, 우아한 아키텍처는 개발자의 설계 고민에서 나옵니다.

결론: 단순함 뒤에 숨겨진 강력함

SwiftUI의 상태 관리는 선언적 UI의 핵심 심장부와 같습니다. 오늘 배운 속성들을 적재적소에 배치하여, 코드의 가독성은 높이고 비즈니스 로직은 명확히 분리된 진정한 ‘Elite’ 코드를 작성해 보시길 바랍니다.

댓글 남기기