iOS, Swift/SwiftUI

SwiftUI body에 사용할 수 없는 것과 대안들 : 변수선언, 조건문, 반복문

Developer88 2025. 3. 22. 16:55
반응형

오늘은 SwiftUI body에 사용할 수 없는 것들에 대해 정리하겠습니다.

 

1. SwiftUI body에 사용할 수 없는 것들

SwiftUI는 명령형이 아닌,

선언형 프로그래밍 방식을 따르는데요.

SwiftUI View의 body에서는,

UI의 상태와 구조만을 '선언'하도록 하고 있습니다.

 

그래서, body안에 사용할 수 없는 것들이 있는데요.

이에 대해 명확히 알아 둘 필요가 있습니다.

 

SwiftUI body에 사용할 수 없는 것들은 다음과 같습니다.

  • 변수 선언 (var 키워드)
  • 명령형 조건문 (if, switch 등을 값 할당에 사용할 때)
  • 명령형 반복문 (for, while 등)

예를 들어,
아래와 같이 코드를 작성하면,
컴파일 에러가 발생합니다. 

 

struct ContentView: View {
    var body: some View {
        VStack {
            // 오류: View의 body 내에서 직접 변수 선언 불가
            var message = "안녕하세요"
            
            // 오류: View의 body 내에서 직접 조건문 사용 불가
            if true {
                message = "반갑습니다"
            }
            
            Text(message)
        }
    }
}

 

 

Swift의 for-in 루프도 사용불가능합니다.

 

struct ContentView: View {
    let items = ["항목 1", "항목 2", "항목 3"]
    
    var body: some View {
        VStack {
            // 오류: 명령형 for 루프 사용 불가
            for item in items {
                Text(item)
            }
        }
    }
}

 

그럼 SwiftUI사용하기 너무 어려워 지는 것 아닐까요?

대안들이 있는데요.

 

지금부터는 SwiftUI body에 사용할 수 있는 것들을 알아보겠습니다.

 

2. let 키워드로 지역 상수 사용하기

body 내에서 let 키워드를 사용하여,

아래와 같이 지역 상수를 선언할 수도 있습니다. 

 

옵셔널에 대한 언래핑이나,

비교를 통한 계산된 값 등을,

let 키워드를 이용한 지역상수에 저장해 사용할 수 있습니다.

 

struct BusDetailView: View {
    let bus: BusModel
    
    var body: some View {
        VStack {
            // 옵셔널 언래핑과 기본값 설정
            let routeType = Int(bus.routeType) ?? NullDataConstants.NULL_ROUTE_TYPE
                        
            // 계산된 값 저장
            let isExpress = routeType == BusTypes.EXPRESS
            
            // 배열에서 인덱스로 값 가져오기
            let statusText = ["운행중", "차고지 대기", "운행 종료"][bus.status]
            
            // 여러 조건을 결합한 표현식
            let statusColor = isExpress && bus.status == 0 ? Color.green : Color.gray
            
            Text(busNumber)
                .foregroundColor(statusColor)
        }
    }
}

 

3. 삼항 연산자 사용

body내에서 아래와 같이 삼항 연산자를 사용할 수 있고요.

 

var body: some View {
    VStack {
        let busNumber = bus.busNumber.isEmpty ? "알 수 없음" : bus.busNumber
        ...
    }
}

 

아래와 같이 사용할수도 있습니다.

 

struct ContentView: View {
    var shouldShowGreeting = true
    
    var body: some View {
        VStack {
            Text(shouldShowGreeting ? "안녕하세요 반갑습니다" : "안녕하세요")
        }
    }
}

 

 

3. ForEach

위에서 Swift의 for in문을 사용할 수 없다는 것을 보았는데요.

그럼 어떤 대안이 있을까요?

 

바로 ForEach인데요.

ForEach는 그 자체로 View이므로 다른 View 내에 포함될 수 있습니다.

 

아래와 같이 사용해 주면 됩니다.

 

struct ContentView: View {
    let items = ["항목 1", "항목 2", "항목 3"]
    
    var body: some View {
        VStack {
            ForEach(items, id: \.self) { item in
                Text(item)
            }
        }
    }
}

 

 

좀 더 복잡한 경우를 보면 다음과 같습니다.

 

struct TodoItem: Identifiable {
    let id = UUID()
    let title: String
    var isCompleted: Bool
}

struct TodoListView: View {
    let todos = [
        TodoItem(title: "우유 사기", isCompleted: false),
        TodoItem(title: "이메일 확인하기", isCompleted: true),
        TodoItem(title: "운동하기", isCompleted: false)
    ]
    
    var body: some View {
        List {
            // Identifiable 프로토콜을 준수하는 객체는 id 파라미터 생략 가능
            ForEach(todos) { todo in
                HStack {
                    Image(systemName: todo.isCompleted ? "checkmark.circle.fill" : "circle")
                    Text(todo.title)
                        .strikethrough(todo.isCompleted)
                }
            }
        }
    }
}

 

4. if-else 구문을 View 선택에 사용하기

View를 리턴해 준다면,

if-else문도 사용할 수 있습니다.

 

struct ContentView: View {
    var shouldShowGreeting = true
    
    var body: some View {
        VStack {
            // if-else를 사용해 다른 View 반환
            if shouldShowGreeting {
                Text("안녕하세요 반갑습니다")
            } else {
                Text("안녕하세요")
            }
        }
    }
}

 

 

5. Switch문 사용하기

view를 리턴해주기만 한다면,

switch문도 사용할 수 있습니다.

 

struct ContentView: View {
    enum WeatherType {
        case sunny, rainy, cloudy, snowy
    }
    var currentWeather: WeatherType = .sunny
    
    var body: some View {
        VStack {
            switch currentWeather {
            case .sunny:
                Text("맑은 날씨")
                    .foregroundColor(.yellow)
                    .background(Image("sun"))
            case .rainy:
                Text("비")
                    .foregroundColor(.blue)
                    .background(Image("rain"))
            case .cloudy:
                Text("구름")
                    .foregroundColor(.gray)
                    .background(Image("cloud"))
            case .snowy:
                Text("눈")
                    .foregroundColor(.white)
                    .background(Image("snow"))
            }
        }
    }
}

 

viewBuilder를 사용하여 아래와 같이 분리할수도 있습니다.

 

struct ContentView: View {
    enum WeatherType {
        case sunny, rainy, cloudy, snowy
    }
    
    var currentWeather: WeatherType = .sunny
    
    var body: some View {
        VStack {
            weatherView
            Text("오늘의 날씨 정보였습니다")
        }
    }
    
    @ViewBuilder
    private var weatherView: some View {
        switch currentWeather {
        case .sunny:
            Text("맑은 날씨").foregroundColor(.yellow)
        case .rainy:
            Text("비").foregroundColor(.blue)
        case .cloudy:
            Text("구름").foregroundColor(.gray)
        case .snowy:
            Text("눈").foregroundColor(.white)
        }
    }
}

 

6. body에서 함수 사용하기

SwiftUI의 body에서 한가지 더 할 수 있는 것이 있는데요.

함수를 선언하거나 사용하는 것 입니다.

 

6-1. 함수 호출하기

아래와 같이,

body외부에 정의한 함수를,

사용할 수 있습니다.

 

struct ContentView: View {
    @State private var counter = 0
    
    var body: some View {
        VStack {
            Text("카운터: \(counter)")
            Button("증가") {
                incrementCounter()
            }
        }
    }
    
    // View 내에 함수 정의
    func incrementCounter() {
        counter += 1
    }
}

 

 

 

하나 더 볼까요?

아래에서는 이름을 합쳐서 보여주는 함수와,

나이를 계산하는 함수를 외부에 정의해 사용하고 있습니다.

 

struct ProfileView: View {
    let user: User
    
    var body: some View {
        VStack {
            Text(formatName())
            Text("나이: \(calculateAge())")
        }
    }
    
    func formatName() -> String {
        return "\(user.lastName) \(user.firstName)"
    }
    
    func calculateAge() -> Int {
        // 생년월일로부터 나이 계산 로직
        let calendar = Calendar.current
        let ageComponents = calendar.dateComponents([.year], from: user.birthDate, to: Date())
        return ageComponents.year ?? 0
    }
}

 

 

View외부에서 정의한 함수도 호출할 수 있습니다.

 

func formatCurrency(_ value: Double) -> String {
    let formatter = NumberFormatter()
    formatter.numberStyle = .currency
    formatter.locale = Locale.current
    return formatter.string(from: NSNumber(value: value)) ?? "0"
}

struct PriceView: View {
    let price: Double
    
    var body: some View {
        Text("가격: \(formatCurrency(price))")  // 외부 함수 호출
    }
}

 

3-2. 조건부 뷰 생성을 위한 함수 사용하기

조건에 따라서 View를 생성해야하는 경우,

if-else문을 사용하는 함수를 ViewBuilder로 만들어,

사용할 수 있습니다.

 

struct WeatherView: View {
    let temperature: Double
    
    var body: some View {
        VStack {
            Text("\(Int(temperature))°C")
            getWeatherIcon()  // 함수 호출하여 뷰 생성
        }
    }
    
    // 조건에 따라 다른 View를 반환하는 함수
    @ViewBuilder func getWeatherIcon() -> some View {
        if temperature > 30 {
            Image(systemName: "sun.max.fill")
                .foregroundColor(.orange)
        } else if temperature > 20 {
            Image(systemName: "sun.min.fill")
                .foregroundColor(.yellow)
        } else if temperature > 10 {
            Image(systemName: "cloud.sun.fill")
                .foregroundColor(.gray)
        } else {
            Image(systemName: "cloud.fill")
                .foregroundColor(.gray)
        }
    }
}

 

이상으로 SwiftUI body에 사용할 수 없는 것들과,

그 대안들을 살펴보았습니다.

728x90