반응형

SwiftUI Study – ViewBuilder 오용을 피하고 조건부 UI를 명확하게 설계하는 방법 (가독성·안정성·유지보수성 향상)

 

1. 왜 중요한가 (문제 배경)

SwiftUI의 @ViewBuilder는 매우 강력하지만, 다음과 같은 문제를 자주 일으킨다.

  • if/else 중첩으로 가독성이 심각하게 저하
  • ViewBuilder 내부에서 불필요한 중복 렌더링
  • 조건문에 따라 View 타입이 달라져 컴파일러 경고·오류 발생
  • 지나친 조건 분기 때문에 View가 비대해지고 유지보수가 어려움
  • 동일한 UI 블록이 뷰 곳곳에서 반복되어 구조적 중복 발생

문제의 핵심은 다음과 같다.

“ViewBuilder는 UI 구성 도구이지,

로직 표현 도구가 아니다.

UI 조건부 렌더링은 명확한 구조화가 필수다.”

ViewBuilder 오용은 규모가 커질수록 유지보수를 불가능하게 만든다.

 

2. 잘못된 패턴 예시

❌ 예시 1: ViewBuilder 내부에서 로직 분기 과도

@ViewBuilder
var content: some View {
    if isLoggedIn {
        if user.isAdmin {
            AdminView()
        } else {
            UserView()
        }
    } else {
        LoginView()
    }
}

문제점

- 조건이 늘어날수록 가독성 붕괴

- UI 구조 파악이 어려움

- 뷰 타입이 지저분하게 분기됨

❌ 예시 2: 동일한 UI가 조건부 반복됨

@ViewBuilder
var body: some View {
    if isLoading {
        ProgressView()
            .frame(maxWidth: .infinity)
            .padding()     // ❌ 중복
    } else {
        Text("완료")
            .frame(maxWidth: .infinity)
            .padding()     // ❌ 중복
    }
}

문제점

- 스타일 중복

- 변경 시 모든 조건문을 수정해야 함

❌ 예시 3: ViewBuilder에 비즈니스 로직이 섞임

@ViewBuilder
func build(for user: User?) -> some View {
    if let user = user {
        if user.age > 19 {     // ❌ 비즈니스 로직이 UI에 섞임
            AdultView()
        } else {
            TeenView()
        }
    } else {
        EmptyView()
    }
}

문제점

- UI 계층이 도메인 규칙을 알게 됨

- 재사용·테스트 불가

- 유지보수 곤란

 

3. 올바른 패턴 예시

✅ 예시 1: 조건 분기를 “명확한 상태”로 변환해 UI 단순화

enum ScreenState {
    case login, user, admin
}

struct HomeView: View {
    let state: ScreenState

    var body: some View {
        switch state {
        case .login: LoginView()
        case .user: UserView()
        case .admin: AdminView()
        }
    }
}

장점

- UI 조건이 한눈에 보임

- ViewBuilder 오염 없음

- 유지보수에 강함

✅ 예시 2: 공통 UI는 조건문 밖으로 추출

var body: some View {
    Group {
        if isLoading {
            ProgressView()
        } else {
            Text("완료")
        }
    }
    .frame(maxWidth: .infinity)
    .padding()
}

장점

- 스타일 중복 제거

- 유지보수 비용 감소

- 조건부 UI 구조가 간결해짐

✅ 예시 3: UI 분기는 ViewModel에서 “상태”로 제공

ViewModel:

enum ProfileState {
    case loading
    case loaded(Profile)
    case error(String)
}

@Published var state: ProfileState = .loading

View:

var body: some View {
    switch vm.state {
    case .loading:
        ProgressView()
    case .loaded(let profile):
        ProfileView(profile: profile)
    case .error(let message):
        ErrorView(message: message)
    }
}

장점

- View는 단순 “상태 → View 매핑”만 처리

- ViewBuilder 로직이 사라져 코드가 깔끔해짐

- 테스트 가능성 향상

✅ 예시 4: ViewBuilder는 UI 조합에만 집중하게 만들기

@ViewBuilder
func Placeholder(isVisible: Bool) -> some View {
    if isVisible {
        RoundedRectangle(cornerRadius: 8)
            .fill(Color.gray.opacity(0.2))
            .frame(height: 20)
    }
}

장점

- 간단한 UI 조건부 조합 기능에 적합

- 복잡한 로직 분기는 ViewModel로 이동

 

4. 실전 적용 팁

✔ 팁 1 – 조건 분기가 많으면 enum 상태로 통합

switch-case가 가장 안정적이고 가독성 좋음.

✔ 팁 2 – ViewBuilder는 UI 조합용일 뿐, 로직 처리용이 아님

조건이 복잡하면 ViewModel/UseCase로 이동.

✔ 팁 3 – 공통 스타일은 Group 블록으로 처리

조건문 안에서 modifier 중복 금지.

✔ 팁 4 – ViewBuilder는 가능한 얇게 유지

조건 2~3개 이상이면 구조적 문제를 의심.

✔ 팁 5 – Preview에서도 ViewBuilder 축약은 지양

명확한 View를 전달해야 디버깅이 쉬움.

 

5. 정리

  • ViewBuilder 오용은 UI 가독성·안정성을 해치는 주요 원인이다.
  • UI의 조건 분기는 “상태 모델링 → View 매핑”이 가장 바람직한 구조다.
  • 공통 UI는 조건문 밖으로 추출해 중복을 제거해야 한다.
  • ViewBuilder는 UI 조합 도구일 뿐, 로직 처리 도구가 아님을 명확히 해야 한다.
반응형
Posted by 까칠코더
,