Result Builder — 개념, 구문 및 활용

저자: IT Sectr 게시일: 2026-06-20 읽는 시간: 8 분

Result Builder는 프로토콜 @resultBuilder를 통해 구현되는 Swift 속성으로, 일련의 표현식을 복합 값으로 변환합니다. 컴파일러는 제어 구문 if, for, switch가 포함된 코드 블록을 빌더의 정적 메서드 — buildBlock, buildEither, buildArray — 호출로 변환합니다. Swift Evolution 제안 SE-0289(2022)에 따르면, result builders를 사용하면 외부 파서 없이 Swift 내에서 선언적 DSL을 만들 수 있습니다. 가장 유명한 예는 SwiftUI의 @ViewBuilder로, 뷰 본문이 조건부 및 반복 요소로 선언적 스타일로 구성됩니다.

핵심 요점

  • Result Builder — 빌더의 정적 메서드를 통해 일련의 표현식을 결과 값으로 변환하는 Swift 속성
  • @ViewBuilder — 가장 유명한 예: 여러 뷰를 단일 복합 표현 TupleView로 변환합니다
  • 제어 구문 — 빌더는 if/else, switch, for-in을 메서드 buildOptional, buildEither, buildArray를 통해 지원합니다
  • 사용자 정의 빌더는 자신의 DSL(HTML, CSS, 구성, 쿼리)용으로 만들 수 있습니다
  • Swift 5.4는 result builders 지원을 함수와 함수 매개변수로 확장했습니다 — 이제 클로저 인수에 빌더를 적용할 수 있습니다

Result Builder란?

Result Builder(이전에는 function builders로 알려짐)는 줄 바꿈으로 구분된 일련의 표현식을 단일 값으로 변환할 수 있는 Swift 메커니즘입니다. 정적 변환 메서드를 구현하는 구조체에 적용되는 @resultBuilder 속성을 사용하여 선언됩니다.

Result builders 이전에는 SwiftUI body의 선언적 구문이 불가능했습니다. 뷰의 간결한 목록 대신 개발자가 수동으로 TupleView 호출을 작성해야 했습니다. Result Builder는 자동으로 각 표현식을 래핑하고, 분기와 루프를 지원하며, 개발자로부터 구성의 복잡성을 숨깁니다.

2022년에 승인된 Swift Evolution SE-0289에 따르면, result builder는 function builders(SE-0258, Swift 5.1) 아이디어의 진화입니다. 주요 변경 사항: @_functionBuilder에서 @resultBuilder로 이름 변경 및 함수 매개변수로의 확장으로, 뷰 본문뿐만 아니라 모든 클로저 인수에 빌더를 사용할 수 있게 되었습니다.

라이브러리 사용자에게 복잡한 구조(구성, 쿼리, UI 구성 요소)를 구축하기 위한 선언적 구문을 제공해야 할 때 Result builders를 사용하세요. 명령형 어셈블리 코드를 작성할 필요가 없습니다.

Result Builder의 작동 방식

Swift 컴파일러는 @resultBuilder로 표시된 각 코드 블록을 빌더의 정적 메서드 호출 시퀀스로 변환합니다. 문자열을 연결하는 간단한 빌더를 살펴보겠습니다:

swift
@resultBuilder
struct StringBuilder {
    static func buildBlock(_ parts: String...) -> String {
        parts.joined(separator: " ")
    }
}

이 빌더 사용 — 각 문자열이 별도 줄에서 공백으로 연결됩니다:

swift
@StringBuilder
func greeting() -> String {
    "Hello"
    "World"
    "from"
    "Swift"
}
// 컴파일러가 이를 변환합니다:
// StringBuilder.buildBlock("Hello", "World", "from", "Swift")
// 결과: "Hello World from Swift"

컴파일러는 연속된 표현식을 그룹화하고 가변 매개변수로 buildBlock에 전달합니다. 표현식 사이에 if가 나타나면 컴파일러는 분기를 위해 buildOptional 또는 buildEither를 호출합니다. for-in 루프의 경우 buildArray를 호출합니다. 이렇게 일반 Swift 코드가 최종 값을 구성하는 호출 체인으로 변환됩니다.

빌더 메서드: buildBlock, buildOptional, buildEither

각 result builder는 컴파일러가 변환 중에 호출하는 정적 메서드 집합을 정의합니다. 주요 메서드:

메서드목적호출 시점
buildBlock일련의 표현식을 결합합니다분기가 없는 각 블록에 대해
buildOptionalelse 없는 if를 처리합니다ifelse 없이 있을 때
buildEither(first:)if-else의 첫 번째 분기ifelse가 있을 때
buildEither(second:)if-else의 두 번째 분기ifelse가 있을 때
buildArrayfor-in 루프를 처리합니다for-in이 있을 때
buildExpression개별 표현식을 변환합니다각 표현식을 buildBlock에 전달하기 전
buildFinalResult최종 변환클로저에서 반환하기 전

최소 구현에는 가변 매개변수가 있는 buildBlock만 필요합니다 — 분기가 없는 블록에 충분합니다. buildOptionalbuildEither를 추가하면 조건부 구문 지원이 포함되고, buildArray는 루프를 지원합니다. Swift 문서(2025)에 따르면, 최대 DSL 유연성을 위해 모든 메서드를 구현하는 것이 좋습니다.

buildExpression은 다른 유형의 표현식을 받아 단일 빌더 유형으로 변환할 수 있습니다. 예를 들어, @ViewBuilder에서 buildExpressionText, Image, Button을 받아 공통 View 유형으로 변환합니다.

사용자 정의 Result Builder 만들기

HTML 문자열을 구성하는 빌더 생성을 살펴보겠습니다. 이 DSL을 사용하면 Swift에서 직접 선언적으로 HTML을 작성할 수 있습니다:

swift
@resultBuilder
enum HTMLBuilder {
    static func buildBlock(_ components: String...) -> String {
        components.joined()
    }

    static func buildOptional(_ component: String?) -> String {
        component ?? ""
    }

    static func buildEither(first component: String) -> String {
        component
    }

    static func buildEither(second component: String) -> String {
        component
    }

    static func buildArray(_ components: [String]) -> String {
        components.joined()
    }
}

사용자 정의 빌더로 HTML 생성:

swift
func div(@HTMLBuilder _ content: () -> String) -> String {
    "<div>\(content())</div>"
}

func p(_ text: String) -> String {
    "<p>\(text)</p>"
}

let page = div {
    p("Hello")
    p("World")

    if showFooter {
        p("바닥글")
    }
}
// 결과: <div><p>Hello</p><p>World</p><p>Footer</p></div>

Swift.org(2025)의 기사 “Building Custom Result Builders in Swift”에 따르면, 사용자 정의 빌더는 구성 파일, UI 구성 요소, 데이터 매핑 및 데이터베이스 쿼리 구축을 위한 라이브러리에서 사용됩니다 — 분기 지원이 있는 선언적 구문이 필요한 모든 곳에서.

SwiftUI의 @ViewBuilder

@ViewBuilder는 SwiftUI에 내장된 result builder로, 대부분의 컨테이너 뷰의 content 매개변수에 적용됩니다: VStack, HStack, ZStack, Group, Listbody 속성 자체. 쉼표나 래퍼 없이 여러 뷰를 별도 줄에 작성할 수 있습니다.

@ViewBuilder는 if-else, switch, for-in 지원을 포함한 모든 result builder 메서드를 구현합니다. 조건이 참이면 buildEither(first:)가 하나의 뷰를 반환하고, 거짓이면 buildEither(second:)가 다른 뷰를 반환합니다. 두 분기 모두 동일한 유형을 반환해야 하지만, SwiftUI는 내부적으로 AnyView 또는 ConditionalContent를 통한 유형 소거를 사용합니다.

swift
struct GreetingView: View {
    let isLoggedIn: Bool

    var body: some View {
        VStack {
            Image(systemName: "person.circle")
            Text("프로필")
                .font(.title)

            if isLoggedIn {
                Text("환영합니다!")
                    .foregroundColor(.green)
            } else {
                Button("로그인") { }
            }
        }
    }
}

@ViewBuilder가 없으면 동일한 코드에 각 조건부 섹션에 Group이 필요하거나 AnyView를 사용해야 하며, 이는 성능을 저하시킵니다. @ViewBuilder는 각 조합에 대해 가장 효율적인 표현 — ConditionalContent 또는 TupleView — 을 자동으로 선택합니다.

Result Builder의 제한 사항

첫 번째 제한은 buildBlock 내 최대 표현식 수입니다. Swift 표준 라이브러리는 2~10개의 표현식에 대한 buildBlock 오버로드를 정의합니다. 블록에 10개 이상의 표현식이 있으면 컴파일러가 오류를 생성합니다. 해결책은 Group 또는 VStack을 통한 그룹화로 하위 블록으로 나누는 것입니다.

두 번째 제한은 빌더 블록 내에서 변수 및 할당 지원 부족입니다. @ViewBuilder 내에서 let x = 5를 선언할 수 없습니다. 모든 표현식은 빌더 유형의 값을 반환하는 표현식이어야 합니다. 중간 계산의 경우 빌더 외부에서 계산하거나 다른 유형을 지원하는 buildExpression을 사용하세요.

세 번째 제한은 디버깅 복잡성입니다. Result builder 내의 컴파일 오류는 특히 if/else 분기에서 유형이 일치하지 않을 때 혼란스러운 메시지를 생성하는 경우가 많습니다. 디버깅에는 명시적 반환 유형과 AnyView를 사용하세요. 후자는 성능을 저하시킬 수 있습니다. Hacking with Swift(2025)에 따르면, 실용적인 팁은 분기 없는 간단한 빌더로 시작하여 점차 조건부 구문 지원을 추가하는 것입니다.

자주 묻는 질문

Swift에서 Result Builder란?

Result Builder는 정적 메서드를 통해 일련의 표현식을 결과 값으로 변환하는 Swift 속성입니다. 선언적 DSL을 만들 수 있으며, 가장 유명한 예는 명령형 코드 없이 뷰 계층 구조를 구축하기 위한 SwiftUI의 @ViewBuilder입니다.

자체 Result Builder를 어떻게 만드나요?

@resultBuilder 속성이 있는 구조체를 선언하고 최소한 buildBlock 메서드를 구현합니다. 조건을 지원하려면 buildOptionalbuildEither를 추가하고, 루프에는 buildArray를 추가합니다. 함수의 클로저 매개변수 앞에 빌더 속성을 사용합니다.

Result Builder에 필수 메서드는?

buildBlock만 필수입니다. 다른 모든 메서드 — buildOptional, buildEither, buildArray, buildExpression, buildFinalResult — 는 선택 사항이며 해당 구문에 대한 지원을 추가합니다. 더 많은 메서드가 구현될수록 DSL이 더 유연해집니다.

@ViewBuilder가 Result Builder인 이유는?

@ViewBuilder는 View 프로토콜에 대한 result builder의 구체적인 구현입니다. SwiftUI에서 @resultBuilder 속성이 있는 구조체로 정의되며, 다양한 수의 뷰에 대한 buildBlock 메서드(TupleView), ConditionalContent에 대한 buildEither 및 ForEach에 대한 buildArray를 제공합니다.

빌더 내 표현식 수에 제한이 있나요?

, 표준 buildBlock 오버로드는 최대 10개의 표현식을 지원합니다. 이를 초과하는 경우 중첩 컨테이너(Group, VStack)를 사용하여 하위 블록으로 나눕니다. 사용자 정의 빌더는 제한 없이 가변 buildBlock을 정의할 수 있습니다.

요약

  • Result Builder — 빌더의 정적 메서드를 통해 일련의 표현식을 단일 값으로 변환하는 Swift 속성
  • 컴파일러는 제어 구문에 따라 코드 블록을 buildBlock, buildEither, buildOptional, buildArray 호출로 대체합니다
  • @ViewBuilder — 조건과 루프를 지원하는 선언적 뷰 계층 코드를 작성할 수 있는 SwiftUI의 내장 result builder
  • 사용자 정의 빌더는 DSL(HTML, 구성, 쿼리) 구축에 사용됩니다 — 선언적 구문의 이점을 얻는 모든 구조
  • 제한 사항: buildBlock 내 최대 10개 표현식, 변수 선언 불가, 유형 불일치 시 혼란스러운 컴파일 오류
  • Swift 5.4+ — 빌더를 함수 매개변수에 적용할 수 있어 뷰 본문을 넘어 사용 사례 확장

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기