SwiftUI 고급 상태 관리 기법: @EnvironmentObject와 Combine
SwiftUI 고급 상태 관리 기법: @EnvironmentObject와 Combine
SwiftUI는 애플리케이션의 UI를 선언적으로 작성할 수 있게 해주는 강력한 프레임워크입니다. 간단한 애플리케이션에서는 @State와 @Binding 정도로도 상태 관리를 충분히 할 수 있지만, 복잡한 애플리케이션에서는 좀 더 강력한 상태 관리 기법이 필요합니다. 여기서 @EnvironmentObject
와 Combine 프레임워크
가 중요한 역할을 합니다. 이 글에서는 @EnvironmentObject
와 Combine
을 활용하여 애플리케이션의 상태를 효과적으로 관리하는 방법을 설명합니다.
@EnvironmentObject 소개
@EnvironmentObject
는 여러 뷰 사이에서 공유되는 객체를 쉽게 사용하게 도와줍니다. 이 객체는 앱의 환경에 묶여있으며, 여러 하위 뷰에서 이를 참조할 수 있습니다. 상위 뷰에서 @EnvironmentObject
로 지정된 객체는 하위 뷰에서 자동으로 주입됩니다.
먼저, 환경 객체로 사용할 클래스는 ObservableObject
프로토콜을 준수해야 합니다. 이 클래스는 SwiftUI에서 상태 변화를 감지할 수 있게 해줍니다.
swiftimport SwiftUI import Combine class AppState: ObservableObject { @Published var count: Int = 0 }
@Published
를 사용하면 해당 속성의 값이 변경될 때마다 자동으로 트리거됩니다. 이제 이 객체를 SwiftUI 뷰에서 어떻게 사용하는지 살펴보겠습니다.
swiftstruct CounterView: View { @EnvironmentObject var appState: AppState var body: some View { VStack { Text("Count: \(appState.count)") Button(action: { appState.count += 1 }) { Text("Increment") } } } }
여기서 CounterView
는 AppState
를 @EnvironmentObject
로 받습니다. 이제 이 뷰를 렌더링 할 때, AppState
객체를 환경에 주입해야 합니다.
swift@main struct MyApp: App { @StateObject var appState = AppState() var body: some Scene { WindowGroup { CounterView().environmentObject(appState) } } }
MyApp
에서 @StateObject
로 AppState
객체를 생성하고, 이를 환경 객체로 주입합니다. 이제 CounterView
에서 이 상태를 자동으로 사용할 수 있게 됩니다.
Combine 소개
Combine
은 애플리케이션의 반응형 프로그래밍을 위한 강력한 프레임워크입니다. 데이터 스트림을 생성하고 이를 관찰하고 변형할 수 있습니다. Combine
을 활용하면 비동기 작업을 좀 더 쉽게 관리할 수 있습니다. Combine
은 여러번의 연산 체인을 통해 데이터의 변화를 감지하고 처리할 수 있게 도와줍니다.
다음은 Combine
을 사용한 간단한 예제입니다.
swiftimport Combine class DataFetcher { var cancellable: AnyCancellable? func fetchData() { let url = URL(string: "https://jsonplaceholder.typicode.com/todos/1")! cancellable = URLSession.shared.dataTaskPublisher(for: url) .map { $0.data } .decode(type: Todo.self, decoder: JSONDecoder()) .sink(receiveCompletion: { completion in switch completion { case .finished: print("Done") case .failure(let error): print("Error: \(error)") } }, receiveValue: { todo in print("Todo: \(todo)") }) } }
위 코드에서 URLSession.shared.dataTaskPublisher
를 사용하여 URL 요청을 수행하고, 받은 데이터를 Todo
구조체로 디코딩합니다. sink
를 사용하여 최종 결과를 수신하고, 성공 또는 실패를 처리합니다.
@EnvironmentObject와 Combine 결합
이제 @EnvironmentObject
와 Combine
을 결합하여 조금 더 복잡한 애플리케이션 상태 관리를 구현해보겠습니다. 예를 들어, 원격 서버에서 데이터를 받아와서 앱 상태에 저장하는 애플리케이션을 만들어봅시다.
먼저 AppState
클래스에 Combine을 사용하여 원격 데이터를 가져오는 메소드를 추가합니다.
swiftimport SwiftUI import Combine class AppState: ObservableObject { @Published var todos: [Todo] = [] private var cancellable: AnyCancellable? func fetchTodos() { let url = URL(string: "https://jsonplaceholder.typicode.com/todos")! cancellable = URLSession.shared.dataTaskPublisher(for: url) .map { $0.data } .decode(type: [Todo].self, decoder: JSONDecoder()) .replaceError(with: []) .receive(on: DispatchQueue.main) .assign(to: \.$todos, on: self) } }
이제 SwiftUI 뷰에서 이 메소드를 호출하여 데이터를 가져올 수 있습니다.
swiftstruct TodoListView: View { @EnvironmentObject var appState: AppState var body: some View { List(appState.todos) { todo in Text(todo.title) } .onAppear { appState.fetchTodos() } } }
TodoListView
는 @EnvironmentObject
로 AppState
를 받아와서 todos
를 리스트로 표시합니다. onAppear
에서 fetchTodos
메소드를 호출하여 데이터를 가져옵니다.
마지막으로, 앱의 진입점을 설정합니다.
swift@main struct MyApp: App { @StateObject var appState = AppState() var body: some Scene { WindowGroup { TodoListView().environmentObject(appState) } } }
이와 같이 @EnvironmentObject
와 Combine
을 결합하여 복잡한 상태 관리를 보다 쉽게 할 수 있습니다. 이를 통해 확장성과 유지보수성을 높인 애플리케이션을 개발할 수 있습니다.