반응형
SwiftUI에서 많이 하는 실수 - ObservableObject에서 @Published를 남발하는 실수
ObservableObject에서 @Published는 값이 바뀔 때마다 View에 변경을 알리는 역할을 합니다.
모든 프로퍼티에 습관적으로 @Published를 붙이면, 작은 변경에도 불필요하게 많은 View 리렌더링이 발생합니다.
1. 문제 원인
- “나중에 쓸 수도 있으니 일단 @Published 붙여두자”라는 습관
- 어떤 값이 실제로 UI에 영향을 주는지 고민하지 않음
- 하나의 ViewModel에 너무 많은 책임(도메인 + UI)을 몰아넣음
2. 나타나는 증상
- 스크롤/입력 시 CPU 사용량이 높고 프레임 드랍
- 작은 상태 변화에도 화면 전체가 깜빡이듯 재렌더링
- 어떤 값이 변해도 특정 View가 계속 body를 다시 그리는 현상
3. 잘못된 코드 예시
final class CardsViewModel: ObservableObject {
// ❌ 전부 @Published
@Published var allCards: [Card] = []
@Published var filteredCards: [Card] = []
@Published var searchText: String = ""
@Published var isLoading: Bool = false
@Published var errorMessage: String? = nil
@Published var selectedCard: Card? = nil
@Published var lastUpdated: Date? = nil
}
위 상태에서 searchText만 바꿔도, 이 ViewModel을 구독하는 모든 View가 다시 계산됩니다.
4. 올바른 코드 예시
4-1. 진짜 UI에 필요한 값만 @Published
final class CardsViewModel: ObservableObject {
// ✅ UI에 직접 그려지는 값 위주로
@Published private(set) var filteredCards: [Card] = []
@Published var searchText: String = ""
@Published var isLoading: Bool = false
@Published var errorMessage: String? = nil
private var allCards: [Card] = [] // ✅ 내부 캐시는 @Published 불필요
private var lastUpdated: Date? = nil
func load() {
// allCards, lastUpdated 변경은 내부에서만 사용
}
}
4-2. 서브 ViewModel로 구조 분리
final class CardListState: ObservableObject {
@Published var filteredCards: [Card] = []
@Published var searchText: String = ""
}
final class CardsViewModel {
let ui = CardListState()
private var allCards: [Card] = []
// 도메인 관련 값들...
}
UI는 CardListState만 구독하고,
나머지 도메인 값들은 별도의 계층에서 관리할 수도 있습니다.
5. 정리 및 팁
- “이 값이 바뀌면 UI가 반드시 다시 그려져야 하는가?”를 기준으로 @Published 여부를 결정합니다.
- 디버깅 시 body 호출 횟수를 로그로 찍어보면,
어떤 Published가 과도하게 영향을 주는지 감을 잡을 수 있습니다.
struct SomeView: View {
var body: some View {
print("body:", Self.self)
return Text("...")
}
}
- 너무 비대해진 ViewModel은 UI 상태 전용 객체, 도메인 서비스/UseCase 등으로 분해하는 것이 좋습니다.
반응형
'Dev Study > SwiftUI' 카테고리의 다른 글
| SwiftUI에서 많이 하는 실수 - ScrollView 안에서 LazyVStack을 사용하지 않아 성능이 나빠지는 실수 (0) | 2025.12.05 |
|---|---|
| SwiftUI에서 많이 하는 실수 - offset을 레이아웃 도구로 사용해서 반응형이 깨지는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 - GeometryReader를 오용해 레이아웃이 엉망이 되는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 - ViewModel에 비즈니스 로직과 UI 상태를 모두 섞어버리는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 - UI 업데이트를 메인 스레드에서 하지 않아 생기는 문제 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 - .task와 .onAppear를 혼동해 비동기 로직이 중복 실행되는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 - onAppear가 여러 번 호출되는 특성을 무시하고 API를 중복 호출하는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 - sheet / fullScreenCover 상태를 여러 곳에서 제어해 모달이 깜빡이거나 중복되는 실수 (0) | 2025.12.05 |

