개발/SwiftUI
SwiftUI에서 많이 하는 실수 - onAppear가 여러 번 호출되는 특성을 무시하고 API를 중복 호출하는 실수
까칠코더
2025. 12. 5. 11:26
반응형
SwiftUI에서 많이 하는 실수 - onAppear가 여러 번 호출되는 특성을 무시하고 API를 중복 호출하는 실수
SwiftUI에서 onAppear는 View가 화면에 “보일 때마다” 호출됩니다.
View가 재생성되거나, 레이아웃/상태 변화로 인해 다시 계산될 때도 호출될 수 있습니다.
이 사실을 고려하지 않고 onAppear 안에서 네트워크 호출을 직접 하면,
같은 API를 여러 번 호출하는 문제가 생깁니다.
1. 문제 원인
- UIKit의 viewDidLoad / viewWillAppear와 동일하게 생각
- “화면 진입 시 1회 호출”이라고 오해
- ViewModel에 플래그를 두지 않고 매번 load() 호출
2. 나타나는 증상
- 탭을 바꿨다 돌아올 때마다 동일 데이터 재호출
- 스크롤/필터 변경 등 단순 동작에 의해 API가 반복 호출
- 서버 로그에서 같은 요청이 여러 번 찍히는 것을 발견
3. 잘못된 코드 예시
struct CardsView: View {
@StateObject private var viewModel = CardsViewModel()
var body: some View {
List(viewModel.cards) { card in
Text(card.title)
}
.onAppear {
// ❌ 진입할 때마다 (또는 재 계산마다) 호출될 수 있음
viewModel.load()
}
}
}
4. 올바른 코드 예시 (ViewModel에서 1회 로딩 보장)
final class CardsViewModel: ObservableObject {
@Published var cards: [Card] = []
private var hasLoaded = false
func loadIfNeeded() {
guard !hasLoaded else { return }
hasLoaded = true
// 네트워크 / 디스크 로딩
}
}
struct CardsView: View {
@StateObject private var viewModel = CardsViewModel()
var body: some View {
List(viewModel.cards) { card in
Text(card.title)
}
.onAppear {
viewModel.loadIfNeeded() // ✅ 중복 호출 방지
}
}
}
또는 Swift Concurrency를 쓰는 경우:
struct CardsView: View {
@StateObject private var viewModel = CardsViewModel()
var body: some View {
List(viewModel.cards) { card in
Text(card.title)
}
.task {
await viewModel.loadIfNeeded()
}
}
}
5. 정리 및 팁
- “정말 한 번만 실행되어야 하는 초기화 로직”은 View가 아니라 ViewModel 내부에서 플래그로 제어하는 것이 안전합니다.
- onAppear는 View가 여러 번 등장·소멸할 수 있는 환경이라는 점을 항상 기억해야 합니다.
반응형