반응형

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 철학과 가장 잘 맞는 방식이다.
반응형
Posted by 까칠코더
,