안녕하세요.
오늘은 Swift의 Concurrency 기능 중 하나인 async let을 사용해서, 기존에 작성했던 HealthKit 데이터 로딩을 리팩토링해본 경험을 정리해보려고 합니다.
이전에 작성했던 코드는 여러 데이터를 순차적으로 받아오는 구조였습니다. 요청이 적을 땐 별문제 없었지만, 요청이 많아질수록 각 요청이 직렬로 실행되면서 전체 응답 속도가 점점 느려지는 문제가 있겠다는 생각이 들었고, 자연스럽게 이걸 병렬로 처리할 수 있는 방법을 고민하게 됐습니다.
그러다가 async let을 알게 되었고, “이거라면 좀 더 깔끔하게 바꿀 수 있겠다?” 싶어서 직접 적용해보게 되었습니다.
기존 ViewModel에서는 다음과 같이 두 개의 비동기 작업을 순차적으로 실행하고 있었습니다.
func fetchData(selectedDate: Date) {
Task {
await fetchStepCount(date: selectedDate)
await fetchEnergyConsumption(date: selectedDate)
}
}
private func fetchStepCount(date: Date) async {
self.stepCount = Int(await hkService.fetchStepCount(date: date))
}
private func fetchEnergyConsumption(date: Date) async {
let (calories, workout, standTime) = await hkService.fetchCalorieConsumption(date: date)
self.calories = calories
self.workoutTime = workout
self.standTime = standTime
}
두 함수 모두 async이기 때문에, fetchStepCount가 끝나야 fetchEnergyConsumption이 실행됩니다.
데이터 간 의존성이 없는데도 이렇게 직렬로 처리하는 건, 리소스를 낭비하는 구조였어요.
async let은 이런 상황에 굉장히 적합한 도구입니다. 의존성이 없는 여러 비동기 작업을 간단하게 병렬 실행할 수 있기 때문이죠.
func fetchData(selectedDate: Date) {
Task {
async let step = hkService.fetchStepCount(date: selectedDate)
async let energy = hkService.fetchCalorieConsumption(date: selectedDate)
self.stepCount = Int(await step)
let (calories, workout, standTime) = await energy
self.calories = calories
self.workoutTime = workout
self.standTime = standTime
}
}
두 개의 비동기 함수가 async let을 통해 동시에 실행되고, 이후에 필요한 시점에 await으로 값을 받아옵니다.
이 덕분에 전체 fetch 시간이 줄어들고, UX 측면에서도 훨씬 자연스러운 흐름이 만들어졌습니다.
이번 리팩토링을 통해 느낀 건, “비동기 코드도 결국은 설계의 문제” 라는 점이었습니다.
기능이 되긴 하지만, 더 좋은 흐름이 있을 수 있다는 걸 직접 느껴보니 Concurrency에 대한 감각도 훨씬 생긴 것 같아요.
다음에는 Combine을 활용해서 HealthKit 데이터와 UI를 바인딩하는 구조도 함께 정리해볼 생각입니다.
감사합니다!