
SwiftUI에서 ForEach를 통해 반복되는 UI를 구성할때 `id: \.self`나 `id: \.id`와 같은 코드를 본 적이 있을것이다.
나는 이 코드의 의미에 대해 궁금해졌다.
이번 글에서는 SwiftUI의 Identity(동일성)에 대해 알아보고, 다음 글에서는 KeyPath에 대해 알아보도록 하겠다.
SwiftUI의 Identity
SwiftUI가 뷰를 그리는 방식은 다음과 같다.
`@State`등의 Source of Truth가 되는 프로퍼티가 변화하면 body 프로퍼티가 다시 렌더링 되면서 View가 다시 그려진다.
즉, `@State` 변수의 값이 변경되면 뷰가 해당 값을 반영한 형태로 다시 그려지는 것이다.
그렇다면, 어떻게 같고 다른것을 구분할 수 있는 것일까?
이 때 등장하는 개념이 Identity이다.
SwiftUI Identity는 최적화 매커니즘을 통해 어떤게 같고, 다른지 구분하려고 하는 개념이다.
그리고 이 Idenetity는 두 가지로 구분되는데, `Structural Identity(구조적 동일성)`, `Explicit Indentity(암묵적 동일성)`이 그것이다.
Structural Identity(구조적 동일성)
구조적 동일성은 뷰의 구조와 위치를 기반으로 Diff 연산을 해서 달라진점을 구분한다.
즉, 상태에 따라 뷰가 바뀌는지가 구조적 동일성을 지키는지에 영향을 미친다.
다음 코드를 보자.
if genre == "로맨스" {
Image(systemName: "star")
} else {
Text("로맨스 아님")
}
이러한 뷰는 genre라는 상태에 따라 뷰의 구조와 위치가 변경될 수 있다.
즉, 이는 구조적 동일성 측면에서 최적화된 매커니즘이 아니라고 할 수 있다.
그 이유는 상태에 따라 뷰의 구조와 위치가 변경되므로 렌더링을 계속 하게 되므로, 비효율적인 렌더링이 일어나기 된다.
반면 다음의 코드는 구조적 동일성을 지키는 코드이다.
struct TamagochiView: View {
@State var count = 100
var body: some View {
VStack {
Text("몇개냐면요")
Text("\(count)개")
Text("추가하기")
.wrapToButton {
count += 1
}
}
}
}
이 코드는 count라는 상태가 변경되면 Texr("\(count)개")만 렌더링되고, 다른 뷰들은 렌더링이 되지 않는 구조적 동일성이 지켜지는 효율적인 코드라고 볼 수 있다.
만약 위와 같은 동작을 하지만, 상태에 따라 뷰를 분기처리하면 구조적 동일성에 있어 비효율적인 코드가 된다.
struct TamagochiView: View {
@State var count = 100
var body: some View {
VStack {
if count < 100 {
Text("몇개냐면요")
} else {
Text("개수가 많아요")
}
Text("\(count)개")
Text("추가하기")
.wrapToButton {
count += 1
}
}
}
}
count가 100개를 넘는 경우에 `개수가 많아요`라는 Text를 보여주는데,
100개를 넘지 않는 경우에는 `몇개냐면요`라는 Text가 변하지 않고 보여진다.
그러나, count가 올라갈때마다 게속 `if count < 100`이 조건문을 판단해야하기 때문에
Text가 변하지 않더라도 계속 `Text("몇개냐면요")`라는 뷰가 렌더링 된다.
이는 구조적 동일성에 있어서 비효율적이라고 볼 수 있다.
Explicit Indentity(암묵적 동일성)
암묵적 동일성은 개발자가 직접 구분되는 점을 조작하는 것으로, 각 뷰에 id를 부여해서 구분이 되도록 하는 개념이다.
다음의 코드를 보자.
ForEach(list, id: \.id) { item in
EventView(
image: "star",
text: "\(item.name): \(item.count)"
)
}
이렇게 ForEach문을 통해 뷰를 반복적으로 생성하는 경우에 id 즉, 구분자를 두고 각 뷰를 구분한다.
즉, id를 부여함으로써 "동일한 뷰의 경우 렌더링을 하지 않아도 된다"고 개발자가 직접 조작할 수 있다.
반복문에서는 안에 들어오는 컨텐츠들이 추가되거나, 삭제되는 등 변경될 수 있기 때문에 특히 구조적 동일성을 지키기 어렵다.
그래서 암묵적 동일성을 부여해서 효율적인 렌더링을 하는 것이다.
다음은 암묵적 동일성의 코드이다.
struct TamagochiView: View {
@State private var list = ["가", "나"]
var body: some View {
VStack {
ForEach(list, id: \.self) { item in
Text("\(item)")
}
Text("리스트에 추가하기")
.wrapToButton {
list.append("a")
}
}
}
}
이 코드를 보면 `/.self`자기 자신 자체를 id로 사용해 각 뷰를 구분하면서 암묵적 동일성을 지킬 수 있다.
그렇다면 `\.self`는 문법적으로 어떤 의미일까?
여기서 KetPath라는 개념이 등장하게 되는데, 이에 대해서는 다음 글에서 계속 소개하도록 하겠다.
'iOS > SwiftUI' 카테고리의 다른 글
| [iOS/SwiftUI] ForEach 반복문의 \.id는 무슨 의미일까? - KeyPath 알아보기 (0) | 2025.04.22 |
|---|---|
| [iOS/SwiftUI] VStack과 LazyVStack의 차이 (0) | 2025.04.16 |
| [SwiftUI] 상호 작용 가능한 위젯 만들기 (0) | 2024.05.12 |