--- id: ios-swiftui-lifecycle-view-identity title: SwiftUI Lifecycle 와 View Identity category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [ios, swiftui, identity, lifecycle, vibe-coding] tech_stack: { language: "Swift / SwiftUI", applicable_to: ["iOS 16+"] } applied_in: [] aliases: [view identity, .id(), onAppear, onChange] --- # SwiftUI Lifecycle 와 View Identity > SwiftUI 는 view 를 **identity** 로 추적. identity 가 같으면 같은 view (state 보존), 다르면 새 view (state 리셋). `if/else` 분기, `.id()` modifier, `ForEach id`, 그리고 위치 — 이 4가지가 identity 를 결정. ## 📖 핵심 개념 - View 는 struct (value type) — render 마다 다시 만들어짐. - 그러나 `@State` 는 identity 가 같으면 보존. - identity 가 바뀌면 onAppear → onDisappear → 새 onAppear. ## 💻 코드 패턴 ### `.id()` 로 강제 재생성 ```swift struct UserView: View { let userId: String @State private var draft = "" var body: some View { TextField("draft", text: $draft) } } // 부모 UserView(userId: id) .id(id) // userId 바뀌면 새 view → draft 리셋 ``` ### if/else 가 identity 만듦 ```swift if isLoggedIn { HomeView() // identity A } else { LoginView() // identity B } // 전환 시 onDisappear/onAppear 발생 ``` ### ForEach 의 id 가 핵심 ```swift ForEach(items, id: \.id) { item in Row(item: item) } // id 가 같으면 같은 row (애니메이션 부드러움) // index 를 id 로 쓰면 reorder 시 사고 ``` ### onAppear vs onChange vs task ```swift struct Detail: View { let id: String @State private var data: Data? var body: some View { Text("...") .onAppear { /* 매 등장마다 호출 */ } .task(id: id) { // id 바뀌면 이전 task cancel + 새 task. async 가능. self.data = try? await load(id: id) } .onChange(of: id) { oldId, newId in // id 바뀐 시점만 } } } ``` ## 🤔 의사결정 기준 | 의도 | 도구 | |---|---| | 비동기 작업 + id 변경 시 cancel/restart | `.task(id:)` | | 매 등장마다 1회 작업 | `.onAppear` | | 특정 값 변경에 반응 | `.onChange(of:)` | | sheet/navigation 등장만 | `.task` (lifecycle 묶임) | | 강제 view 리셋 | `.id(value)` | ## ❌ 안티패턴 - **ForEach 에 `id: \.self` + 같은 값 가진 항목**: 충돌. unique id. - **`.task` 에 cancellation 무시 + 긴 loop**: id 변경 후에도 옛 task 가 setState. 새 task 가 자동 cancel 하므로 within task에서 checkCancellation. - **onAppear 에서 fetch 후 setState**: id 변경 시 cancel 안 됨. `.task(id:)` 권장. - **`.id()` 남발**: 모든 view 강제 재생성 → 애니메이션 깨짐 + 성능 저하. - **부모 rebuild 마다 자식 `@StateObject` 새로**: identity 다른 경우. 위치/조건 점검. - **Animation 안에서 ForEach id 가 index**: 항목 추가/삭제 시 잘못된 row animate. ## 🤖 LLM 활용 힌트 - "이 view 는 어떤 값에 의해 identity 가 바뀌어야 하는가?" 질문 후 .id() 또는 .task(id:) 결정. - 비동기 + cancel 필요 = `.task(id:)`. ## 🔗 관련 문서 - [[iOS_SwiftUI_State_Property_Wrappers]] - [[iOS_Swift_Concurrency_async_await]]