안녕하세요, RUEL입니다.
최근 프로젝트를 SwiftUI로 진행하면서 조건별로 화면을 다르게 구성해야 하는 상황이 생겼습니다.
이때 처음에는 “Group을 써야 할까, ViewModifier를 사용해야 할까?”라는 고민에 빠져버렸지만,
(사실 고민할 거리도 아니었음.... 왜냐? 이미 결론에 있는 거처럼 쓰고 있었으니까 하하하)
정리하면서 생각해야할 부분이 잘못된 걸 알게 되었습니다.
그래서 이번 글에서 엉뚱하게 생각하던걸 다시 한번 바로 잡고자 합니다!
1. Group
Group은 여러 뷰를 묶어주는 컨테이너 역할을 합니다.
조건에 따라 뷰를 선택해서 직관적으로 보여줄 수 있죠.
그리고 간단합니다!
코드로 보게 되면
struct ContentView: View {
var type: Int
var body: some View {
Group {
if type == 0 {
Text("타입 0")
.font(.title)
.foregroundColor(.blue)
} else {
Text("타입 1")
.font(.headline)
.foregroundColor(.red)
}
}
}
}
위 코드와 같이 간단하게 조건에 따라 구분할 수 있어요.
- 장점
- 뷰 구조 전체를 조건부로 바꾸기 직관적
- 간단한 조건 분기 처리에 적합
- 단점
- 뷰 계층이 깊어지면 코드 가독성이 떨어짐
- 스타일 및 속성등이 중복될 수 있음
2. ViewModifier
ViewModifier는 뷰에 스타일이나 속성을 재사용 가능하게 적용할 때 사용해요.
뷰 구조는 동일하지만 속성이 달라지는 경우 적합하다고 볼 수 있어요.
struct TypeStyle: ViewModifier {
var type: Int
func body(content: Content) -> some View {
content
.font(type == 0 ? .title : .headline)
.foregroundColor(type == 0 ? .blue : .red)
}
}
extension View {
func typeStyle(_ type: Int) -> some View {
self.modifier(TypeStyle(type: type))
}
}
struct ContentView: View {
var type: Int
var body: some View {
Text("Hello SwiftUI")
.typeStyle(type)
}
}
- 장점
- 조건별 스타일을 재사용할 수 있음
- 코드 중복을 최소화할 수 있음
- 단점
- 뷰 구조 전체를 바꾸기에는 적합하지 않음
- 복잡한 조건 분기 처리 시 modifier 내부가 복잡해질 수 있음
3. 언제 어떤 걸 써야 하지?
| 조건 | 추천 | 이유 |
| 뷰 전체가 타입마다 완전히 다름 | Group | 조건별로 뷰 구조 자체가 달라야 직관적 |
| 뷰 구조는 같고 스타일/속성만 달라짐 | ViewModifier | 재사용 가능, 코드 중복 최소화 |
| 작은 뷰 속성 변화가 반복적 | ViewModifier | 반복되는 스타일을 중앙에서 관리 가능 |
| 간단한 조건 뷰 전환 | Group | 코드가 간단하고 직관적 |
- 뷰 구조 자체를 바꿀지 속성/스타일만 바꾸는지가 기준이 될 거 같다
여기까지는 그럴싸했지
4. 생각이 잘못되었다..
정리를 하다 보니 다시 느꼈다..
Group과 ViewModifier 중 어떤 걸 사용할지에 대한 고민이 이 둘을 제대로 이해하지 못한 상태에서 발생했다는 것을....
ViewModifier를 쓰거나 Group을 쓸지 비교할게 아니라, 조건에 따라 다른 View를 넣어줘야 했었는데....
기존 다른 feature에서는 조건에 따라 View를 넣어 사용하고 있었는데
갑자기 왜 이렇게 Group과 ViewModifier에 빠져서 고민을 했을까ㅎㅎㅎ;;
다시 생각해 보자면
- ViewModifier -> 뷰 스타일이나 속성 변경
- Group -> 뷰를 묶는 컨테이너
- 조건별 화면 전환 -> 서로 다른 View 구조를 넣어줌
조건에 따라 뷰 전체 레이아웃이나 구성이 완전히 달라져야 한다면, Group을 사용하며 조건별로 서로 다른 View (Struct)를 넣어줘야 할 것이다.
struct AView: View {
var body: some View {
VStack {
Text("A View")
.font(.title)
.foregroundColor(.blue)
Image(systemName: "star.fill")
}
}
}
struct BView: View {
var body: some View {
VStack {
Text("B View")
.font(.headline)
.foregroundColor(.red)
Image(systemName: "heart.fill")
}
}
}
struct ContentView: View {
enum ViewType {
case a
case b
}
var type: ViewType = .a
var body: some View {
Group {
switch type {
case a: AView()
case b: BView()
}
}
}
}
위와 같이 사용하게 된다면
- 조건별 화면이 명확해짐
- 각각의 뷰를 독립적으로 관리 가능
기존에도 위와 같이 사용하다가 이상한 방향으로 빠져버려 혼란이 와버린 상황이었다...
고민의 방향이 어느 것을 쓸지가 아니라 어떻게 뷰를 나눌까였어야 했다.
기존에도 사용하던 방식을 뇌를 빼놓고 있었던 탓인지 다른 생각에 사로잡혀있었다.
이번 계기로 엉뚱하게 빠져버리는 일은 없었으면 좋겠다....
그냥 오늘은 일기 같은 그런 글이 되어버렸다 ㅎㅎㅎㅎㅎ