[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}
정리
핵심 요약
- DSL: 람다 + 확장 함수로 도메인 언어 구축
- 리플렉션: 런타임 타입 정보 조회
- 애노테이션: 메타데이터 정의 및 처리
- inline: 람다 오버헤드 제거
- reified: 제네릭 타입 런타임 접근
- 델리게이트: 프로퍼티 동작 위임
다음 단계
Kotlin 시리즈를 완료했습니다! 이제 다음 언어로 넘어가세요: