SwiftUI는 관찰하는 상태가 바뀔 때마다 body를 다시 실행하고, 새 뷰 트리를 이전 것과 diff하여 달라진 부분만 업데이트합니다. 성능 문제는 그 diff가 너무 많은 일을 하게 만드는 데서 옵니다. 불안정한 정체성, 지나치게 넓은 관찰, 값비싼 body 계산 말입니다. 정체성을 제어하고, 상태 범위를 좁히고, 를 저렴하게 유지하여 해결합니다.
bodySwiftUI는 렌더링 간에 뷰를 정체성으로 매칭합니다. 리스트의 경우 이는 id입니다. id가 불안정하면 SwiftUI는 뷰를 업데이트하는 대신 버리고 다시 만듭니다(상태와 애니메이션을 잃음).
// 나쁨: 배열 인덱스를 id로 — 재정렬/삽입이 정체성을 깨뜨려 전체 재구축
ForEach(items.indices, id: \.self) { i in Row(items[i]) }
// 좋음: 데이터에 묶인 안정적 정체성
ForEach(items) { item in Row(item) } // items: Identifiable, 안정적 id
ObservableObject는 어떤 @Published 프로퍼티가 바뀌든 그것을 관찰하는 모든 뷰를 재렌더링합니다. 큰 모델을 분할하거나, 프로퍼티별 읽기를 추적하는 Observation 프레임워크(@Observable, iOS 17+)로 이동하세요. 그러면 뷰는 실제로 사용한 프로퍼티가 바뀔 때만 재렌더링됩니다.
@Observable final class Feed { // iOS 17: 세밀한 의존성 추적
var unreadCount = 0 // .posts만 읽는 뷰는 이 변경에 재렌더링 안 함
var posts: [Post] = []
}
body는 자주 실행되므로, 무거운 작업을 인라인으로 하지 마세요:
var body: some View {
List(sortedItems) { ... } // 나쁨: sortedItems가 렌더링마다 정렬한다면
}
// 좋음: 파생 데이터는 body가 아니라 모델에서 계산
LazyVStack/List를 사용해 화면 밖 행이 만들어지지 않게 하세요..equatable()이나 EquatableView를 추가해 안정적인 서브트리의 diffing을 단락 평가하세요.AnyView는 타입 정체성을 지우고 구조적 diffing을 무력화합니다. 핫 패스에서는 피하세요.120Hz ProMotion 디스플레이에서는 프레임당 약 8ms밖에 없습니다. 큰 배열을 정렬하거나 전체 리스트를 재렌더링하는 body는 프레임을 떨어뜨리고 스크롤을 버벅이게 만듭니다. 시니어 면접이 뷰 정체성과 관찰 범위를 파고드는 이유는, 실제 SwiftUI 앱이 느려지는 지점이 바로 거기이고, 몇 가지 정밀한 수정이 부드러움을 되찾아 주기 때문입니다.
주니어부터 시니어까지 상세한 답변이 포함된 IT 면접 질문 라이브러리.
후원하기