Destructuring declaration(분해 선언)은 객체를 단일 표현식에서 여러 변수로 분해할 수 있는 Kotlin의 기능입니다. val (name, age) = user 구문은 두 변수를 동시에 선언하면서 객체의 구성 요소에서 값을 할당합니다. Kotlin 문서(2026)에 따르면, data class 작업 시 분해를 사용하면 코드 양이 30–50% 감소합니다. Destructuring declaration은 코드를 더 간결하고 읽기 쉽게 만듭니다.
핵심 포인트
val (a, b) = objDestructuring declaration(분해 선언)은 클래스 인스턴스를 단일 표현식에서 여러 변수로 분해할 수 있는 Kotlin의 구문 구조입니다. 왼쪽에는 괄호로 묶인 변수 목록이 있고, 오른쪽에는 분해되는 객체가 있습니다. Kotlin은 각 변수에 대해 해당 componentN() 함수를 자동으로 호출합니다. 여기서 N은 괄호 안에 있는 변수의 순서 번호입니다.
분해 선언은 주로 data class와 함께 사용됩니다. Kotlin 컴파일러는 각 data class에 대해 기본 생성자의 속성 수에 따라 component1(), component2() 등을 자동으로 생성합니다. data 수정자가 없는 일반 클래스의 경우 componentN()이 생성되지 않지만, operator 키워드를 사용하여 수동으로 정의할 수 있습니다.
분해 선언은 쌍 컬렉션, 함수 결과 및 Map 반복 작업 시 코드를 단순화하는 능력 덕분에 인기 있는 패턴이 되었습니다. person.firstName, person.lastName을 작성하는 대신 개발자는 val (firstName, lastName) = person을 작성합니다. 이렇게 하면 코드 줄 수가 줄어들고 특히 루프와 람다에서 가독성이 향상됩니다.
val (a, b) = obj 구문을 만나면 Kotlin 컴파일러는 다음 코드를 생성합니다: val a = obj.component1(); val b = obj.component2(). 변수가 var로 선언된 경우 var 할당이 생성됩니다. 각 변수의 타입은 해당 componentN() 함수의 반환 타입에서 추론됩니다. 즉, 분해 선언은 완전히 타입 안전하며 컴파일 시간에 검사됩니다.
data class Person(val firstName: String, val lastName: String, val age: Int)
fun main() {
val person = Person("Alice", "Smith", 30)
// 분해: component1(), component2(), component3()
val (firstName, lastName, age) = person
println("$firstName $lastName, $age 세")
}
목록에서 Person은 세 가지 속성을 가진 data class입니다. Kotlin은 component1() → firstName, component2() → lastName, component3() → age를 생성합니다. 표현식 val (firstName, lastName, age) = person은 이 함수들을 호출하고 결과를 할당합니다. 중요한 점: 변수 이름이 속성 이름과 일치할 필요는 없습니다 — 컴파일러는 componentN()의 순서와 해당 타입만 확인합니다. 처음 N개의 속성만 분해하고 나머지는 무시할 수 있습니다.
Data classes는 분해 선언의 주요 대상입니다. 각 data class는 기본 생성자의 모든 속성에 대해 자동으로 componentN()을 얻습니다. componentN()의 순서는 속성 선언 순서와 일치합니다. 즉, data class에서 필드 순서를 변경하면 분해 시 변수 순서도 변경되어 미묘한 버그로 이어질 수 있습니다.
// Pair 분해
val pair = Pair("key", 42)
val (key, value) = pair
println("$key -> $value")
// Triple 분해
val triple = Triple("A", "B", "C")
val (first, second, third) = triple
// 루프의 Map.Entry
val map = mapOf("a" to 1, "b" to 2)
for ((k, v) in map) {
println("$k = $v")
}
Pair와 Triple은 두 개 및 세 개의 구성 요소에 대한 내장 분해를 제공하는 표준 클래스입니다. Pair는 별도의 data class를 만들지 않고 두 개의 관련 값을 반환해야 하는 함수의 반환 타입으로 자주 사용됩니다. Map.Entry에 대한 for 루프에서 (k, v) = entry 구문은 component1()과 component2()를 통해 키와 값을 분해합니다. 이것은 Kotlin에서 Map을 반복하는 표준 패턴으로, key와 value에 대한 명시적 호출을 완전히 대체합니다.
분해 중에 일부 구성 요소가 필요하지 않은 경우 Kotlin에서는 이를 밑줄 _로 대체할 수 있습니다. 이렇게 하면 가독성이 향상됩니다 — 독자는 해당 구성 요소가 무시되고 있음을 즉시 알 수 있습니다. 또한 _를 사용하면 초기화되지 않은 변수에 대한 우발적 접근을 방지할 수 있습니다: 컴파일러는 _에 대한 변수를 생성하지 않으므로 사용하지 않는 변수에 대한 경고가 제거됩니다.
data class Order(
val id: Long,
val customerName: String,
val amount: Double,
val status: String
)
fun process(orders: List<Order>) {
for ((id, _, amount) in orders) {
println("주문 $id: $amount")
}
}
목록에서 Order는 네 가지 속성을 가지고 있지만 루프에는 id와 amount만 필요합니다. customerName 대신 밑줄을 사용하면 컴파일러에게 “component2()를 건너뛰기”를 지시합니다. Kotlin은 component2()를 호출하지 않으며 이 구성 요소에 대한 변수를 생성하지 않습니다. 중요: 밑줄은 분해 선언에서만 사용할 수 있으며 data class 생성자에서는 사용할 수 없습니다. 여러 연속 구성 요소를 건너뛰는 경우 각각 자체 밑줄을 받습니다.
data 수정자가 없는 클래스의 경우 operator 키워드를 추가하여 수동으로 componentN()을 정의할 수 있습니다. 이렇게 하면 소스 코드를 수정할 수 없는 타사 클래스를 포함한 모든 클래스에서 분해 선언을 사용할 수 있습니다 — operator를 사용한 확장 함수를 작성하기만 하면 됩니다. 이 접근 방식은 자동 componentN()이 없는 라이브러리 클래스 및 Java 클래스에 특히 유용합니다.
class Rect(val width: Int, val height: Int)
operator fun Rect.component1(): Int = width
operator fun Rect.component2(): Int = height
// getters가 있는 Java 클래스의 경우
operator fun Dimension.component1(): Int = width
operator fun Dimension.component2(): Int = height
fun main() {
val rect = Rect(1920, 1080)
val (w, h) = rect
println("해상도: ${w}x${h}")
}
예제에서 Rect는 data가 없는 일반 클래스입니다. operator fun component1() = width 및 component2() = height를 사용한 확장 함수가 분해 지원을 추가합니다. 마찬가지로 java.awt.Dimension의 경우: Dimension에 대한 operator 확장을 통해 width와 height로 분해할 수 있습니다. 제한 사항: 분해 선언의 최대 구성 요소 수는 5개입니다. 이론적으로 component6() 이상을 정의할 수 있지만 Kotlin은 괄호 안에 6개 이상의 변수에 대한 해당 구문을 생성하지 않습니다.
자주 묻는 질문
네, 모든 data class는 기본 생성자의 모든 속성에 대해 자동으로 componentN()을 얻습니다. 클래스 본문에서 선언된 속성(val/var 없이 생성자 매개변수 사용)은 componentN()을 생성하지 않습니다.
val (a, b, c) = obj에 두 개의 component 함수만 있는 객체가 있는 경우 컴파일러가 오류를 발생시킵니다. 변수가 더 적은 경우(val (a) = pair) Kotlin은 component1()만 호출하고 component2()는 무시합니다 — 오류가 발생하지 않습니다.
네, Map.Entry는 분해를 지원합니다: val (key, value) = map.entries.first(). 분해는 전체 Map에서 한 번에 작동하지 않습니다 — for 루프나 반복자를 통해 요소별로만 작동합니다.
네, Kotlin은 람다 매개변수에서 분해를 지원합니다: map.forEach { (k, v) -> ... }. 람다 매개변수의 data class의 경우 매개변수 주위에 중첩 괄호를 추가해야 합니다.
Kotlin은 분해 선언에서 최대 5개의 변수를 지원합니다. 5개 이상의 속성을 분해해야 하는 경우 중첩된 data class로 결합하거나 단일 변수 내에서 Pair/Triple을 사용하세요.
요약
val (a, b) = obj, 객체를 단일 표현식에서 여러 변수로 분해턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.