안녕하세요, iOS 개발하는 루피입니다.
오늘은 SwiftUI View와 Modifiers에 대해 공부하면서 궁금했던 some View, 특히 some 키워드에 대해 좀 더 자세히 알아보겠습니다. 바로 시작합니다.
1. some View를 왜 써야 하는가?
지난 글에서 "some View는 View 프로토콜을 준수하는 어떤 객체이지만, 정확히 어떤 타입인지는 말하고 싶지 않다"라고 간략하게 설명했는데요. 그래 편하겠지... 근데 뭐가 어떻게 편한거야? 그리고 왜 써야하는지 잘 와 닿지가 않더라고요... 그래서 이 부분에 대해 더 자세히 살펴보겠습니다.
1) 편리성과 코드 가독성
만약 우리가 some View를 사용하지 못한다면 어떻게 될까요?
만약 Text를 반환하고 싶다면...
struct ContentView: View {
var body: Text {
Text("Hello, World!")
}
}
만약 HStack을 사용한다면...
struct ContentView: View {
var body: HStack<TupleView<(Text, Text, Text)>> {
HStack {
Text("A")
Text("B")
Text("C")
}
}
}
이렇게 하나하나 복잡한 타입을 직접 작성해줘야 합니다.
더 복잡한 뷰 계층구조라면 타입은 아마 말도 안되게 복잡해 지겠죠?? 하지만 some View 한 줄로 이 모든 복잡성을 추상화할 수 있습니다.
2) 연관 타입이 있는 프로토콜의 제약
"그러면 그냥 View만 써도 되지 않나? 어차피 Text든 HStack이든 다 View 프로토콜을 채택하고 있는 거잖아요?"라고 생각할 수 있습니다.
하지만 이렇게 하면 코드가 컴파일되지 않습니다.
struct ContentView: View {
var body: View {
Text("Hello, World!")
}
} // 컴파일 에러!
이는 View 프로토콜이 associated type을 가지고 있기 때문인데요!
Swift에서는 연관 타입이 있는 프로토콜을 직접 반환 타입으로 사용할 수 없습니다. 이런 경우에 some을 사용해야 합니다. Shape와 같은 다른 프로토콜도 마찬가지로, 연관 타입이 있는 프로토콜을 반환값으로 사용할 경우 some을 꼭 붙여주어야 합니다.
func makeShape() -> some Shape { return Circle() }
3) 타입 안정성 유지
some View는 opaque return type으로, 컴파일러는 내부적으로 정확한 타입을 알고 있지만, 외부에는 그 타입을 숨깁니다.
이는 타입 안정성을 유지하면서도 API를 단순화할 수 있게 해줍니다.
또한 some View를 사용하면 함수나 프로퍼티는 항상 동일한 구체 타입을 반환해야 합니다. 이는 타입 일관성을 보장하여 예측 가능한 동작을 제공합니다.
코드로 살펴 보면..
func makeShape(isCircle : Bool) -> some Shape {
if isCircle {
return Circle()
}
else {
return Rectangle()
}
} // 컴파일 에러
이와 같이 조건에 따라 다른 타입을 반환할 경우 컴파일 에러가 발생하게 됩니다. 따라서 우리는 some 을 사용할 경우 항상 동일한 구체 타입을 반환해야만 합니다.
2. body
SwiftUI를 사용하면서 body 프로퍼티에 대해 고민해보신 적 있으신가요?
왜 body를 써야하는거지?? 그냥 의미상 변수명을 맞춘건가? 여기에 대한 답을 찾아보려합니다.
변수명을 body가 아닌 k로 한번 변경해봤습니다.
struct ContentView: View {
var k: some View {
Text("Hello")
}
} // 컴파일 에러
그러자 컴파일 에러가 발생했습니다.
발생한 에러는 "Type 'ContentView" does not conform to protocol 'View' 입니다. 즉, ContentView가 View 프로토콜을 따르지 않는다는 에러인데요. 그리고 Protocol require nested type 'Body' (SwiftUICore.View.Body) 라는 경고도 같이 보입니다.
즉, SwiftUI 에서 View Protocol을 준수하기 위해서는 반드시 body가 필요하다는 것입니다. 그렇다면 왜 꼭 body가 필요할까요???
View 프로토콜에 대해 알아보겠습니다.
public protocol View {
associatedtype Body : View
@ViewBuilder var body: Self.Body { get }
}
View 프로토콜을 채택한 구조체는 반드시 body라는 계산 프로퍼티를 구현해야합니다.
여기서 잠시 위에서 배운 내용을 한번더 적용하고 가겠습니다. 보시면 View 프로토콜은 연관타입으로 Body가 있습니다. 그리고 우리는 연관 타입이 있는 프로토콜을 반환 값으로 사용하기 위해서는 some을 사용해야한다는 것을 알고 있기에 view가 아닌 some View를 사용하는 것입니다.
그리고 @ViewBuilder 속성이 적용되어 있어 여러 뷰를 조합할 수 있는 것입니다. 만약 다른 계산 프로퍼티에서도 여러 뷰를 반환하고 싶다면, 직접 @ViewBuilder 속성을 추가해야 합니다.
오늘도 화이팅입니다.
'iOS > SwiftUI' 카테고리의 다른 글
| [SwiftUI] Demystify SwiftUI (2/3) - Lifetime (0) | 2025.10.29 |
|---|---|
| [SwiftUI] Demystify SwiftUI (1/3) - Identity (0) | 2025.10.29 |
| [SwiftUI] View & Modifiers (0) | 2025.05.13 |
| [SwiftUI] StateObject (0) | 2025.04.21 |
| [SwiftUI] Bindable (1) | 2025.04.21 |