Kotlin의 Extension property — 정의, 구문 및 사용법

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

extension property — 상속 없이 소스 코드를 변경하지 않고 기존 클래스에 새 속성을 추가할 수 있는 Kotlin 메커니즘입니다. extension functions과 달리 extension properties는 상태를 저장할 수 없습니다 — backing field가 없기 때문에 getter만, 또는 선택적으로 setter와 함께 선언됩니다. Kotlin Documentation, 2025에 따르면 extension properties는 receiver를 첫 번째 매개변수로 하는 정적 getter 및 setter 메서드로 컴파일됩니다.

핵심 포인트

  • Extension property — receiver type을 가진 속성으로, Kotlin 속성 구문을 통해 접근 가능
  • backing field 없음 — extension property는 상태를 저장할 수 없고 계산만 가능
  • Getter 필수, setter 선택 사항 — 일반 extension functions처럼 선언
  • Nullable 타입 지원: receiver는 내부 검사와 함께 nullable 가능
  • 가변 extension property — getter와 setter가 있는 var로 선언된 경우에만

Kotlin의 extension property란?

Extension property는 선언을 변경하지 않고 기존 타입에 속성을 추가하는 Kotlin의 구문 구조입니다. 속성은 receiver type으로 선언되며 getter를 포함해야 합니다. 일반 속성과의 주요 차이점은 backing field가 없다는 것입니다: extension property는 데이터를 저장할 수 없고 receiver 객체를 기반으로 계산만 수행합니다.

Kotlin Foundation Survey (2024)에 따르면 extension properties는 extension functions보다 덜 인기가 있으며 약 45%의 개발자가 정기적으로 사용합니다. 이는 상태를 가질 수 없는 제한 때문이며 적용 범위를 좁힙니다. 그럼에도 불구하고 타입에 논리적으로 연결된 계산 속성의 경우 extension properties가 가장 간결한 옵션입니다.

Extension properties는 정적 getter 및 setter 메서드 쌍으로 컴파일됩니다. 바이트코드 수준에서는 extension property 호출과 extension function 호출 간에 차이가 없습니다 — 둘 다 receiver 매개변수가 있는 정적 메서드가 됩니다. JetBrains(Kotlin Docs, 2025)에 따르면 오버헤드가 전혀 없습니다.

메서드 호출이 아닌 속성처럼 보여야 하는 짧은 계산 값에는 extension properties를 사용하세요 — 코드 가독성이 향상되고 Uniform Access Principle을 따릅니다.

Extension property 구문: val과 var

Extension property를 선언하려면 일반 속성과 유사한 구문을 사용하되 receiver type 접두사를 붙입니다. val은 필수 getter가 있는 읽기 전용 extension property를 선언하고 var는 getter와 선택적 setter가 있는 가변 속성을 선언합니다.

kotlin
// 읽기 전용 extension property
val String.isEmail: Boolean
    get() = this.contains("@") && this.contains(".")

// 호출
val valid = "test@test.com".isEmail

참고: extension property는 괄호 없이 호출됩니다 — str.isEmail이며 str.isEmail()이 아닙니다. 이것이 extension property와 extension function의 주요 차이점입니다: 속성은 실제로 getter를 통해 계산되지만 필드처럼 보입니다.

제네릭 extension property

Extension properties는 제네릭이 될 수 있습니다 — receiver는 제네릭 타입 매개변수를 사용할 수 있습니다. 이를 통해 모든 컬렉션 타입에서 작동하는 범용 속성을 만들 수 있습니다.

kotlin
val List<T>.secondOrNull: T?
    get() = if (size >= 2) this[1] else null

val items = listOf("a", "b", "c")
val second = items.secondOrNull // "b"

secondOrNull 속성은 모든 타입 T에서 작동하며 목록의 두 번째 요소를 반환하거나 요소가 두 개 미만이면 null을 반환합니다. 이는 extension property가 함수보다 더 적절한 전형적인 예입니다 — 접근이 필드 읽기처럼 보입니다.

Extension property가 상태를 저장할 수 없는 이유

Extension property는 클래스 메타데이터에 추가되지 않기 때문에 backing field를 가질 수 없습니다 — 정적 getter/setter 함수 쌍으로만 존재합니다. Backing field(Kotlin의 field 키워드)는 속성 값을 저장하는 클래스의 내부 필드입니다. Extension property는 클래스의 내부 구조에 접근할 수 없습니다.

kotlin
// ❌ 오류: extension property는 backing field를 가질 수 없습니다
var String.cachedValue: String
    get() = "computed"
    set(value) {
        field = value // 필드에 접근할 수 없습니다!
    }

// ✅ 올바름: 외부 저장소 사용
val cache = MutableMap<String, String>()

var String.cachedValue: String
    get() = cache[this] ?: ""
    set(value) { cache[this] = value }

예제의 외부 Map은 저장 문제를 해결하지만 다른 문제인 메모리 누수를 만듭니다. Extension property를 통해 얻은 값은 정리하지 않으면 Map에 영원히 남습니다. 이 제한으로 인해 extension properties는 캐싱이나 임시 데이터 저장에 적합하지 않습니다.

캐싱에는 WeakHashMap 또는 자동 정리 메커니즘 사용이 권장됩니다. JetBrains는 수명 주기 관리 없이 프로덕션 코드에서 외부 저장소와 함께 var extension properties를 사용하지 않을 것을 권장합니다.

Extension property vs extension function: 언제 무엇을 선택할까

Extension property와 extension function의 선택은 의미론에 따라 다릅니다: 속성은 객체의 특성을 설명하고 함수는 동작을 설명합니다. Uniform Access Principle은 다음과 같이 말합니다: 클라이언트는 값이 계산되는지 저장되는지 알 필요가 없습니다. 값을 특성(길이, 크기, 상태)으로 표현할 수 있다면 속성을 사용하세요.

기준Extension propertyExtension function
호출괄호 없음: obj.property괄호 있음: obj.function()
의미론특성, 속성동작, 연산
Backing field지원 안 함해당 없음
매개변수getter/setter만모든 매개변수
성능동일 (정적 메서드)동일 (정적 메서드)
예제text.lengthtext.isEmail()

규칙은 간단합니다: 연산이 매개변수를 받으면 extension function을 사용하세요. 매개변수 없는 단순 계산 값이면 extension property를 사용하세요. Android Architecture Guide(Google, 2025)에 따르면 데이터 접근에는 extension properties를, 부작용이 있는 연산에는 extension functions를 우선해야 합니다.

var와 setter를 사용한 가변 extension property

var 키워드가 있는 extension property는 setter를 지원하지만 값을 저장하는 기능은 없습니다 — setter는 일반적으로 부작용을 수행하거나 외부 저장소에 데이터를 저장합니다. 구문은 가변 클래스 속성과 유사합니다.

kotlin
// setter가 있는 가변 extension property
var StringBuilder.lastChar: Char
    get() = this[length - 1]
    set(value) {
        this.setCharAt(length - 1, value)
    }

val sb = StringBuilder("Kotlin")
println(sb.lastChar) // n
sb.lastChar = '!'
println(sb) // Kotli!

lastChar 속성은 Kotlin 문서의 고전적인 예입니다. Getter는 StringBuilder의 마지막 문자를 반환하고 setter는 새 값으로 바꿉니다. 참고: 상태는 별도 필드가 아닌 StringBuilder 자체(setCharAt 통해)에 저장됩니다 — 이것이 extension property의 올바른 사용법입니다.

Extension properties 실전 예제

실제 프로젝트에서 extension properties는 주로 컬렉션 데이터 접근을 단순화하고 UI 요소의 크기나 상태를 계산하며 기존 클래스 위에 편리한 API를 만드는 데 사용됩니다. Kotlin 표준 라이브러리는 이 메커니즘을 적극적으로 사용합니다: 컬렉션의 size, indices, lastIndex는 extension properties입니다.

kotlin
// 컬렉션용 extension properties
val List<Int>.sumFast: Int
    get() = fold(0) { acc, i -> acc + i }

val String.half: String
    get() = this.substring(0, length / 2)

// Android View용 extension property
val View.isVisible: Boolean
    get() = visibility == View.VISIBLE

// 안전한 receiver를 통한 null 검사
val String?.isNullOrBlank: Boolean
    get() = this == null || this.isBlank()

View의 extension property isVisible은 모든 Android 개발자가 알아야 할 예제입니다. view.visibility == View.VISIBLE 대신 view.isVisible이라고 쓸 수 있습니다. 더 짧을 뿐만 아니라 자연어처럼 읽힙니다: “뷰가 보이는 경우”. 단순함에도 불구하고 이러한 속성은 코드 가독성을 크게 향상시킵니다.

자주 묻는 질문

companion object에 대해 extension property를 선언할 수 있나요?

아니요, extension properties는 companion object나 object declaration에 대해 선언할 수 없습니다. 확장 메커니즘은 클래스, 인터페이스 및 nullable 타입에만 적용됩니다. object에는 일반 최상위 함수를 사용하세요.

Extension property와 inline 속성의 차이는 무엇인가요?

Inline 속성(inline 수정자 사용)은 속성 객체를 생성하지 않고 getter/setter를 호출하는 Kotlin 메커니즘입니다. Extension property는 항상 정적 메서드로 컴파일되는 반면 inline 속성은 래퍼 없이 호출로 컴파일됩니다. 서로 다른 문제를 해결합니다: extension property는 기존 타입에 속성을 추가하고 inline은 자체 속성 호출을 최적화합니다.

Extension properties는 어노테이션을 지원하나요?

, extension property는 어노테이션을 가질 수 있지만 선언 수준에서만 가능합니다. 일반 클래스 속성과 달리 extension property의 getter나 setter를 별도로 어노테이션할 수 없습니다. 예: @JvmName(“getIsValid”) val String.isValid get() = true.

클래스의 companion object와 함께 extension property를 사용할 수 있나요?

아니요, extension properties는 companion object를 receiver로 선언할 수 없습니다. 이는 언어 제한입니다 — extension property는 타입 인스턴스에서만 작동하며 companion object는 정적 컨텍스트입니다. 최상위 extension 함수나 상수를 사용하세요.

Extension property가 APK 크기에 영향을 미치나요?

최소한으로 영향을 미칩니다. 각 extension property는 컴파일된 바이트코드에 하나의 정적 getter 메서드(그리고 선택적으로 setter)를 추가합니다. 비교하면 동일한 속성을 가진 래퍼 클래스를 만들면 전체 클래스가 추가됩니다. Extension properties는 기능을 확장하는 더 가벼운 접근 방식입니다.

요약

  • Extension property — 상속 없이 기존 타입에 대한 계산 속성
  • backing field 없음 — 상태가 저장되지 않고 getter를 통해서만 계산
  • setter가 있는 var — 가능하지만 데이터 쓰기를 위해 외부 저장소 필요
  • 구문 — receiver type과 필수 getter가 있는 val/var
  • 성능 — 제로 오버헤드, 정적 메서드로 컴파일
  • 용도 — 계산된 특성: 길이, 상태, 크기, 검사
  • 제한 — 상태 저장, 메모리 관리 없는 캐싱에 부적합

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

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

프로젝트 논의

더 읽어보기