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() 로 강제 재생성
structUserView:View{letuserId:String@Stateprivatevardraft=""varbody:someView{TextField("draft",text:$draft)}}// 부모UserView(userId:id).id(id)// userId 바뀌면 새 view → draft 리셋
if/else 가 identity 만듦
ifisLoggedIn{HomeView()// identity A}else{LoginView()// identity B}// 전환 시 onDisappear/onAppear 발생
ForEach 의 id 가 핵심
ForEach(items,id:\.id){iteminRow(item:item)}// id 가 같으면 같은 row (애니메이션 부드러움)// index 를 id 로 쓰면 reorder 시 사고
onAppear vs onChange vs task
structDetail:View{letid:String@Stateprivatevardata:Data?varbody:someView{Text("...").onAppear{/* 매 등장마다 호출 */}.task(id:id){// id 바뀌면 이전 task cancel + 새 task. async 가능.self.data=try?awaitload(id:id)}.onChange(of:id){oldId,newIdin// 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:) 결정.