반응형
SwiftUI Study – View 중첩이 깊어지지 않도록 컴포넌트를 분리하는 실전 패턴
1. 왜 중요한가 (문제 배경)
SwiftUI는 선언적 UI라서 한 파일에서 여러 UI 요소를 계속 쌓아 올리기 쉽습니다.
하지만 다음과 같은 상황이 발생하면 유지보수 난이도는 기하급수적으로 증가합니다.
- VStack → HStack → ZStack → if → switch → ForEach… 형태의 중첩 지옥
- body 내부 라인이 200줄 이상 길어지는 상황
- 작은 UI 변경도 전체 코드를 다시 읽어야 하는 문제
- 테스트 및 Preview 구성 불가
- 동일 UI 패턴이 여러 화면에 중복됨
문제의 핵심은 다음과 같습니다.
SwiftUI는 “작은 View 조각을 조합하는 구조”가 기본 철학이다.
하나의 View에 모든 내용을 몰아넣는 순간 SwiftUI의 장점을 잃는다.
2. 잘못된 패턴 예시
❌ 예시 1: 모든 UI를 한 View 안에서 구성하는 경우
struct WrongNestedView: View {
@State private var name = ""
@State private var email = ""
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("회원 가입")
.font(.largeTitle)
VStack {
Text("이름")
TextField("이름 입력", text: $name)
.textFieldStyle(.roundedBorder)
Text("이메일")
TextField("이메일 입력", text: $email)
.textFieldStyle(.roundedBorder)
}
HStack {
Button("취소") {}
Spacer()
Button("가입하기") {}
}
.padding(.top, 20)
// 여기에 또 조건문, ForEach 등이 붙으면 유지보수 지옥 시작
}
.padding()
}
}
문제점
- UI 구조를 한눈에 이해하기 어려움
- 사소한 변경도 body 전체를 뒤져야 함
- 테스트 및 Preview 단위 격리가 불가능함
- 화면이 복잡해질수록 중첩 구조가 무한히 깊어짐
3. 올바른 패턴 예시
✅ 예시 1: 의미 단위로 View를 분리
struct SignUpView: View {
@State private var name = ""
@State private var email = ""
var body: some View {
VStack(alignment: .leading, spacing: 16) {
HeaderView()
InputFieldsView(name: $name, email: $email)
ActionButtonsView()
}
.padding()
}
}
HeaderView
private struct HeaderView: View {
var body: some View {
Text("회원 가입")
.font(.largeTitle)
}
}
InputFieldsView
private struct InputFieldsView: View {
@Binding var name: String
@Binding var email: String
var body: some View {
VStack(alignment: .leading) {
Text("이름")
TextField("이름 입력", text: $name)
.textFieldStyle(.roundedBorder)
Text("이메일")
TextField("이메일 입력", text: $email)
.textFieldStyle(.roundedBorder)
}
}
}
ActionButtonsView
private struct ActionButtonsView: View {
var body: some View {
HStack {
Button("취소") {}
Spacer()
Button("가입하기") {}
}
.padding(.top, 20)
}
}
장점
- 각각의 UI 조각이 명확한 책임을 가진다
- SignUpView의 body는 매우 읽기 쉬워짐
- 각 컴포넌트만 따로 Preview 가능
- 중복을 제거하고 재사용 가능해짐
4. 실전 적용 팁
✔ 팁 1 – “UI 그룹 하나당 하나의 View” 원칙 사용
- Header
- Form Section
- Button Area
- Image + Text 묶음
UI 논리 그룹별로 View를 쪼개면 View는 절대 중첩이 깊어지지 않는다.
✔ 팁 2 – private extension으로 세부 컴포넌트 정리
하나의 파일 안에서도 다음처럼 정리할 수 있음:
extension SignUpView {
private struct Header: View { ... }
private struct FormFields: View { ... }
private struct Actions: View { ... }
}
✔ 팁 3 – Preview는 컴포넌트 레벨로 작성
View 전체보다 작은 단위로 Preview를 만들면 수정 속도가 크게 빨라진다.
✔ 팁 4 – TCA와 함께 사용할 때 View 분리가 구조를 깔끔하게 유지
Reducer 단위가 명확해지고 State 구조도 자연스럽게 작아진다.
✔ 팁 5 – 복잡한 UI는 “트리 구조로 종이 위에 먼저 그려보기”
이를 기반으로 파일/컴포넌트 분할을 결정하면 코드를 자동으로 최적화할 수 있다.
5. 정리
- SwiftUI는 작은 컴포넌트를 조합하는 방식이 가장 안정적이다.
- View 내부 중첩이 깊어질수록 유지보수 난이도는 기하급수적으로 상승한다.
- 의미 단위로 쪼개면 Preview, 테스트, 재사용성이 모두 향상된다.
- UI 구조가 복잡해질수록 View 분리는 필수이며 SwiftUI 철학과 가장 잘 맞는 방식이다.
반응형
'Dev Study > SwiftUI' 카테고리의 다른 글
| SwiftUI Study – 이미지 로딩·캐싱·리사이징을 효율적으로 처리하는 실전 패턴 (0) | 2025.12.09 |
|---|---|
| SwiftUI Study – ScrollView 안에서 LazyVStack을 사용해 성능을 최적화하는 방법 (0) | 2025.12.09 |
| SwiftUI Study – body 안에서 무거운 연산을 실행하지 않는 이유와 해결법 (0) | 2025.12.09 |
| SwiftUI Study – AnyView 남용을 피하고 타입 안정성을 유지하는 방법 (0) | 2025.12.09 |
| SwiftUI Study – offset / position으로 레이아웃을 잡지 말아야 하는 이유 (0) | 2025.12.09 |
| SwiftUI Study – GeometryReader에 의존하지 않고 레이아웃을 안정적으로 구성하는 방법 (0) | 2025.12.09 |
| SwiftUI Study – 상태 변경을 한 곳으로 모으는 패턴 (0) | 2025.12.09 |
| SwiftUI Study – FocusState를 안전하게 설계하는 방법 (0) | 2025.12.09 |

