[2026] Kotlin 고급 기능 | DSL, 리플렉션, 애노테이션

[2026] Kotlin 고급 기능 | DSL, 리플렉션, 애노테이션

이 글의 핵심

Kotlin 고급 기능: DSL, 리플렉션, 애노테이션. DSL (Domain Specific Language)·리플렉션 (Reflection).

들어가며

DSL 빌더·인라인·리플렉션 등은 라이브러리 API를 부드럽게 만드는 데 쓰입니다. 팀이 읽기 쉬운 수준으로만 도입하는 것이 좋습니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. DSL (Domain Specific Language)

DSL이란?

DSL은 특정 도메인에 특화된 언어입니다. Kotlin의 람다와 확장 함수를 활용해 자연스러운 DSL을 만들 수 있습니다.

HTML DSL 예제

다음은 kotlin를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class HTML {
    private val content = StringBuilder()
    
    fun body(block: Body.() -> Unit) {
        content.append("<body>")
        Body().apply(block).render(content)
        content.append("</body>")
    }
    
    fun render() = content.toString()
}
class Body {
    private val content = StringBuilder()
    
    fun h1(text: String) {
        content.append("<h1>$text</h1>")
    }
    
    fun p(text: String) {
        content.append("<p>$text</p>")
    }
    
    fun render(sb: StringBuilder) {
        sb.append(content)
    }
}
fun html(block: HTML.() -> Unit): HTML {
    return HTML().apply(block)
}
// 사용
val page = html {
    body {
        h1("제목")
        p("내용입니다.")
    }
}
println(page.render())

테스트 DSL

다음은 kotlin를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 비동기 처리를 통해 효율적으로 작업을 수행합니다, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class TestSuite {
    private val tests = mutableListOf<Test>()
    
    fun test(name: String, block: () -> Unit) {
        tests.add(Test(name, block))
    }
    
    fun run() {
        tests.forEach { it.run() }
    }
}
data class Test(val name: String, val block: () -> Unit) {
    fun run() {
        try {
            block()
            println("✓ $name")
        } catch (e: AssertionError) {
            println("✗ $name: ${e.message}")
        }
    }
}
fun suite(block: TestSuite.() -> Unit): TestSuite {
    return TestSuite().apply(block)
}
// 사용
suite {
    test("덧셈 테스트") {
        assert(1 + 1 == 2)
    }
    
    test("곱셈 테스트") {
        assert(2 * 3 == 6)
    }
}.run()

2. 리플렉션 (Reflection)

기본 리플렉션

다음은 kotlin를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며, 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import kotlin.reflect.full.*
class User(val name: String, val age: Int) {
    fun greet() = "안녕하세요, $name님!"
}
fun main() {
    val user = User("홍길동", 25)
    val kClass = user::class
    
    // 클래스 정보
    println("클래스명: ${kClass.simpleName}")
    println("패키지: ${kClass.qualifiedName}")
    
    // 프로퍼티 조회
    kClass.memberProperties.forEach { prop ->
        println("${prop.name}: ${prop.get(user)}")
    }
    
    // 함수 조회
    kClass.memberFunctions.forEach { func ->
        println("함수: ${func.name}")
    }
}

리플렉션으로 객체 생성

아래 코드는 kotlin를 사용한 구현 예제입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import kotlin.reflect.full.createInstance
import kotlin.reflect.full.primaryConstructor
class Person(val name: String, val age: Int)
fun main() {
    val kClass = Person::class
    
    // 생성자로 생성
    val constructor = kClass.primaryConstructor!!
    val person = constructor.call("홍길동", 25)
    
    println("${person.name}, ${person.age}")
}

3. 애노테이션 (Annotation)

커스텀 애노테이션

다음은 kotlin를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Entity(val tableName: String)
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class Column(val name: String)
@Entity(tableName = "users")
class User(
    @Column(name = "user_name")
    val name: String,
    
    @Column(name = "user_age")
    val age: Int
)

애노테이션 처리

아래 코드는 kotlin를 사용한 구현 예제입니다. 필요한 모듈을 import하고. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import kotlin.reflect.full.*
fun getTableName(obj: Any): String? {
    val annotation = obj::class.findAnnotation<Entity>()
    return annotation?.tableName
}
fun main() {
    val user = User("홍길동", 25)
    println("테이블명: ${getTableName(user)}")  // users
}

4. 인라인 함수 (Inline Functions)

기본 인라인

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

inline fun measureTime(block: () -> Unit) {
    val start = System.currentTimeMillis()
    block()
    val end = System.currentTimeMillis()
    println("실행 시간: ${end - start}ms")
}
measureTime {
    Thread.sleep(100)
}

reified 타입 매개변수

다음은 kotlin를 활용한 상세한 구현 코드입니다. 반복문으로 데이터를 처리합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

inline fun <reified T> isInstance(value: Any): Boolean {
    return value is T
}
inline fun <reified T> List<*>.filterIsInstance(): List<T> {
    return this.filter { it is T }.map { it as T }
}
fun main() {
    println(isInstance<String>("Hello"))  // true
    println(isInstance<Int>("Hello"))     // false
    
    val mixed: List<Any> = listOf(1, "two", 3, "four", 5)
    val strings = mixed.filterIsInstance<String>()
    println(strings)  // [two, four]
}

noinline과 crossinline

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

inline fun foo(
    inlined: () -> Unit,
    noinline notInlined: () -> Unit
) {
    inlined()
    notInlined()
}
inline fun bar(crossinline block: () -> Unit) {
    val runnable = Runnable { block() }
    runnable.run()
}

5. 델리게이트 (Delegates)

프로퍼티 델리게이트

다음은 kotlin를 활용한 상세한 구현 코드입니다. 필요한 모듈을 import하고, 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

import kotlin.properties.Delegates
class User {
    var name: String by Delegates.observable("초기값") { prop, old, new ->
        println("${prop.name}: $old$new")
    }
    
    var age: Int by Delegates.vetoable(0) { prop, old, new ->
        new >= 0  // 음수 거부
    }
}
fun main() {
    val user = User()
    user.name = "홍길동"  // name: 초기값 → 홍길동
    user.age = 25
    user.age = -1  // 거부됨
    println(user.age)  // 25
}

lazy 델리게이트

아래 코드는 kotlin를 사용한 구현 예제입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

val heavyObject: String by lazy {
    println("초기화 중...")
    "무거운 객체"
}
fun main() {
    println("시작")
    println(heavyObject)  // 여기서 초기화
    println(heavyObject)  // 재사용
}

6. 실전 예제

예제: JSON DSL

다음은 kotlin를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class JsonObject {
    private val properties = mutableMapOf<String, Any>()
    
    infix fun String.to(value: Any) {
        properties[this] = value
    }
    
    fun toJson(): String {
        return properties.entries.joinToString(
            separator = ", ",
            prefix = "{",
            postfix = "}"
        ) { (k, v) ->
            val valueStr = if (v is String) "\"$v\"" else v.toString()
            "\"$k\": $valueStr"
        }
    }
}
fun json(block: JsonObject.() -> Unit): JsonObject {
    return JsonObject().apply(block)
}
// 사용
val user = json {
    "name" to "홍길동"
    "age" to 25
    "active" to true
}
println(user.toJson())
// {"name": "홍길동", "age": 25, "active": true}

정리

핵심 요약

  1. DSL: 람다 + 확장 함수로 도메인 언어 구축
  2. 리플렉션: 런타임 타입 정보 조회
  3. 애노테이션: 메타데이터 정의 및 처리
  4. inline: 람다 오버헤드 제거
  5. reified: 제네릭 타입 런타임 접근
  6. 델리게이트: 프로퍼티 동작 위임

다음 단계

Kotlin 시리즈를 완료했습니다! 이제 다음 언어로 넘어가세요:


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3