Builder — 모바일 개발에서 빌더 패턴의 기초

저자: IT Sectr 게시일: 2026-02-17 읽는 시간: 7 분

Builder — 복잡한 객체를 단계별로 생성할 수 있는 생성 패턴입니다. 수십 개의 매개변수를 가진 생성자와 달리 Builder는 호출 체인을 통해 객체를 조립하며, 각 호출이 하나의 필드를 구성합니다. 이 패턴은 네트워크 클라이언트 구성, 데이터베이스 설정, 알림 및 탐색 빌더 등 많은 선택적 매개변수가 있는 객체에 특히 유용합니다. 자세한 내용은 Refactoring Guru: Builder를 참조하세요.

핵심 포인트

  • Builder — 프로세스와 결과를 분리한 단계별 객체 생성
  • Fluent interface — 편리한 구성을 위한 set()/with() 호출 체인
  • 불변성 — Builder는 setter가 필요 없는 완성된 객체를 생성
  • 하위 호환성 — Builder에 새 필드를 추가해도 클라이언트가 깨지지 않음
  • Kotlin DSL vs Builder — Kotlin은 대안으로 type-safe builders 제공

Builder란 무엇인가: 빌더 패턴의 본질

Builder — 복잡한 객체의 생성을 그 표현에서 분리하는 GoF 생성 패턴입니다. 동일한 생성 프로세스가 다른 표현을 만들 수 있습니다. Builder는 객체에 많은 선택적 매개변수가 있고 10개의 필드를 가진 생성자가 읽기 어렵고 유연성이 떨어질 때 유용합니다. 이 패턴은 생성자 오버로드 수가 기하급수적으로 증가하는 Telescoping Constructor 안티패턴도 해결합니다.

Builder 구조는 메인 클래스의 필드를 미러링하는 필드를 가진 내부 정적 Builder 클래스를 포함합니다. 각 set-메서드는 fluent chaining을 위해 Builder(this)를 반환합니다. 최종 build() 메서드는 필드 값을 프라이빗 생성자에 전달하여 대상 객체를 생성합니다. 메인 클래스에는 Builder를 받는 프라이빗 생성자가 있습니다. 클라이언트: Object.builder().setField1(val1).setField2(val2).build().

Builder 사용 시기 — 5+ 필드가 있는 객체 중 필수 필드가 2-3개만 있는 경우. 구성 객체(RequestConfig, DatabaseConfig). 생성 시 복잡한 유효성 검사 로직이 있는 객체. 생성 후 불변(immutable)해야 하는 객체. Android에서 Builder는 SDK에서 적극적으로 사용됩니다: AlertDialog.Builder, Retrofit.Builder, OkHttpClient.Builder, NotificationCompat.Builder.

Kotlin의 Builder: 클래식 및 DSL 구현

Kotlin Builder에는 두 가지 접근 방식이 있습니다: 클래식 Java 스타일 Builder(중첩 클래스 사용)와 Kotlin 스타일 DSL builder(리시버가 있는 람다 사용). Java 스타일 Builder는 Android 호환성 및 Java 코드와 함께 사용할 때 선호됩니다. DSL builder는 Kotlin의 관용적인 방식입니다: 함수가 람다를 받아들이고, 그 내부에서 this가 필드를 직접 할당할 수 있는 Builder 컨텍스트가 됩니다.

kotlin
// 클래식 Builder
data class HttpConfig private constructor(
    val baseUrl: String,
    val timeout: Long = 30_000,
    val retries: Int = 3,
    val headers: Map<String, String> = emptyMap()
) {
    class Builder {
        private var baseUrl: String = ""
        private var timeout: Long = 30_000
        private var retries: Int = 3
        private var headers: MutableMap<String, String> = mutableMapOf()

        fun baseUrl(url: String) = apply { this.baseUrl = url }
        fun timeout(ms: Long) = apply { this.timeout = ms }
        fun retries(n: Int) = apply { this.retries = n }
        fun header(key: String, value: String) = apply { headers[key] = value }

        fun build(): HttpConfig {
            require(baseUrl.isNotBlank()) { "baseUrl is required" }
            return HttpConfig(baseUrl, timeout, retries, headers)
        }
    }
}

// 사용법
val config = HttpConfig.Builder()
    .baseUrl("https://api.example.com")
    .timeout(15_000)
    .header("Authorization", "Bearer token")
    .build()

Kotlin DSL builder — 중첩 클래스가 없는 대안입니다. 빌더 함수는 빌더 객체의 컨텍스트에서 람다를 받아들입니다. 이것은 Kotlin에 관용적이며 write-필드가 필요하지 않습니다. DSL builders는 Ktor Client, kotlinx.serialization, Compose(Modifier)에서 적극적으로 사용됩니다. DSL builder는 Java와 호환되지 않으며 Java API가 있는 라이브러리에 적합하지 않습니다.

Swift의 Builder: result builders 및 체인

Swift Builder — Swift에는 내장 Builder 패턴이 없지만, Self를 반환하는 메서드를 통해 fluent interface를 쉽게 구현할 수 있습니다. 각 메서드가 속성을 구성하고 self를 반환합니다. Kotlin과 달리 Swift는 별도의 Builder 클래스가 필요하지 않습니다 — 조립 중에 객체가 mutable이면 객체 자체를 반환할 수 있습니다. 불변 객체의 경우 Kotlin과 유사하게 중첩된 Builder 클래스가 사용됩니다.

swift
struct NetworkRequest {
    let url: String
    let method: HTTPMethod
    let headers: [String: String]
    let body: Data?
    let timeout: TimeInterval

    final class Builder {
        private var url: String = ""
        private var method: HTTPMethod = .get
        private var headers: [String: String] = [:]
        private var body: Data? = nil
        private var timeout: TimeInterval = 30

        func withURL(_: String) -> Self { /* self */ }
        func withMethod(_: HTTPMethod) -> Self { /* self */ }
        func withHeader(key: String, value: String) -> Self { /* self */ }
        func withBody(_: Data) -> Self { /* self */ }
        func withTimeout(_: TimeInterval) -> Self { /* self */ }

        func build() throws -> NetworkRequest {
            guard !url.isEmpty else { throw BuilderError.missingURL }
            return NetworkRequest(
                url: url, method: method, headers: headers,
                body: body, timeout: timeout
            )
        }
    }
}

Result Builders — Swift 5.4에서 @resultBuilder가 도입되었습니다 — 선언적 구조 생성을 위한 언어 메커니즘입니다. SwiftUI, AttributedString, SceneBuilder가 result builders를 사용합니다. 이것은 클래식 Builder의 대안입니다: set-메서드 체인 대신, result builder는 컴파일러가 배열이나 트리로 조립하는 요소가 있는 코드 블록을 사용합니다. SwiftUI의 @ViewBuilder가 가장 유명한 예입니다: body 내에서 if, switch, ForEach를 작성할 수 있고, 컴파일러가 조건에서 View를 만듭니다.

Builder vs Telescoping Constructor: 접근 방식 비교

Telescoping Constructor — 클래스에 다양한 매개변수 세트를 가진 여러 오버로드된 생성자가 있는 안티패턴입니다. 예를 들어, 세 개의 생성자: HttpConfig(url), HttpConfig(url, timeout), HttpConfig(url, timeout, retries). 매개변수가 증가함에 따라 생성자 수가 기하급수적으로 증가합니다 — n개의 선택적 필드에 n!개의 조합이 필요합니다. Builder는 필요한 필드만 지정할 수 있게 하여 이 문제를 해결합니다.

특성Telescoping ConstructorBuilderKotlin named args
코드 양기하급수적 증가선형 증가최소
가독성낮음(어떤 매개변수가 무엇인가?)높음(메서드 + 이름)높음(이름 = 값)
불변성불변불변불변
Java 호환성완전완전없음(Kotlin만)
유효성 검사각 생성자에서build()에서 — 한 번init()에서

Kotlin named arguments + 기본값 — 순수 Kotlin 프로젝트에서 Builder의 우아한 대안입니다. 생성자 매개변수에 기본값이 있으며, 클라이언트는 필요한 것만 전달합니다: HttpConfig(baseUrl = url, timeout = 15_000). 단점은 컴파일 타임에 필수 필드를 검증할 수 없다는 것입니다. Builder는 Builder 생성자를 통해 필수 필드를 제공합니다(baseUrl은 필수). Java 라이브러리의 경우 Builder가 사실상의 표준으로 남아 있습니다.

Android SDK의 Builder: AlertDialog, Retrofit, OkHttp

Android SDK의 Builder — 표준 라이브러리에서 가장 일반적인 패턴 중 하나입니다. AlertDialog.Builder: new AlertDialog.Builder(context).setTitle().setMessage().setPositiveButton().create(). Retrofit.Builder: new Retrofit.Builder().baseUrl().addConverterFactory().build(). OkHttpClient.Builder: new OkHttpClient.Builder().connectTimeout().addInterceptor().build(). NotificationCompat.Builder: setContentTitle().setContentText().setSmallIcon().build().

Google이 Builder를 사용하는 이유 — 하위 호환성입니다. Builder에 새 메서드를 추가해도 기존 코드가 깨지지 않습니다. Google이 20개 매개변수의 생성자를 사용했다면 각 새 필드마다 새 오버로드가 필요했을 것입니다. Builder는 breaking changes 없이 수년간 set-메서드를 추가할 수 있게 합니다. 예를 들어, NotificationCompat.Builder는 Android 11에서 setBubbleMetadata()를 추가했으며 기존 코드에 영향을 주지 않았습니다.

Kotlin 라이브러리의 Builder — Ktor(HttpClientBuilder), Coil(ImageRequest.Builder), Room(Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build()), Navigation(NavOptionsBuilder). Kotlin 프로젝트에서 Builder는 종종 DSL과 결합됩니다: Room.databaseBuilder(context, AppDatabase.class, "db").fallbackToDestructiveMigration().build(). 이 패턴은 하위 호환성과 Java 상호 운용성이 중요한 공개 API에서 계속 관련성이 있습니다.

자주 묻는 질문

Builder가 과잉인 경우는?

Builder는 1-3개 필드의 객체에는 과잉입니다 — 일반 생성자나 data class가 더 명확합니다. Java 상호 운용성이 없는 Kotlin 프로젝트에서도 과잉이며, named arguments + 기본값이 동일한 작업을 더 간단하게 해결합니다. Builder는 5+ 필드, 복잡한 검증 또는 named arguments를 사용할 수 없는 Java API에 정당화됩니다.

Builder와 Factory의 차이점은?

Builder는 하나의 복잡한 객체를 단계별로 생성하고(필드 구성), Factory는 유형이나 매개변수에 따라 전체 객체를 생성합니다. Builder는 «어떻게 조립할까?»라는 질문에 답하고, Factory는 «무엇을 만들까?»에 답합니다. Builder는 종종 Factory와 결합됩니다: Factory가 유형을 선택하고 Builder가 필드를 구성합니다.

SwiftUI에 Builder가 필요한가요?

SwiftUI에서 Builder의 역할은 result builders(@ViewBuilder, @SceneBuilder)와 View 수정자(.font(), .padding())가 수행합니다. SwiftUI는 선언적 접근 방식과 fluent modifiers를 사용하므로 클래식 Builder가 필요하지 않습니다. UIKit 구성 요소의 경우 Builder가 유용합니다: UIAlertController, URLRequest, NSAttributedString.

Builder를 스레드 안전하게 만드는 방법은?

Builder는 일반적으로 단일 스레드에서 객체를 조립하는 데 사용되므로 스레드 안전성이 필요하지 않습니다. Builder가 멀티스레드 환경(드문 경우)에서 사용되는 경우 각 set-메서드와 build()를 동기화하세요. 대안 — Immutable Builder: 각 set-메서드가 수정된 필드로 새 Builder 인스턴스를 반환합니다.

Retrofit은 왜 DI 대신 Builder를 사용하나요?

Retrofit.Builder는 DI 컨테이너 없이 작동해야 하는 공개 라이브러리 API입니다. Builder는 Dagger 또는 다른 DI 프레임워크에 대한 의존성 없이 구성 유연성(baseUrl, 변환기, 인터셉터, 사용자 정의 콜 어댑터)을 제공합니다. 애플리케이션 내에서 DI는 Builder를 통해 Retrofit을 한 번 생성할 수 있지만, Builder 자체는 Retrofit 공개 API의 일부로 남아 있습니다.

요약

  • Builder — fluent interface를 통한 단계별 객체 생성
  • Kotlin Builder — 클래식(중첩 클래스) 및 DSL(리시버가 있는 람다)
  • Swift Builder — 선언적 코드를 위한 중첩 클래스 또는 @resultBuilder
  • 불변성 — Builder는 프라이빗 생성자를 통해 불변 객체 생성
  • Android SDK — AlertDialog, Retrofit, OkHttp, NotificationCompat — 업계 표준
  • 하위 호환성 — Builder에 필드 추가해도 기존 코드 깨지지 않음
  • Kotlin 대안 — named arguments + 기본값이 순수 Kotlin 프로젝트에 더 간단

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

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

프로젝트 논의

더 읽어보기