반응형
SwiftUI에서 많이 하는 실수 25 : 하나의 상태를 여러 곳에서 동시에 수정해 충돌이 나는 실수
SwiftUI는 단방향 데이터 흐름을 기반으로 합니다.
즉, 상태는 한 곳에서만 진짜 값을 가지고, 나머지는 그 값을 “구독”만 해야 합니다.
하지만 같은 모델을 여러 ViewModel/Service가 동시에 수정하면,
어느 쪽 변경이 최종 결과인지 예측하기 어려워지고,
UI와 내부 데이터가 서로 다른 값을 보게 되는 일이 발생합니다.
1. 문제 원인
- 동일한 배열/딕셔너리를 여러 객체에 참조로 넘겨놓고, 각자 수정
- 같은 도메인 상태를 여러 ViewModel이 소유
- 클린 아키텍처/리덕스/TCA의 “single source of truth” 개념을 무시
2. 나타나는 증상
- 화면 A에서 삭제한 항목이 화면 B에서는 그대로 남아 있음
- A/B 화면이 번갈아 상태를 바꾸다 보면, 최종 상태가 이상해짐
- 디버깅 시, 어느 시점에 상태가 변경됐는지 추적이 힘듦
3. 잘못된 코드 예시
final class ListViewModel: ObservableObject {
@Published var cards: [Card] = []
}
final class DetailViewModel: ObservableObject {
// ❌ 같은 배열을 참조로 넘겨 받아서 직접 수정
@Published var cards: [Card]
init(cards: [Card]) {
self.cards = cards
}
func delete(_ card: Card) {
cards.removeAll { $0.id == card.id }
}
}
View 쪽에서:
let listVM = ListViewModel()
let detailVM = DetailViewModel(cards: listVM.cards) // ❌ 복사/참조 모호
4. 올바른 코드 예시
4-1. 상태 소유자 한 곳으로 통합
@MainActor
final class CardsStore: ObservableObject {
@Published private(set) var cards: [Card] = []
func delete(_ card: Card) {
cards.removeAll { $0.id == card.id }
}
}
4-2. ViewModel은 Store를 참조만
final class ListViewModel: ObservableObject {
@ObservedObject var store: CardsStore
init(store: CardsStore) { self.store = store }
}
final class DetailViewModel: ObservableObject {
@ObservedObject var store: CardsStore
let cardID: Card.ID
init(store: CardsStore, cardID: Card.ID) {
self.store = store
self.cardID = cardID
}
var card: Card? {
store.cards.first { $0.id == cardID }
}
func delete() {
if let card = card {
store.delete(card) // ✅ 단일 소스에서 수정
}
}
}
- 실제 데이터 변경은 CardsStore 한 곳에서만 일어납니다.
5. 정리 및 팁
- “이 도메인 상태(예: 카드 리스트)의 최종 소유자는 어디인가?”를 먼저 정해야 합니다.
- 나머지 계층(ViewModel, View, Helper)은 그 소유자를 의존성으로 받아 읽기/요청만하는 구조로 만들면,
충돌과 불일치가 크게 줄어듭니다. - 리덕스/TCA/단일 Store 패턴은 이런 문제를 체계적으로 해결하기 위한 대표적인 접근 방식입니다.
반응형
'Dev Study > SwiftUI' 카테고리의 다른 글
| SwiftUI에서 많이 하는 실수 24 : AppStorage / SceneStorage를 남용해 예기치 않은 상태 유지가 발생하는 실수 (0) | 2025.12.05 |
|---|---|
| SwiftUI에서 많이 하는 실수 23 : View 중첩이 지나치게 깊어져 가독성과 유지보수가 나빠지는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 22 : Modifier 순서를 이해하지 못해 스타일이 적용되지 않는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 21 : 도메인 모델과 UI 전용 모델을 구분하지 않는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 20 : Preview에서 실제와 다른 상태를 구성해 놓고 디버깅이 꼬이는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 19 : onChange(of:)를 잘못 사용해 과도한 리렌더링이 일어나는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 18 : animation()을 남발해 의도치 않은 애니메이션이 발생하는 실수 (0) | 2025.12.05 |
| SwiftUI에서 많이 하는 실수 17 : List 셀 내부에서 상태 변경/로직을 수행해 무한 렌더링이 발생하는 실수 (0) | 2025.12.05 |


