반응형

SwiftUI Study – List 성능과 안정성을 높이는 ForEach·id 선택 기준 (셀 깜빡임·재배치·중복 렌더링 방지)

 

1. 왜 중요한가 (문제 배경)

SwiftUI List / ForEach는 식별자(id) 기반으로 diffing·렌더링을 수행합니다.

따라서 id가 잘못되면 다음과 같은 문제가 발생합니다.

  • 스크롤 시 셀 깜빡임(flickering)
  • 셀 위치가 재배치되거나 튀는 현상
  • 상태가 있는 셀에서 값이 뒤섞임
  • View가 필요 이상으로 전체 재렌더링
  • 셀 삭제/삽입 시 애니메이션이 비정상적

문제의 핵심은 다음입니다.

“ForEach의 id는 절대 변하지 않는 고유 값이어야 한다.”

하지만 실무에서는 index를 id로 쓰거나, 매번 바뀌는 객체를 id로 지정해서

성능 문제와 UI 꼬임이 자주 발생한다.

 

2. 잘못된 패턴 예시

❌ 예시 1: index를 id로 사용하는 경우

ForEach(items.indices, id: \.self) { index in
    RowView(item: items[index])   // ❌ index는 데이터 변경 시 달라짐
}

문제점

- 아이템 삽입/삭제 시 index가 전부 바뀜

- SwiftUI diffing이 모든 셀을 새로운 셀로 판정 → 깜빡임 발생

- 셀이 가진 @State가 섞이거나 초기화됨

❌ 예시 2: 값 타입 전체를 id로 사용 (비효율)

ForEach(items, id: \.self) { item in   // ❌ 큰 구조체일 경우 비용 증가
    RowView(item: item)
}

문제점

- Hashable 연산 비용 증가

- 구조체가 클수록 diffing 성능 저하

- 의도치 않게 값이 변경되면 id 변화로 간주되어 셀 재생성 발생

❌ 예시 3: 서버 응답 배열을 그대로 ForEach에 바인딩

ForEach(dataFromAPI, id: \.id) { item in  // ❌ 서버가 id를 보장하지 않을 수도 있음
    RowView(item: item)
}

문제점

- 일부 API는 id가 빠지거나 중복되는 경우 있음

- SwiftUI는 id 충돌 시 undefined behavior (재배치, 깜빡임)

 

3. 올바른 패턴 예시

✅ 예시 1: 데이터 모델에 명확한 id를 부여

struct User: Identifiable {
    let id: UUID
    let name: String
}

사용:

ForEach(users) { user in
    UserRow(user: user)
}

장점

- UUID는 절대 중복되지 않음

- diffing 안정적

- 셀 상태 혼동 없음

✅ 예시 2: index가 아닌 item.id를 기준으로 ForEach 구성

ForEach(viewModel.items) { item in
    Row(item: item)
}

장점

- 삽입/삭제/정렬 변경에도 UI 안정적

- 애니메이션 자연스러움

- 리스트가 커질수록 성능 차이가 크게 나타남

✅ 예시 3: 서버 id 부족 시 로컬 UUID 생성

struct Product: Identifiable {
    let id = UUID()          // 로컬에서 생성
    let serverID: Int?
    let name: String
}

장점

- 서버 응답 구조에 관계없이 안정적인 고유 id 확보

- 리스트 성능 보장

- 일관된 diffing

✅ 예시 4: 값 타입을 id로 사용할 때는 변하지 않는 필드만 선택

ForEach(messages, id: \.messageID) { message in
    MessageRow(message: message)
}

장점

- message 구조가 달라져도 messageID가 같으면 셀을 재사용

- 메시지 앱, 채팅 UI에서 필수적인 패턴

 

4. 실전 적용 팁

✔ 팁 1 – id는 변하지 않는 값이어야 한다

index, 배열 순서 기반 id는 절대 사용 금지.

✔ 팁 2 – Identifiable 모델을 설계하는 것이 가장 안전

SwiftUI가 자동으로 id를 추적하여 재렌더링 최적화.

✔ 팁 3 – 서버 id가 불안정하면 로컬 UUID를 추가

API가 항상 믿을 만한 것은 아님.

✔ 팁 4 – 셀 내부에 @State가 있다면 반드시 안정적인 id가 필요

id가 변경되면 셀 자체가 재생성되어 내부 상태가 사라짐.

✔ 팁 5 – 리스트가 100개 이상이면 diffing 효율이 매우 중요해짐

잘못된 id는 성능 저하로 직결됨.

 

5. 정리

  • ForEach의 id는 절대로 변하지 않는 고유 값이어야 한다.
  • index 또는 구조체 전체를 id로 사용하는 것은 성능과 안정성을 크게 해친다.
  • Identifiable 모델 설계는 SwiftUI 리스트 구현의 핵심이다.
  • id 선택만 제대로 해도 셀 깜빡임·재배치·상태 손실 문제 대부분을 해결할 수 있다.
반응형
Posted by 까칠코더
,