---
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]]
