์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ Kotlin ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API ์™„์ „์ •๋ณต: ๋Ÿฐํƒ€์ž„์— ์ฝ”๋“œ ๋ถ„์„ํ•˜๊ณ  ์กฐ์ž‘ํ•˜๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ์ˆ 

๐Ÿš€ Kotlin ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API ์™„์ „์ •๋ณต: ๋Ÿฐํƒ€์ž„์— ์ฝ”๋“œ ๋ถ„์„ํ•˜๊ณ  ์กฐ์ž‘ํ•˜๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ์ˆ 

์•ˆ๋…•, ์ฝ”ํ‹€๋ฆฐ ๊ฐœ๋ฐœ์ž ์นœ๊ตฌ๋“ค! ์˜ค๋Š˜์€ 2025๋…„ 3์›”, ์ฝ”ํ‹€๋ฆฐ ์„ธ๊ณ„์—์„œ ๊ฝค๋‚˜ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ ์ž˜ ๋ชจ๋ฅด๋Š” ์‚ฌ๋žŒ๋„ ๋งŽ์€ ๋ฆฌํ”Œ๋ ‰์…˜ API์— ๋Œ€ํ•ด ํ•จ๊ป˜ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ๋งˆ์น˜ ๋งˆ๋ฒ•์ฒ˜๋Ÿผ ๋Ÿฐํƒ€์ž„์— ์ฝ”๋“œ๋ฅผ ๋“ค์—ฌ๋‹ค๋ณด๊ณ  ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ด ๊ธฐ์ˆ , ์–ด๋ ต๊ฒŒ๋งŒ ๋А๊ปด์กŒ๋‹ค๋ฉด ์ด์ œ ์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด ์ค„๊ฒŒ! ๐Ÿง™โ€โ™‚๏ธโœจ

๐Ÿ“š ๋ชฉ์ฐจ

  1. ๋ฆฌํ”Œ๋ ‰์…˜์ด ๋ญ์•ผ? ์™œ ํ•„์š”ํ•œ ๊ฑฐ์ง€? ๐Ÿค”
  2. ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API ์‹œ์ž‘ํ•˜๊ธฐ ๐Ÿš€
  3. ํด๋ž˜์Šค์™€ ๊ฐ์ฒด ๋ถ„์„ํ•˜๊ธฐ ๐Ÿ”
  4. ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ ๋‹ค๋ฃจ๊ธฐ โš™๏ธ
  5. ์• ๋…ธํ…Œ์ด์…˜ ํ™œ์šฉํ•˜๊ธฐ ๐Ÿท๏ธ
  6. ์ œ๋„ค๋ฆญ๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜ ๐Ÿ’ซ
  7. ์‹ค์ „ ํ™œ์šฉ ์‚ฌ๋ก€ ๋ฐ ํ”„๋กœ์ ํŠธ ๐Ÿ’ผ
  8. ์„ฑ๋Šฅ๊ณผ ์ฃผ์˜์‚ฌํ•ญ โš ๏ธ
  9. ๋งˆ๋ฌด๋ฆฌ ๋ฐ ์ถ”๊ฐ€ ์ž๋ฃŒ ๐Ÿ“

1. ๋ฆฌํ”Œ๋ ‰์…˜์ด ๋ญ์•ผ? ์™œ ํ•„์š”ํ•œ ๊ฑฐ์ง€? ๐Ÿค”

๋ฆฌํ”Œ๋ ‰์…˜์ด๋ผ๋Š” ๋‹จ์–ด๋ฅผ ๋“ค์–ด๋ดค์–ด? ์˜์–ด๋กœ๋Š” 'reflection'์ธ๋ฐ, ๊ฑฐ์šธ์— ๋น„์นœ ๋ชจ์Šต์ฒ˜๋Ÿผ ํ”„๋กœ๊ทธ๋žจ์ด ์ž๊ธฐ ์ž์‹ ์„ ๋“ค์—ฌ๋‹ค๋ณด๊ณ  ๋ถ„์„ํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ์„ ๋งํ•ด. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰ ์ค‘์— ์ž๊ธฐ ์ž์‹ ์˜ ๊ตฌ์กฐ๋ฅผ ํŒŒ์•…ํ•˜๊ณ  ์‹ฌ์ง€์–ด ์ˆ˜์ •ํ•  ์ˆ˜๋„ ์žˆ๋Š” ๋งˆ๋ฒ• ๊ฐ™์€ ๊ธฐ๋Šฅ์ด์•ผ! ๐Ÿ˜ฒ

์˜ˆ๋ฅผ ๋“ค์–ด, ๋„ˆ๊ฐ€ ์–ด๋–ค ํด๋ž˜์Šค์˜ ์ด๋ฆ„๋งŒ ์•Œ๊ณ  ์žˆ๋Š”๋ฐ ๊ทธ ํด๋ž˜์Šค์˜ ๋ชจ๋“  ๋ฉ”์†Œ๋“œ๋‚˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด? ๋˜๋Š” ๋Ÿฐํƒ€์ž„์— ํŠน์ • ์• ๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ํ•จ์ˆ˜๋“ค๋งŒ ์ฐพ์•„์„œ ์‹คํ–‰ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด? ์ด๋Ÿด ๋•Œ ๋ฆฌํ”Œ๋ ‰์…˜์ด ํ•„์š”ํ•œ ๊ฑฐ์ง€!

์ฝ”๋“œ class Person(val name: String) ๋ฆฌํ”Œ๋ ‰์…˜ - ํด๋ž˜์Šค ์ด๋ฆ„: Person - ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ: name (String) - ํ”„๋กœํผํ‹ฐ: name ๋ถ„์„ ๋Ÿฐํƒ€์ž„์— ์ฝ”๋“œ ์‚ดํŽด๋ณด๊ธฐ

๋ฆฌํ”Œ๋ ‰์…˜์ด ํ•„์š”ํ•œ ์ƒํ™ฉ๋“ค ๐Ÿ› ๏ธ

  1. ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐœ๋ฐœ - Spring, Ktor ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์€ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ™œ์šฉํ•ด ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ด.
  2. ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ - ๋Ÿฐํƒ€์ž„์— ์ƒˆ๋กœ์šด ์ฝ”๋“œ๋ฅผ ๋ฐœ๊ฒฌํ•˜๊ณ  ๋กœ๋“œํ•  ๋•Œ ์œ ์šฉํ•ด.
  3. ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” - JSON ํŒŒ์‹ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ๊ฐ์ฒด ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•  ๋•Œ ์‚ฌ์šฉํ•ด.
  4. ํ…Œ์ŠคํŠธ ๋„๊ตฌ - ๋ชจํ‚น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‚˜ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ๋งŽ์ด ํ™œ์šฉ๋ผ.
  5. ์ฝ”๋“œ ์ƒ์„ฑ - ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ฝ”๋“œ๋ฅผ ์ž๋™ ์ƒ์„ฑํ•˜๋Š” ๋„๊ตฌ๋ฅผ ๋งŒ๋“ค ๋•Œ ํ•„์š”ํ•ด.

๐Ÿ”” ์ฐธ๊ณ : ๋ฆฌํ”Œ๋ ‰์…˜์€ ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ๋‚จ์šฉํ•˜๋ฉด ์ฝ”๋“œ๊ฐ€ ๋ณต์žกํ•ด์ง€๊ณ  ์„ฑ๋Šฅ์ด ์ €ํ•˜๋  ์ˆ˜ ์žˆ์–ด. ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„! ์ผ๋ฐ˜์ ์ธ ๊ฐœ๋ฐœ์—์„œ๋Š” ์ฝ”ํ‹€๋ฆฐ์˜ ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ์‹œ์Šคํ…œ์„ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์•ˆ์ „ํ•˜๊ณ  ํšจ์œจ์ ์ด์ง€.

2. ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API ์‹œ์ž‘ํ•˜๊ธฐ ๐Ÿš€

์ž, ์ด์ œ ์ฝ”ํ‹€๋ฆฐ์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณผ ์ฐจ๋ก€์•ผ. ์ฝ”ํ‹€๋ฆฐ์€ ์ž๋ฐ”์˜ ๋ฆฌํ”Œ๋ ‰์…˜ API๋ฅผ ์™„์ „ํžˆ ์ง€์›ํ•˜๋ฉด์„œ๋„, ๋” ์ฝ”ํ‹€๋ฆฐ์Šค๋Ÿฌ์šด(Kotlin-friendly) ์ž์ฒด ๋ฆฌํ”Œ๋ ‰์…˜ API๋ฅผ ์ œ๊ณตํ•ด. ๋‘˜ ๋‹ค ์•Œ์•„๋‘๋ฉด ์ข‹์ง€๋งŒ, ์˜ค๋Š˜์€ ์ฃผ๋กœ ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API์— ์ง‘์ค‘ํ•  ๊ฑฐ์•ผ. ๐Ÿ˜Š

๋ฆฌํ”Œ๋ ‰์…˜ ์˜์กด์„ฑ ์ถ”๊ฐ€ํ•˜๊ธฐ ๐Ÿ“ฆ

์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๋จผ์ € ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ด. ์ฝ”ํ‹€๋ฆฐ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฆฌํ”Œ๋ ‰์…˜ ๊ธฐ๋Šฅ๋งŒ ํฌํ•จ๋˜์–ด ์žˆ๊ฑฐ๋“ .

Gradle (build.gradle.kts):

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.22") // 2025๋…„ 3์›” ๊ธฐ์ค€ ์ตœ์‹  ๋ฒ„์ „ ์‚ฌ์šฉ
}

Maven (pom.xml):

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-reflect</artifactId>
    <version>1.9.22</version>
</dependency>

๐Ÿ’ก ํŒ: ํ•ญ์ƒ ์ฝ”ํ‹€๋ฆฐ ์ปดํŒŒ์ผ๋Ÿฌ ๋ฒ„์ „๊ณผ ๋™์ผํ•œ ๋ฒ„์ „์˜ kotlin-reflect๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„. ๋ฒ„์ „์ด ๋งž์ง€ ์•Š์œผ๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด.

๊ธฐ๋ณธ์ ์ธ ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ๋ฒ• ๐Ÿ”

์ฝ”ํ‹€๋ฆฐ์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‹œ์ž‘ํ•˜๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•์€ :: ์—ฐ์‚ฐ์ž(์ด์ค‘ ์ฝœ๋ก )๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฑฐ์•ผ. ์ด ์—ฐ์‚ฐ์ž๋Š” ํด๋ž˜์Šค, ํ•จ์ˆ˜, ํ”„๋กœํผํ‹ฐ์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜.

import kotlin.reflect.KClass
import kotlin.reflect.full.*

// ๊ฐ„๋‹จํ•œ ํด๋ž˜์Šค ์ •์˜
class Person(val name: String, var age: Int) {
    fun introduce() = "์•ˆ๋…•, ๋‚˜๋Š” $name์ด๊ณ , $age์‚ด์ด์•ผ."
    private fun secretMethod() = "์ด๊ฑด ๋น„๋ฐ€์ด์•ผ!"
}

fun main() {
    // ํด๋ž˜์Šค ์ฐธ์กฐ ์–ป๊ธฐ
    val personClass: KClass<Person> = Person::class
    
    // ํด๋ž˜์Šค ์ด๋ฆ„ ์ถœ๋ ฅ
    println("ํด๋ž˜์Šค ์ด๋ฆ„: ${personClass.simpleName}")  // ์ถœ๋ ฅ: ํด๋ž˜์Šค ์ด๋ฆ„: Person
    
    // ์ƒ์„ฑ์ž ์ •๋ณด ์–ป๊ธฐ
    val constructor = personClass.primaryConstructor
    println("์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ: ${constructor?.parameters?.joinToString { it.name ?: "" }}")
    
    // ํ”„๋กœํผํ‹ฐ ๋ชฉ๋ก ์–ป๊ธฐ
    val properties = personClass.memberProperties
    println("ํ”„๋กœํผํ‹ฐ ๋ชฉ๋ก: ${properties.joinToString { it.name }}")
    
    // ํ•จ์ˆ˜ ๋ชฉ๋ก ์–ป๊ธฐ
    val functions = personClass.memberFunctions
    println("ํ•จ์ˆ˜ ๋ชฉ๋ก: ${functions.joinToString { it.name }}")
    
    // ๊ฐ์ฒด ์ƒ์„ฑ ๋ฐ ํ•จ์ˆ˜ ํ˜ธ์ถœ
    val person = Person("์ฝ”ํ‹€๋ฆฐ", 11)
    val introduceFunc = Person::introduce
    println(introduceFunc.invoke(person))  // ์ถœ๋ ฅ: ์•ˆ๋…•, ๋‚˜๋Š” ์ฝ”ํ‹€๋ฆฐ์ด๊ณ , 11์‚ด์ด์•ผ.
}
Person::class ํด๋ž˜์Šค ์ฐธ์กฐ (KClass<Person>) Properties name: String age: Int Constructors Person(name: String, age: Int) Functions introduce() secretMethod() ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ

์œ„ ์ฝ”๋“œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API๋Š” ํด๋ž˜์Šค, ํ”„๋กœํผํ‹ฐ, ํ•จ์ˆ˜ ๋“ฑ์˜ ์ •๋ณด๋ฅผ ์‰ฝ๊ฒŒ ์–ป์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค˜. ์ด์ œ ๊ฐ๊ฐ์˜ ์š”์†Œ๋“ค์„ ๋” ์ž์„ธํžˆ ์‚ดํŽด๋ณผ๊ฒŒ!

3. ํด๋ž˜์Šค์™€ ๊ฐ์ฒด ๋ถ„์„ํ•˜๊ธฐ ๐Ÿ”

์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜์—์„œ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ด ๋˜๋Š” ๊ฒƒ์€ ํด๋ž˜์Šค์™€ ๊ฐ์ฒด๋ฅผ ๋ถ„์„ํ•˜๋Š” ๊ฑฐ์•ผ. ํด๋ž˜์Šค์˜ ๊ตฌ์กฐ๋ฅผ ํŒŒ์•…ํ•˜๊ณ , ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํƒ€์ž…์„ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ•ต๏ธโ€โ™‚๏ธ

ํด๋ž˜์Šค ์ฐธ์กฐ ์–ป๊ธฐ ๐Ÿ“‹

์ฝ”ํ‹€๋ฆฐ์—์„œ ํด๋ž˜์Šค ์ฐธ์กฐ๋ฅผ ์–ป๋Š” ๋ฐฉ๋ฒ•์€ ํฌ๊ฒŒ ๋‘ ๊ฐ€์ง€์•ผ:

// 1. ํด๋ž˜์Šค ์ด๋ฆ„์œผ๋กœ ์ง์ ‘ ์ฐธ์กฐ ์–ป๊ธฐ
val personClass = Person::class

// 2. ๊ฐ์ฒด๋กœ๋ถ€ํ„ฐ ํด๋ž˜์Šค ์ฐธ์กฐ ์–ป๊ธฐ
val person = Person("์ฝ”ํ‹€๋ฆฐ", 11)
val personClassFromObj = person::class

๋‘ ๋ฐฉ๋ฒ• ๋ชจ๋‘ KClass<Person> ํƒ€์ž…์˜ ์ฐธ์กฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ด. ์ด ์ฐธ์กฐ๋ฅผ ํ†ตํ•ด ํด๋ž˜์Šค์˜ ๋‹ค์–‘ํ•œ ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด.

ํด๋ž˜์Šค ์ •๋ณด ํƒ์ƒ‰ํ•˜๊ธฐ ๐Ÿงญ

ํด๋ž˜์Šค ์ฐธ์กฐ๋ฅผ ์–ป์—ˆ๋‹ค๋ฉด, ์ด์ œ ๊ทธ ํด๋ž˜์Šค์— ๋Œ€ํ•œ ๋‹ค์–‘ํ•œ ์ •๋ณด๋ฅผ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ์–ด:

val personClass = Person::class

// ํด๋ž˜์Šค ๊ธฐ๋ณธ ์ •๋ณด
println("ํด๋ž˜์Šค ์ด๋ฆ„: ${personClass.simpleName}")
println("ํŒจํ‚ค์ง€: ${personClass.java.`package`?.name ?: "default package"}")
println("์ถ”์ƒ ํด๋ž˜์Šค์ธ๊ฐ€? ${personClass.isAbstract}")
println("๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์ธ๊ฐ€? ${personClass.isData}")
println("๋‚ด๋ถ€ ํด๋ž˜์Šค์ธ๊ฐ€? ${personClass.isInner}")
println("Companion ๊ฐ์ฒด๊ฐ€ ์žˆ๋Š”๊ฐ€? ${personClass.companionObject != null}")

// ์ƒ์† ๊ด€๊ณ„ ํ™•์ธ
println("์Šˆํผํด๋ž˜์Šค: ${personClass.supertypes.joinToString()}")
println("๋ชจ๋“  ์Šˆํผํƒ€์ž…: ${personClass.allSupertypes.joinToString()}")

// ๊ฐ€์‹œ์„ฑ ํ™•์ธ
println("๊ฐ€์‹œ์„ฑ: ${personClass.visibility}")

// ์ œ๋„ค๋ฆญ ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ
println("ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ: ${personClass.typeParameters.joinToString { it.name }}")

๐Ÿ” ์˜ˆ์‹œ ๊ฒฐ๊ณผ:
ํด๋ž˜์Šค ์ด๋ฆ„: Person
ํŒจํ‚ค์ง€: com.example
์ถ”์ƒ ํด๋ž˜์Šค์ธ๊ฐ€? false
๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์ธ๊ฐ€? false
๋‚ด๋ถ€ ํด๋ž˜์Šค์ธ๊ฐ€? false
Companion ๊ฐ์ฒด๊ฐ€ ์žˆ๋Š”๊ฐ€? false
์Šˆํผํด๋ž˜์Šค: kotlin.Any
๋ชจ๋“  ์Šˆํผํƒ€์ž…: kotlin.Any
๊ฐ€์‹œ์„ฑ: PUBLIC
ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ:

๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ ๐Ÿ—๏ธ

๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ํด๋ž˜์Šค ์ •๋ณด๋งŒ ๊ฐ€์ง€๊ณ ๋„ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฑด ํŠนํžˆ ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ์ด๋‚˜ ์˜์กด์„ฑ ์ฃผ์ž… ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉ๋ผ.

import kotlin.reflect.full.primaryConstructor

fun main() {
    val personClass = Person::class
    
    // ๊ธฐ๋ณธ ์ƒ์„ฑ์ž ๊ฐ€์ ธ์˜ค๊ธฐ
    val constructor = personClass.primaryConstructor
    
    if (constructor != null) {
        // ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด ์ถœ๋ ฅ
        println("์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ:")
        constructor.parameters.forEach { param ->
            println("- ${param.name}: ${param.type}")
        }
        
        // ๊ฐ์ฒด ์ƒ์„ฑํ•˜๊ธฐ
        val person = constructor.call("๋ฆฌํ”Œ๋ ‰์…˜", 3)
        println("์ƒ์„ฑ๋œ ๊ฐ์ฒด: $person")
        println("์ž๊ธฐ์†Œ๊ฐœ: ${person.introduce()}")
    }
}

์œ„ ์ฝ”๋“œ์—์„œ๋Š” primaryConstructor๋ฅผ ์‚ฌ์šฉํ•ด ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ , call() ๋ฉ”์†Œ๋“œ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ–ˆ์–ด. ํŒŒ๋ผ๋ฏธํ„ฐ ์ˆœ์„œ๋Œ€๋กœ ์ธ์ž๋ฅผ ์ „๋‹ฌํ•˜๋ฉด ๋ผ.

โš ๏ธ ์ฃผ์˜: ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ๋Š” private ์ƒ์„ฑ์ž์—๋„ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฑด ๊ฐ•๋ ฅํ•˜์ง€๋งŒ, ํด๋ž˜์Šค ์„ค๊ณ„์ž์˜ ์˜๋„๋ฅผ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ด!

ํƒ€์ž… ์ฒดํฌ์™€ ์บ์ŠคํŒ… ๐Ÿ”„

๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋Ÿฐํƒ€์ž„์— ๊ฐ์ฒด์˜ ํƒ€์ž…์„ ํ™•์ธํ•˜๊ณ  ์•ˆ์ „ํ•˜๊ฒŒ ์บ์ŠคํŒ…ํ•  ์ˆ˜ ์žˆ์–ด:

fun examineObject(any: Any) {
    val kClass = any::class
    
    println("๊ฐ์ฒด ํƒ€์ž…: ${kClass.qualifiedName}")
    
    when (any) {
        is Person -> println("์ด ๊ฐ์ฒด๋Š” Person ํƒ€์ž…์ด๊ณ , ์ด๋ฆ„์€ ${any.name}์ด์•ผ.")
        is String -> println("์ด ๊ฐ์ฒด๋Š” String ํƒ€์ž…์ด๊ณ , ๊ธธ์ด๋Š” ${any.length}์•ผ.")
        is List<*> -> println("์ด ๊ฐ์ฒด๋Š” List ํƒ€์ž…์ด๊ณ , ํฌ๊ธฐ๋Š” ${any.size}์•ผ.")
        else -> println("์•Œ ์ˆ˜ ์—†๋Š” ํƒ€์ž…์˜ ๊ฐ์ฒด์•ผ.")
    }
    
    // ํŠน์ • ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ ์—ฌ๋ถ€ ํ™•์ธ
    if (kClass.java.interfaces.any { it.name == "java.io.Serializable" }) {
        println("์ด ๊ฐ์ฒด๋Š” Serializable์„ ๊ตฌํ˜„ํ–ˆ์–ด.")
    }
}

์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” is ์—ฐ์‚ฐ์ž์™€ ์Šค๋งˆํŠธ ์บ์ŠคํŠธ ๋•๋ถ„์— ํƒ€์ž… ์ฒดํฌ๊ฐ€ ๋งค์šฐ ํŽธ๋ฆฌํ•ด. ํ•˜์ง€๋งŒ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋” ๋™์ ์ธ ํƒ€์ž… ๊ฒ€์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ ธ.

๊ฐ์ฒด Person("์ฝ”ํ‹€๋ฆฐ", 11) ๋Ÿฐํƒ€์ž„ ์ธ์Šคํ„ด์Šค any::class KClass<Person> qualifiedName = "com.example.Person" isData = false memberProperties = [name, age] memberFunctions = [introduce, ...] ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ๊ฐ์ฒด ํƒ€์ž… ๋ถ„์„ํ•˜๊ธฐ

์ด๋ ‡๊ฒŒ ํด๋ž˜์Šค์™€ ๊ฐ์ฒด๋ฅผ ๋ถ„์„ํ•˜๋Š” ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค์–ด. ๋‹ค์Œ์œผ๋กœ๋Š” ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณผ๊ฒŒ! ๐Ÿš€

4. ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ ๋‹ค๋ฃจ๊ธฐ โš™๏ธ

ํด๋ž˜์Šค์˜ ๊ตฌ์กฐ๋ฅผ ํŒŒ์•…ํ–ˆ๋‹ค๋ฉด, ์ด์ œ ๊ทธ ํด๋ž˜์Šค์˜ ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ ์ฐจ๋ก€์•ผ. ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋Ÿฐํƒ€์ž„์— ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ์ฝ๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด! ๐Ÿงฐ

ํ•จ์ˆ˜ ๋‹ค๋ฃจ๊ธฐ ๐ŸŽฎ

ํ•จ์ˆ˜๋ฅผ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ๋‹ค๋ฃจ๋ ค๋ฉด ๋จผ์ € ํ•จ์ˆ˜ ์ฐธ์กฐ๋ฅผ ์–ป์–ด์•ผ ํ•ด. ํ•จ์ˆ˜ ์ฐธ์กฐ๋Š” KFunction ํƒ€์ž…์œผ๋กœ ํ‘œํ˜„๋ผ.

import kotlin.reflect.KFunction
import kotlin.reflect.full.functions
import kotlin.reflect.full.memberFunctions

class Calculator {
    fun add(a: Int, b: Int): Int = a + b
    fun multiply(a: Int, b: Int): Int = a * b
    private fun secretOperation(x: Int): Int = x * x * x
}

fun main() {
    val calculator = Calculator()
    val calculatorClass = Calculator::class
    
    // 1. ํด๋ž˜์Šค์—์„œ ๋ชจ๋“  ํ•จ์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
    val allFunctions = calculatorClass.memberFunctions
    println("๋ชจ๋“  ํ•จ์ˆ˜: ${allFunctions.joinToString { it.name }}")
    
    // 2. ํŠน์ • ์ด๋ฆ„์˜ ํ•จ์ˆ˜ ์ฐพ๊ธฐ
    val addFunction = calculatorClass.functions.find { it.name == "add" }
    
    if (addFunction != null) {
        // ํ•จ์ˆ˜ ์ •๋ณด ์ถœ๋ ฅ
        println("ํ•จ์ˆ˜ ์ด๋ฆ„: ${addFunction.name}")
        println("๋ฐ˜ํ™˜ ํƒ€์ž…: ${addFunction.returnType}")
        println("ํŒŒ๋ผ๋ฏธํ„ฐ: ${addFunction.parameters.joinToString { "${it.name}: ${it.type}" }}")
        
        // ํ•จ์ˆ˜ ํ˜ธ์ถœํ•˜๊ธฐ
        val result = addFunction.call(calculator, 10, 20)
        println("10 + 20 = $result")  // ์ถœ๋ ฅ: 10 + 20 = 30
    }
    
    // 3. private ํ•จ์ˆ˜์— ์ ‘๊ทผํ•˜๊ธฐ
    val secretFunction = calculatorClass.functions.find { it.name == "secretOperation" }
    if (secretFunction != null) {
        // ์ ‘๊ทผ ์ œํ•œ ์šฐํšŒํ•˜๊ธฐ
        secretFunction.isAccessible = true
        val result = secretFunction.call(calculator, 4)
        println("๋น„๋ฐ€ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ: $result")  // ์ถœ๋ ฅ: ๋น„๋ฐ€ ์—ฐ์‚ฐ ๊ฒฐ๊ณผ: 64
    }
}

๐Ÿ’ก ํŒ: ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋Š” ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋กœ ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ์ „๋‹ฌํ•ด์•ผ ํ•ด. ์ด๋Š” ํ•จ์ˆ˜๊ฐ€ ์–ด๋–ค ๊ฐ์ฒด์— ๋Œ€ํ•ด ํ˜ธ์ถœ๋ ์ง€๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฑฐ์•ผ. ์ •์  ํ•จ์ˆ˜๋ผ๋ฉด ์ด ์ธ์ž๋Š” ํ•„์š” ์—†์–ด.

ํ•จ์ˆ˜ ์ฐธ์กฐ์™€ ๋ฐ”์ธ๋”ฉ ๐Ÿ”—

์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ํ•จ์ˆ˜ ์ฐธ์กฐ๋ฅผ ๋” ์‰ฝ๊ฒŒ ์–ป๋Š” ๋ฐฉ๋ฒ•๋„ ์žˆ์–ด:

val calculator = Calculator()

// ํด๋ž˜์Šค์˜ ํ•จ์ˆ˜ ์ฐธ์กฐ ์–ป๊ธฐ
val addRef = Calculator::add
val result1 = addRef.invoke(calculator, 5, 7)  // 12

// ํŠน์ • ์ธ์Šคํ„ด์Šค์— ๋ฐ”์ธ๋”ฉ๋œ ํ•จ์ˆ˜ ์ฐธ์กฐ
val boundAddRef = calculator::add
val result2 = boundAddRef.invoke(5, 7)  // 12 (์ธ์Šคํ„ด์Šค๊ฐ€ ์ด๋ฏธ ๋ฐ”์ธ๋”ฉ๋˜์–ด ์žˆ์Œ)

// ์ตœ์ƒ์œ„ ํ•จ์ˆ˜ ์ฐธ์กฐ
fun square(n: Int): Int = n * n
val squareRef = ::square
val result3 = squareRef.invoke(4)  // 16

๋ฐ”์ธ๋”ฉ๋œ ํ•จ์ˆ˜ ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ์ฒด ์ธ์Šคํ„ด์Šค๋ฅผ ๋งค๋ฒˆ ์ „๋‹ฌํ•  ํ•„์š”๊ฐ€ ์—†์–ด์„œ ์ฝ”๋“œ๊ฐ€ ๋” ๊ฐ„๊ฒฐํ•ด์ ธ.

ํ”„๋กœํผํ‹ฐ ๋‹ค๋ฃจ๊ธฐ ๐Ÿ“Š

ํ”„๋กœํผํ‹ฐ๋Š” ์ฝ”ํ‹€๋ฆฐ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์•ผ. ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ๋Ÿฐํƒ€์ž„์— ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ์ฝ๊ฑฐ๋‚˜ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด:

import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible

class User(val username: String, private var _password: String, var email: String) {
    val isAdmin: Boolean get() = username == "admin"
}

fun main() {
    val user = User("kotlin", "secret123", "kotlin@example.com")
    val userClass = User::class
    
    // 1. ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ ๊ฐ€์ ธ์˜ค๊ธฐ
    val properties = userClass.memberProperties
    println("๋ชจ๋“  ํ”„๋กœํผํ‹ฐ: ${properties.joinToString { it.name }}")
    
    // 2. ํŠน์ • ํ”„๋กœํผํ‹ฐ ์ฐพ๊ธฐ
    val emailProperty = userClass.memberProperties.find { it.name == "email" }
    
    if (emailProperty != null) {
        // ํ”„๋กœํผํ‹ฐ ์ •๋ณด ์ถœ๋ ฅ
        println("ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„: ${emailProperty.name}")
        println("ํ”„๋กœํผํ‹ฐ ํƒ€์ž…: ${emailProperty.returnType}")
        
        // ํ”„๋กœํผํ‹ฐ ๊ฐ’ ์ฝ๊ธฐ
        val email = emailProperty.get(user)
        println("์ด๋ฉ”์ผ: $email")  // ์ถœ๋ ฅ: ์ด๋ฉ”์ผ: kotlin@example.com
        
        // ํ”„๋กœํผํ‹ฐ ๊ฐ’ ๋ณ€๊ฒฝํ•˜๊ธฐ (๊ฐ€๋ณ€ ํ”„๋กœํผํ‹ฐ์ธ ๊ฒฝ์šฐ)
        if (emailProperty is kotlin.reflect.KMutableProperty1) {
            emailProperty.set(user, "new.kotlin@example.com")
            println("๋ณ€๊ฒฝ๋œ ์ด๋ฉ”์ผ: ${emailProperty.get(user)}")
        }
    }
    
    // 3. private ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผํ•˜๊ธฐ
    val passwordProperty = userClass.memberProperties.find { it.name == "_password" }
    if (passwordProperty != null) {
        // ์ ‘๊ทผ ์ œํ•œ ์šฐํšŒํ•˜๊ธฐ
        passwordProperty.isAccessible = true
        val password = passwordProperty.get(user)
        println("๋น„๋ฐ€๋ฒˆํ˜ธ: $password")  // ์ถœ๋ ฅ: ๋น„๋ฐ€๋ฒˆํ˜ธ: secret123
    }
    
    // 4. ๊ณ„์‚ฐ ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผํ•˜๊ธฐ
    val isAdminProperty = userClass.memberProperties.find { it.name == "isAdmin" }
    if (isAdminProperty != null) {
        val isAdmin = isAdminProperty.get(user)
        println("๊ด€๋ฆฌ์ž ์—ฌ๋ถ€: $isAdmin")  // ์ถœ๋ ฅ: ๊ด€๋ฆฌ์ž ์—ฌ๋ถ€: false
    }
}

โš ๏ธ ์ฃผ์˜: private ํ”„๋กœํผํ‹ฐ์— ์ ‘๊ทผํ•  ๋•Œ๋Š” isAccessible = true๋กœ ์„ค์ •ํ•ด์•ผ ํ•ด. ํ•˜์ง€๋งŒ ์ด๋Š” ์บก์Аํ™”๋ฅผ ๊นจ๋Š” ํ–‰์œ„์ด๋ฏ€๋กœ ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด!

ํ•จ์ˆ˜ ๋ฆฌํ”Œ๋ ‰์…˜ KFunction<R> ํ•จ์ˆ˜ ์ฐธ์กฐ์˜ ๊ธฐ๋ณธ ์ธํ„ฐํŽ˜์ด์Šค call(instance, arg1, arg2, ...) ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋ฉ”์†Œ๋“œ isAccessible = true private ํ•จ์ˆ˜ ์ ‘๊ทผ ํ—ˆ์šฉ ํ”„๋กœํผํ‹ฐ ๋ฆฌํ”Œ๋ ‰์…˜ KProperty<R> ํ”„๋กœํผํ‹ฐ ์ฐธ์กฐ์˜ ๊ธฐ๋ณธ ์ธํ„ฐํŽ˜์ด์Šค get(instance) ํ”„๋กœํผํ‹ฐ ๊ฐ’ ์ฝ๊ธฐ set(instance, value) ํ”„๋กœํผํ‹ฐ ๊ฐ’ ๋ณ€๊ฒฝ (๊ฐ€๋ณ€ ํ”„๋กœํผํ‹ฐ) ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ ๋ฆฌํ”Œ๋ ‰์…˜ API

ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค์–ด. ์ด์ œ ์• ๋…ธํ…Œ์ด์…˜์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณผ๊ฒŒ! ๐Ÿท๏ธ

5. ์• ๋…ธํ…Œ์ด์…˜ ํ™œ์šฉํ•˜๊ธฐ ๐Ÿท๏ธ

์• ๋…ธํ…Œ์ด์…˜์€ ์ฝ”๋“œ์— ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฐ•๋ ฅํ•œ ๋ฐฉ๋ฒ•์ด์•ผ. ๋ฆฌํ”Œ๋ ‰์…˜๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋งŒ๋“ค ๋•Œ ์ •๋ง ์œ ์šฉํ•ด! ํŠนํžˆ ์„ ์–ธ์  ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์Šคํƒ€์ผ์„ ๊ตฌํ˜„ํ•  ๋•Œ ๋งŽ์ด ์‚ฌ์šฉ๋ผ. ๐Ÿ˜Š

์• ๋…ธํ…Œ์ด์…˜ ์ •์˜ํ•˜๊ธฐ ๐Ÿ“

๋จผ์ € ๊ฐ„๋‹จํ•œ ์• ๋…ธํ…Œ์ด์…˜์„ ๋ช‡ ๊ฐœ ์ •์˜ํ•ด๋ณผ๊ฒŒ:

// ํ•„๋“œ ๊ฒ€์ฆ์šฉ ์• ๋…ธํ…Œ์ด์…˜
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class MaxLength(val length: Int)

// HTTP ์š”์ฒญ ๋งคํ•‘์šฉ ์• ๋…ธํ…Œ์ด์…˜
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class HttpEndpoint(val path: String, val method: String = "GET")

// ์ง๋ ฌํ™” ์ œ์™ธ ์• ๋…ธํ…Œ์ด์…˜
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class Exclude

์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•œ ๋ถ€๋ถ„์€ @Retention(AnnotationRetention.RUNTIME)์ด์•ผ. ์ด ์„ค์ •์ด ์žˆ์–ด์•ผ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ๋Ÿฐํƒ€์ž„์— ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ณด๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์–ด.

์• ๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉํ•˜๊ธฐ ๐Ÿ”–

์ด์ œ ์ •์˜ํ•œ ์• ๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•ด๋ณผ๊ฒŒ:

class UserProfile(
    @MaxLength(50) val username: String,
    @MaxLength(100) val bio: String,
    @Exclude val internalId: String
) {
    @HttpEndpoint("/profile", "GET")
    fun getProfile(): Map<String, String> {
        return mapOf("username" to username, "bio" to bio)
    }
    
    @HttpEndpoint("/profile", "PUT")
    fun updateProfile(newBio: String) {
        // ์—…๋ฐ์ดํŠธ ๋กœ์ง
    }
}

๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ์• ๋…ธํ…Œ์ด์…˜ ์ฝ๊ธฐ ๐Ÿ”

์ด์ œ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์• ๋…ธํ…Œ์ด์…˜ ์ •๋ณด๋ฅผ ์ฝ๊ณ  ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊ฒŒ:

import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.memberProperties

fun main() {
    val userProfileClass = UserProfile::class
    
    // 1. ํ”„๋กœํผํ‹ฐ์˜ ์• ๋…ธํ…Œ์ด์…˜ ํ™•์ธํ•˜๊ธฐ
    println("=== ํ”„๋กœํผํ‹ฐ ์• ๋…ธํ…Œ์ด์…˜ ===")
    userProfileClass.memberProperties.forEach { property ->
        // MaxLength ์• ๋…ธํ…Œ์ด์…˜ ํ™•์ธ
        val maxLengthAnnotation = property.findAnnotation<MaxLength>()
        if (maxLengthAnnotation != null) {
            println("${property.name}: ์ตœ๋Œ€ ๊ธธ์ด๋Š” ${maxLengthAnnotation.length}์ž")
        }
        
        // Exclude ์• ๋…ธํ…Œ์ด์…˜ ํ™•์ธ
        if (property.hasAnnotation<Exclude>()) {
            println("${property.name}: ์ง๋ ฌํ™”์—์„œ ์ œ์™ธ๋จ")
        }
    }
    
    // 2. ํ•จ์ˆ˜์˜ ์• ๋…ธํ…Œ์ด์…˜ ํ™•์ธํ•˜๊ธฐ
    println("\n=== ํ•จ์ˆ˜ ์• ๋…ธํ…Œ์ด์…˜ ===")
    userProfileClass.memberFunctions.forEach { function ->
        val httpEndpoint = function.findAnnotation<HttpEndpoint>()
        if (httpEndpoint != null) {
            println("${function.name}: ${httpEndpoint.method} ${httpEndpoint.path}")
        }
    }
    
    // 3. ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๊ฒ€์ฆ ๋กœ์ง ๊ตฌํ˜„ํ•˜๊ธฐ
    val user = UserProfile(
        username = "verylongusernamethatexceedsthelimit",
        bio = "Hello!",
        internalId = "12345"
    )
    
    validateObject(user)
}

// ๊ฐ์ฒด ๊ฒ€์ฆ ํ•จ์ˆ˜
fun validateObject(obj: Any) {
    println("\n=== ๊ฐ์ฒด ๊ฒ€์ฆ ===")
    val kClass = obj::class
    
    kClass.memberProperties.forEach { property ->
        val maxLengthAnnotation = property.findAnnotation<MaxLength>()
        if (maxLengthAnnotation != null) {
            val value = property.get(obj) as? String
            if (value != null && value.length > maxLengthAnnotation.length) {
                println("๊ฒ€์ฆ ์‹คํŒจ: ${property.name}์˜ ๊ธธ์ด๊ฐ€ ${value.length}๋กœ, ์ตœ๋Œ€ ํ—ˆ์šฉ ๊ธธ์ด ${maxLengthAnnotation.length}๋ฅผ ์ดˆ๊ณผํ•จ")
            }
        }
    }
}

๐Ÿ” ์˜ˆ์‹œ ๊ฒฐ๊ณผ:
=== ํ”„๋กœํผํ‹ฐ ์• ๋…ธํ…Œ์ด์…˜ ===
username: ์ตœ๋Œ€ ๊ธธ์ด๋Š” 50์ž
bio: ์ตœ๋Œ€ ๊ธธ์ด๋Š” 100์ž
internalId: ์ง๋ ฌํ™”์—์„œ ์ œ์™ธ๋จ

=== ํ•จ์ˆ˜ ์• ๋…ธํ…Œ์ด์…˜ ===
getProfile: GET /profile
updateProfile: PUT /profile

=== ๊ฐ์ฒด ๊ฒ€์ฆ ===
๊ฒ€์ฆ ์‹คํŒจ: username์˜ ๊ธธ์ด๊ฐ€ 32๋กœ, ์ตœ๋Œ€ ํ—ˆ์šฉ ๊ธธ์ด 50๋ฅผ ์ดˆ๊ณผํ•จ

์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ํ”„๋ ˆ์ž„์›Œํฌ ๋งŒ๋“ค๊ธฐ ๐Ÿ—๏ธ

์ด์ œ ์• ๋…ธํ…Œ์ด์…˜๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ™œ์šฉํ•ด ๊ฐ„๋‹จํ•œ HTTP ๋ผ์šฐํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ:

// ๊ฐ„๋‹จํ•œ HTTP ๋ผ์šฐํ„ฐ ๊ตฌํ˜„
class SimpleHttpRouter {
    private val routes = mutableMapOf<Pair<String, String>, (Any, Map<String, Any>) -> Any>()
    
    // ์ปจํŠธ๋กค๋Ÿฌ ๋“ฑ๋ก ํ•จ์ˆ˜
    fun registerController(controllerClass: KClass<*>) {
        val instance = controllerClass.constructors.first().call()
        
        controllerClass.memberFunctions.forEach { function ->
            val endpoint = function.findAnnotation<HttpEndpoint>() ?: return@forEach
            
            val routeKey = Pair(endpoint.method, endpoint.path)
            routes[routeKey] = { controller, params ->
                function.call(controller, *params.values.toTypedArray())
            }
            
            println("๋ผ์šฐํŠธ ๋“ฑ๋ก: ${endpoint.method} ${endpoint.path} -> ${function.name}")
        }
    }
    
    // ์š”์ฒญ ์ฒ˜๋ฆฌ ํ•จ์ˆ˜
    fun handleRequest(method: String, path: String, params: Map<String, Any> = emptyMap()): Any? {
        val routeKey = Pair(method, path)
        val handler = routes[routeKey]
        
        return handler?.invoke(instance, params)
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
class UserController {
    @HttpEndpoint("/users", "GET")
    fun getUsers(): List<String> {
        return listOf("User1", "User2", "User3")
    }
    
    @HttpEndpoint("/users/{id}", "GET")
    fun getUser(id: String): String {
        return "User $id details"
    }
    
    @HttpEndpoint("/users", "POST")
    fun createUser(name: String, email: String): String {
        return "User created: $name ($email)"
    }
}

fun main() {
    val router = SimpleHttpRouter()
    router.registerController(UserController::class)
    
    // ์š”์ฒญ ์‹œ๋ฎฌ๋ ˆ์ด์…˜
    val users = router.handleRequest("GET", "/users")
    println("๊ฒฐ๊ณผ: $users")
    
    val userDetails = router.handleRequest("GET", "/users/{id}", mapOf("id" to "42"))
    println("๊ฒฐ๊ณผ: $userDetails")
    
    val createResult = router.handleRequest("POST", "/users", 
                                          mapOf("name" to "์ฝ”ํ‹€๋ฆฐ", "email" to "kotlin@example.com"))
    println("๊ฒฐ๊ณผ: $createResult")
}

์œ„ ์˜ˆ์ œ๋Š” ๋งค์šฐ ๊ฐ„๋‹จํ•˜์ง€๋งŒ, ์ด๋Ÿฐ ๋ฐฉ์‹์œผ๋กœ Spring์ด๋‚˜ Ktor ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์ด ์• ๋…ธํ…Œ์ด์…˜ ๊ธฐ๋ฐ˜ ๋ผ์šฐํŒ…์„ ๊ตฌํ˜„ํ•œ๋‹ค๋Š” ๊ฑธ ์•Œ ์ˆ˜ ์žˆ์–ด. ๋ฆฌํ”Œ๋ ‰์…˜๊ณผ ์• ๋…ธํ…Œ์ด์…˜์˜ ์กฐํ•ฉ์€ ์ •๋ง ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์•ผ!

์• ๋…ธํ…Œ์ด์…˜ ์ •์˜ @Target ์ ์šฉ ๋Œ€์ƒ ์ง€์ • @Retention ์œ ์ง€ ์ •์ฑ… (RUNTIME) annotation class ์• ๋…ธํ…Œ์ด์…˜ ํด๋ž˜์Šค ์„ ์–ธ ์• ๋…ธํ…Œ์ด์…˜ ์‚ฌ์šฉ @MaxLength(50) ํ”„๋กœํผํ‹ฐ์— ์ ์šฉ @HttpEndpoint(...) ํ•จ์ˆ˜์— ์ ์šฉ @Exclude ๋งˆ์ปค ์• ๋…ธํ…Œ์ด์…˜ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ์ฝ๊ธฐ findAnnotation<T>() ํŠน์ • ์• ๋…ธํ…Œ์ด์…˜ ์ฐพ๊ธฐ hasAnnotation<T>() ์• ๋…ธํ…Œ์ด์…˜ ์กด์žฌ ํ™•์ธ annotations ๋ชจ๋“  ์• ๋…ธํ…Œ์ด์…˜ ๊ฐ€์ ธ์˜ค๊ธฐ ์• ๋…ธํ…Œ์ด์…˜๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์˜ ์กฐํ•ฉ

์ด์ œ ์• ๋…ธํ…Œ์ด์…˜๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ•จ๊ป˜ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค์–ด. ๋‹ค์Œ์œผ๋กœ๋Š” ์ œ๋„ค๋ฆญ๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์˜ ๊ด€๊ณ„๋ฅผ ์‚ดํŽด๋ณผ๊ฒŒ! ๐Ÿ’ซ

6. ์ œ๋„ค๋ฆญ๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜ ๐Ÿ’ซ

์ œ๋„ค๋ฆญ์€ ์ฝ”ํ‹€๋ฆฐ ํƒ€์ž… ์‹œ์Šคํ…œ์˜ ๊ฐ•๋ ฅํ•œ ๊ธฐ๋Šฅ์ด์ง€๋งŒ, ๋ฆฌํ”Œ๋ ‰์…˜๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋„์ „๊ณผ์ œ๊ฐ€ ์žˆ์–ด. ์ด๋ฒˆ์—๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊ฒŒ! ๐Ÿงฉ

ํƒ€์ž… ์†Œ๊ฑฐ์™€ ๋„์ „๊ณผ์ œ ๐Ÿค”

์ž๋ฐ”์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ฝ”ํ‹€๋ฆฐ๋„ ์ œ๋„ค๋ฆญ์— ๋Œ€ํ•ด 'ํƒ€์ž… ์†Œ๊ฑฐ(Type Erasure)'๋ฅผ ์‚ฌ์šฉํ•ด. ์ด๋Š” ์ปดํŒŒ์ผ ์‹œ์—๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ •๋ณด๊ฐ€ ์žˆ์ง€๋งŒ, ๋Ÿฐํƒ€์ž„์—๋Š” ๊ทธ ์ •๋ณด๊ฐ€ '์†Œ๊ฑฐ'๋œ๋‹ค๋Š” ์˜๋ฏธ์•ผ. ์ด๋กœ ์ธํ•ด ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ๋‹ค๋ฃฐ ๋•Œ ๋ช‡ ๊ฐ€์ง€ ์ œ์•ฝ์ด ์ƒ๊ฒจ.

// ์ œ๋„ค๋ฆญ ํด๋ž˜์Šค ์˜ˆ์‹œ
class Box<T>(val value: T)

fun main() {
    val stringBox = Box("Hello")
    val intBox = Box(42)
    
    // ๋Ÿฐํƒ€์ž„์—๋Š” ๋‘ ๊ฐ์ฒด์˜ ํด๋ž˜์Šค๊ฐ€ ๋™์ผํ•˜๊ฒŒ ๋ณด์ž„
    println(stringBox::class == intBox::class)  // true
    
    // ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ ์ •๋ณด๋Š” ์†Œ๊ฑฐ๋จ
    println(stringBox::class.java.typeParameters)  // [T]
    println(intBox::class.java.typeParameters)    // [T]
}

์œ„ ์˜ˆ์ œ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, ๋Ÿฐํƒ€์ž„์—๋Š” Box<String>๊ณผ Box<Int>๊ฐ€ ๊ฐ™์€ ํด๋ž˜์Šค๋กœ ์ทจ๊ธ‰๋ผ. ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ T์˜ ์‹ค์ œ ํƒ€์ž… ์ •๋ณด(String, Int)๋Š” ์†Œ๊ฑฐ๋˜์–ด ๋ฒ„๋ ค.

์Šคํƒ€ ํ”„๋กœ์ ์…˜๊ณผ ํƒ€์ž… ํ† ํฐ โญ

์ฝ”ํ‹€๋ฆฐ์—์„œ๋Š” ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ๋‹ค๋ฃฐ ๋•Œ ์Šคํƒ€ ํ”„๋กœ์ ์…˜(*)๊ณผ ํƒ€์ž… ํ† ํฐ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด:

import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance

// ์ œ๋„ค๋ฆญ ํƒ€์ž… ํ† ํฐ ํ™œ์šฉ ์˜ˆ์‹œ
inline fun <reified T : Any> createInstance(): T {
    return T::class.createInstance()
}

// ํƒ€์ž… ํ† ํฐ์„ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ๋ฐ›๋Š” ํ•จ์ˆ˜
fun <T : Any> deserialize(json: String, clazz: KClass<T>): T {
    // JSON์„ ํŒŒ์‹ฑํ•˜์—ฌ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋กœ์ง
    println("${clazz.simpleName} ํƒ€์ž…์œผ๋กœ ์—ญ์ง๋ ฌํ™”: $json")
    
    // ๊ฐ„๋‹จํ•œ ๊ตฌํ˜„์„ ์œ„ํ•ด createInstance ์‚ฌ์šฉ
    return clazz.createInstance()
}

// reified ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ ๋ฒ„์ „
inline fun <reified T : Any> deserializeReified(json: String): T {
    return deserialize(json, T::class)
}

class User(val name: String = "๊ธฐ๋ณธ์ด๋ฆ„", val age: Int = 0)
class Product(val id: String = "๊ธฐ๋ณธID", val price: Double = 0.0)

fun main() {
    // KClass๋ฅผ ์ง์ ‘ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹
    val user = deserialize("""{"name":"์ฝ”ํ‹€๋ฆฐ","age":11}""", User::class)
    println(user)  // User(name="๊ธฐ๋ณธ์ด๋ฆ„", age=0)
    
    // reified ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•œ ๋ฐฉ์‹
    val product = deserializeReified<Product>("""{"id":"kotlin-book","price":29.99}""")
    println(product)  // Product(id="๊ธฐ๋ณธID", price=0.0)
    
    // createInstance ์‚ฌ์šฉ ์˜ˆ์‹œ
    val newUser = createInstance<User>()
    println(newUser)  // User(name="๊ธฐ๋ณธ์ด๋ฆ„", age=0)
}

๐Ÿ’ก ํŒ: reified ํ‚ค์›Œ๋“œ๋Š” ์ธ๋ผ์ธ ํ•จ์ˆ˜์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ์ด ํ‚ค์›Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ์ ์— ์‹ค์ œ ํƒ€์ž… ์ •๋ณด๋ฅผ ์‚ฝ์ž…ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ๋Ÿฐํƒ€์ž„์—๋„ ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ •๋ณด๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

์ œ๋„ค๋ฆญ ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ ๋‹ค๋ฃจ๊ธฐ ๐Ÿงฐ

์ด์ œ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณผ๊ฒŒ:

import kotlin.reflect.full.functions
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.javaType
import kotlin.reflect.jvm.jvmErasure

class Repository<T> {
    private val items = mutableListOf<T>()
    
    fun add(item: T) {
        items.add(item)
    }
    
    fun getAll(): List<T> {
        return items.toList()
    }
    
    fun <R> map(transform: (T) -> R): List<R> {
        return items.map(transform)
    }
}

class GenericHolder {
    val stringList: List<String> = listOf("a", "b", "c")
    val intMap: Map<Int, String> = mapOf(1 to "one", 2 to "two")
    
    fun <T> process(item: T): T {
        return item
    }
}

fun main() {
    val holderClass = GenericHolder::class
    
    // ์ œ๋„ค๋ฆญ ํ”„๋กœํผํ‹ฐ ๋ถ„์„
    println("=== ์ œ๋„ค๋ฆญ ํ”„๋กœํผํ‹ฐ ๋ถ„์„ ===")
    holderClass.memberProperties.forEach { property ->
        println("${property.name}: ${property.returnType}")
        
        // ์ œ๋„ค๋ฆญ ํƒ€์ž…์˜ ์ธ์ž ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
        val typeArguments = property.returnType.arguments
        if (typeArguments.isNotEmpty()) {
            println("  ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ธ์ž:")
            typeArguments.forEach { typeArg ->
                println("  - ${typeArg.type}")
            }
        }
    }
    
    // ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜ ๋ถ„์„
    println("\n=== ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜ ๋ถ„์„ ===")
    val processFunction = holderClass.functions.find { it.name == "process" }
    if (processFunction != null) {
        println("ํ•จ์ˆ˜: ${processFunction.name}")
        println("ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ: ${processFunction.typeParameters.joinToString { it.name }}")
        println("ํŒŒ๋ผ๋ฏธํ„ฐ: ${processFunction.parameters.drop(1).joinToString { "${it.name}: ${it.type}" }}")
        println("๋ฐ˜ํ™˜ ํƒ€์ž…: ${processFunction.returnType}")
    }
    
    // Repository ํด๋ž˜์Šค ๋ถ„์„
    println("\n=== Repository ํด๋ž˜์Šค ๋ถ„์„ ===")
    val repoClass = Repository::class
    println("ํด๋ž˜์Šค ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ: ${repoClass.typeParameters.joinToString { it.name }}")
    
    val getAllFunction = repoClass.functions.find { it.name == "getAll" }
    if (getAllFunction != null) {
        println("getAll() ๋ฐ˜ํ™˜ ํƒ€์ž…: ${getAllFunction.returnType}")
        println("๋ฐ˜ํ™˜ ํƒ€์ž…์˜ JVM ์†Œ๊ฑฐ ํƒ€์ž…: ${getAllFunction.returnType.jvmErasure}")
        println("๋ฐ˜ํ™˜ ํƒ€์ž…์˜ Java ํƒ€์ž…: ${getAllFunction.returnType.javaType}")
    }
}

๐Ÿ” ์˜ˆ์‹œ ๊ฒฐ๊ณผ:
=== ์ œ๋„ค๋ฆญ ํ”„๋กœํผํ‹ฐ ๋ถ„์„ ===
stringList: kotlin.collections.List<kotlin.String>
์ œ๋„ค๋ฆญ ํƒ€์ž… ์ธ์ž:
- kotlin.String
intMap: kotlin.collections.Map<kotlin.Int, kotlin.String>
์ œ๋„ค๋ฆญ ํƒ€์ž… ์ธ์ž:
- kotlin.Int
- kotlin.String

=== ์ œ๋„ค๋ฆญ ํ•จ์ˆ˜ ๋ถ„์„ ===
ํ•จ์ˆ˜: process
ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ: T
ํŒŒ๋ผ๋ฏธํ„ฐ: item: T
๋ฐ˜ํ™˜ ํƒ€์ž…: T

=== Repository ํด๋ž˜์Šค ๋ถ„์„ ===
ํด๋ž˜์Šค ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ: T
getAll() ๋ฐ˜ํ™˜ ํƒ€์ž…: kotlin.collections.List<T>
๋ฐ˜ํ™˜ ํƒ€์ž…์˜ JVM ์†Œ๊ฑฐ ํƒ€์ž…: kotlin.collections.List
๋ฐ˜ํ™˜ ํƒ€์ž…์˜ Java ํƒ€์ž…: java.util.List

์ œ๋„ค๋ฆญ ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค๋ฃจ๊ธฐ ๐Ÿ›ก๏ธ

์ œ๋„ค๋ฆญ๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ๋•Œ๋Š” ํƒ€์ž… ์•ˆ์ „์„ฑ์„ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•ด. ๋‹ค์Œ์€ ํƒ€์ž… ์•ˆ์ „ํ•œ ๋ฐฉ์‹์œผ๋กœ ์ œ๋„ค๋ฆญ์„ ๋‹ค๋ฃจ๋Š” ์˜ˆ์‹œ์•ผ:

import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf
import kotlin.reflect.full.isSuperclassOf

// ํƒ€์ž… ์•ˆ์ „ํ•œ ์ปจํ…Œ์ด๋„ˆ ํด๋ž˜์Šค
class TypeSafeMap {
    private val map = mutableMapOf<KClass<*>, Any>()
    
    // ๊ฐ’ ์ €์žฅ (ํƒ€์ž… ์•ˆ์ „)
    fun <T : Any> set(clazz: KClass<T>, value: T) {
        map[clazz] = value
    }
    
    // ๊ฐ’ ์กฐํšŒ (ํƒ€์ž… ์•ˆ์ „)
    @Suppress("UNCHECKED_CAST")
    fun <T : Any> get(clazz: KClass<T>): T? {
        val value = map[clazz]
        
        // ํƒ€์ž… ๊ฒ€์‚ฌ
        if (value != null && clazz.isInstance(value)) {
            return value as T
        }
        return null
    }
    
    // ์ƒ์† ๊ด€๊ณ„๊นŒ์ง€ ๊ณ ๋ คํ•œ ์กฐํšŒ
    @Suppress("UNCHECKED_CAST")
    fun <T : Any> getCompatible(clazz: KClass<T>): T? {
        // ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ํƒ€์ž…์ด ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜
        val exactMatch = get(clazz)
        if (exactMatch != null) return exactMatch
        
        // ํ˜ธํ™˜ ๊ฐ€๋Šฅํ•œ ํƒ€์ž… ์ฐพ๊ธฐ
        for ((storedClass, value) in map) {
            if (storedClass.isSubclassOf(clazz) && clazz.isInstance(value)) {
                return value as T
            }
        }
        
        return null
    }
}

// ํ…Œ์ŠคํŠธ์šฉ ํด๋ž˜์Šค ๊ณ„์ธต
open class Animal
class Dog : Animal()
class Cat : Animal()

fun main() {
    val container = TypeSafeMap()
    
    // ๋‹ค์–‘ํ•œ ํƒ€์ž…์˜ ๊ฐ์ฒด ์ €์žฅ
    container.set(String::class, "Hello Kotlin")
    container.set(Int::class, 42)
    container.set(Dog::class, Dog())
    
    // ํƒ€์ž… ์•ˆ์ „ํ•˜๊ฒŒ ์กฐํšŒ
    val str = container.get(String::class)
    println("String ๊ฐ’: $str")  // String ๊ฐ’: Hello Kotlin
    
    val num = container.get(Int::class)
    println("Int ๊ฐ’: $num")  // Int ๊ฐ’: 42
    
    val dog = container.get(Dog::class)
    println("Dog ๊ฐ์ฒด: ${dog?.javaClass?.simpleName}")  // Dog ๊ฐ์ฒด: Dog
    
    // ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํƒ€์ž…
    val cat = container.get(Cat::class)
    println("Cat ๊ฐ์ฒด: $cat")  // Cat ๊ฐ์ฒด: null
    
    // ์ƒ์† ๊ด€๊ณ„ ํ™œ์šฉ
    val animal = container.getCompatible(Animal::class)
    println("Animal ํ˜ธํ™˜ ๊ฐ์ฒด: ${animal?.javaClass?.simpleName}")  // Animal ํ˜ธํ™˜ ๊ฐ์ฒด: Dog
}
์ปดํŒŒ์ผ ํƒ€์ž„ List<String> List<Int> ๋Ÿฐํƒ€์ž„ (ํƒ€์ž… ์†Œ๊ฑฐ ํ›„) List List ํƒ€์ž… ์†Œ๊ฑฐ ๋ฆฌํ”Œ๋ ‰์…˜ ํ•ด๊ฒฐ์ฑ… reified ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ inline fun <reified T> ... KType.arguments ์ œ๋„ค๋ฆญ ํƒ€์ž… ์ธ์ž ์ •๋ณด ์ ‘๊ทผ ํƒ€์ž… ํ† ํฐ ํŒจํ„ด KClass<T>๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์ „๋‹ฌ ์ œ๋„ค๋ฆญ๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์˜ ๋„์ „๊ณผ์ œ

์ œ๋„ค๋ฆญ๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ดค์–ด. ํƒ€์ž… ์†Œ๊ฑฐ๋ผ๋Š” ์ œ์•ฝ์ด ์žˆ์ง€๋งŒ, reified ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ํƒ€์ž… ํ† ํฐ ํŒจํ„ด์„ ํ™œ์šฉํ•˜๋ฉด ๋งŽ์€ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด. ๋‹ค์Œ์œผ๋กœ๋Š” ์‹ค์ „ ํ™œ์šฉ ์‚ฌ๋ก€๋ฅผ ์‚ดํŽด๋ณผ๊ฒŒ! ๐Ÿ’ผ

7. ์‹ค์ „ ํ™œ์šฉ ์‚ฌ๋ก€ ๋ฐ ํ”„๋กœ์ ํŠธ ๐Ÿ’ผ

์ด์ œ ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API๋ฅผ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๋ช‡ ๊ฐ€์ง€ ์‹ค์ „ ์‚ฌ๋ก€๋ฅผ ํ†ตํ•ด ์•Œ์•„๋ณผ๊ฒŒ. ์ด๋ก ๋งŒ ์•Œ๋ฉด ๋ญํ•ด, ์‹ค์ œ๋กœ ์จ๋จน์–ด์•ผ์ง€! ๐Ÿ› ๏ธ

๊ฐ„๋‹จํ•œ DI(์˜์กด์„ฑ ์ฃผ์ž…) ์ปจํ…Œ์ด๋„ˆ ๋งŒ๋“ค๊ธฐ ๐Ÿ’‰

Spring ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ํ•ต์‹ฌ ๊ธฐ๋Šฅ ์ค‘ ํ•˜๋‚˜์ธ ์˜์กด์„ฑ ์ฃผ์ž…์„ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•ด๋ณผ๊ฒŒ:

import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.primaryConstructor

// ์˜์กด์„ฑ ์ฃผ์ž… ๊ด€๋ จ ์• ๋…ธํ…Œ์ด์…˜
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Component

@Target(AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class Inject

@Target(AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class Value(val value: String)

// ๊ฐ„๋‹จํ•œ DI ์ปจํ…Œ์ด๋„ˆ
class DIContainer {
    private val components = mutableMapOf<KClass<*>, Any>()
    
    // ์ปดํฌ๋„ŒํŠธ ๋“ฑ๋ก
    fun <T : Any> register(clazz: KClass<T>, instance: T) {
        components[clazz] = instance
    }
    
    // ์ปดํฌ๋„ŒํŠธ ์กฐํšŒ
    @Suppress("UNCHECKED_CAST")
    fun <T : Any> get(clazz: KClass<T>): T {
        // ์ด๋ฏธ ๋“ฑ๋ก๋œ ์ธ์Šคํ„ด์Šค๊ฐ€ ์žˆ์œผ๋ฉด ๋ฐ˜ํ™˜
        components[clazz]?.let { return it as T }
        
        // ํด๋ž˜์Šค์— @Component ์• ๋…ธํ…Œ์ด์…˜์ด ์žˆ๋Š”์ง€ ํ™•์ธ
        if (clazz.findAnnotation<Component>() == null) {
            throw IllegalArgumentException("${clazz.simpleName}๋Š” @Component๋กœ ๋“ฑ๋ก๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.")
        }
        
        // ์ƒ์„ฑ์ž ์ฐพ๊ธฐ
        val constructor = clazz.primaryConstructor
            ?: throw IllegalArgumentException("${clazz.simpleName}์— ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
        
        // ์ƒ์„ฑ์ž ํŒŒ๋ผ๋ฏธํ„ฐ ๋ถ„์„
        val parameters = constructor.parameters
        val arguments = parameters.map { param ->
            // @Value ์• ๋…ธํ…Œ์ด์…˜์ด ์žˆ์œผ๋ฉด ํ•ด๋‹น ๊ฐ’ ์‚ฌ์šฉ
            param.findAnnotation<Value>()?.let { return@map it.value }
            
            // ์•„๋‹ˆ๋ฉด ํƒ€์ž…์— ๋งž๋Š” ์ปดํฌ๋„ŒํŠธ ์กฐํšŒ
            val paramType = param.type.classifier as? KClass<*>
                ?: throw IllegalArgumentException("${param.name}์˜ ํƒ€์ž…์„ ๊ฒฐ์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
            
            // ์žฌ๊ท€์ ์œผ๋กœ ์˜์กด์„ฑ ํ•ด๊ฒฐ
            get(paramType)
        }.toTypedArray()
        
        // ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋ฐ ๋“ฑ๋ก
        val instance = constructor.call(*arguments)
        register(clazz, instance)
        return instance
    }
    
    // ํƒ€์ž… ์•ˆ์ „ํ•œ ๋ฒ„์ „
    inline fun <reified T : Any> get(): T = get(T::class)
    
    // ์ปดํฌ๋„ŒํŠธ ์Šค์บ” (ํŒจํ‚ค์ง€ ๋‚ด ๋ชจ๋“  @Component ํด๋ž˜์Šค ๋“ฑ๋ก)
    fun scanComponents(packageName: String) {
        // ์‹ค์ œ ๊ตฌํ˜„์—์„œ๋Š” ํด๋ž˜์ŠคํŒจ์Šค ์Šค์บ๋‹ ๋กœ์ง์ด ํ•„์š”ํ•จ
        println("$packageName ํŒจํ‚ค์ง€์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์Šค์บ”ํ•ฉ๋‹ˆ๋‹ค...")
        // ์—ฌ๊ธฐ์„œ๋Š” ์˜ˆ์‹œ๋กœ ๋ช‡ ๊ฐ€์ง€ ํด๋ž˜์Šค๋งŒ ๋“ฑ๋ก
    }
}

// ํ…Œ์ŠคํŠธ์šฉ ํด๋ž˜์Šค๋“ค
@Component
class UserRepository {
    fun findAll(): List<String> = listOf("User1", "User2", "User3")
}

@Component
class UserService(private val repository: UserRepository) {
    fun getUsers(): List<String> = repository.findAll()
}

@Component
class UserController(
    private val service: UserService,
    @Value("api/v1") private val apiPath: String
) {
    fun listUsers(): String {
        return "$apiPath/users: ${service.getUsers().joinToString()}"
    }
}

fun main() {
    val container = DIContainer()
    
    // ์ˆ˜๋™ ๋“ฑ๋ก
    container.register(UserRepository::class, UserRepository())
    
    // ์ž๋™ ์˜์กด์„ฑ ์ฃผ์ž…์œผ๋กœ ์ธ์Šคํ„ด์Šค ์–ป๊ธฐ
    val controller = container.get<UserController>()
    println(controller.listUsers())  // ์ถœ๋ ฅ: api/v1/users: User1, User2, User3
    
    // ์ปดํฌ๋„ŒํŠธ ์Šค์บ” (์‹ค์ œ ๊ตฌํ˜„์—์„œ๋Š” ํŒจํ‚ค์ง€ ๋‚ด ๋ชจ๋“  @Component ํด๋ž˜์Šค๋ฅผ ์ฐพ์•„ ๋“ฑ๋ก)
    container.scanComponents("com.example")
}

์œ„ ์˜ˆ์ œ๋Š” Spring์˜ DI ์ปจํ…Œ์ด๋„ˆ๋ฅผ ๋งค์šฐ ๊ฐ„์†Œํ™”ํ•œ ๋ฒ„์ „์ด์•ผ. ์‹ค์ œ Spring์€ ํ›จ์”ฌ ๋” ๋ณต์žกํ•˜๊ณ  ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ ์›๋ฆฌ๋Š” ์ด๋ ‡๊ฒŒ ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ™œ์šฉํ•ด ๊ฐ์ฒด์˜ ์˜์กด์„ฑ์„ ์ž๋™์œผ๋กœ ์ฃผ์ž…ํ•˜๋Š” ๊ฑฐ์•ผ.

JSON ์ง๋ ฌํ™”/์—ญ์ง๋ ฌํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋งŒ๋“ค๊ธฐ ๐Ÿ“ฆ

Jackson์ด๋‚˜ Gson ๊ฐ™์€ JSON ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ JSON ๋ณ€ํ™˜ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณผ๊ฒŒ:

import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor

// JSON ์ง๋ ฌํ™” ์ œ์™ธ ์• ๋…ธํ…Œ์ด์…˜
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class JsonExclude

// ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„ ๋ณ€๊ฒฝ ์• ๋…ธํ…Œ์ด์…˜
@Target(AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.RUNTIME)
annotation class JsonProperty(val name: String)

// ๊ฐ„๋‹จํ•œ JSON ๋ณ€ํ™˜๊ธฐ
class SimpleJsonConverter {
    // ๊ฐ์ฒด๋ฅผ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
    fun toJson(obj: Any): String {
        val kClass = obj::class
        val properties = kClass.memberProperties
        
        val jsonProps = properties
            .filter { prop -> prop.findAnnotation<JsonExclude>() == null }
            .map { prop ->
                val jsonName = prop.findAnnotation<JsonProperty>()?.name ?: prop.name
                val value = prop.get(obj)
                "\"$jsonName\": ${valueToJson(value)}"
            }
        
        return "{${jsonProps.joinToString(", ")}}"
    }
    
    // ๊ฐ’์„ JSON ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜
    private fun valueToJson(value: Any?): String {
        return when (value) {
            null -> "null"
            is String -> "\"$value\""
            is Number, is Boolean -> value.toString()
            is Collection<*> -> "[${value.joinToString(", ") { valueToJson(it) }}]"
            is Map<*, *> -> {
                val entries = value.entries.joinToString(", ") { (k, v) ->
                    "\"$k\": ${valueToJson(v)}"
                }
                "{$entries}"
            }
            else -> toJson(value)  // ์ค‘์ฒฉ ๊ฐ์ฒด
        }
    }
    
    // JSON ๋ฌธ์ž์—ด์„ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜ (๊ฐ„์†Œํ™”๋œ ๋ฒ„์ „)
    inline fun <reified T : Any> fromJson(json: String): T {
        return fromJson(json, T::class)
    }
    
    // ์‹ค์ œ ์—ญ์ง๋ ฌํ™” ๊ตฌํ˜„ (๋งค์šฐ ๊ฐ„์†Œํ™”๋จ)
    fun <T : Any> fromJson(json: String, clazz: KClass<T>): T {
        // ์‹ค์ œ ๊ตฌํ˜„์—์„œ๋Š” JSON ํŒŒ์‹ฑ ๋กœ์ง์ด ํ•„์š”ํ•จ
        println("JSON ํŒŒ์‹ฑ ์ค‘: $json -> ${clazz.simpleName}")
        
        // ์—ฌ๊ธฐ์„œ๋Š” ์˜ˆ์‹œ๋ฅผ ์œ„ํ•ด ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋กœ ์ธ์Šคํ„ด์Šค๋งŒ ์ƒ์„ฑ
        val constructor = clazz.primaryConstructor
            ?: throw IllegalArgumentException("${clazz.simpleName}์— ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.")
        
        // ๋ชจ๋“  ํŒŒ๋ผ๋ฏธํ„ฐ์— ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ (์‹ค์ œ๋กœ๋Š” JSON์—์„œ ๊ฐ’์„ ์ถ”์ถœํ•ด์•ผ ํ•จ)
        val instance = constructor.callBy(emptyMap())
        
        // ์‹ค์ œ ๊ตฌํ˜„์—์„œ๋Š” ์—ฌ๊ธฐ์„œ JSON ๊ฐ’์„ ๊ฐ์ฒด์˜ ํ”„๋กœํผํ‹ฐ์— ์„ค์ •ํ•ด์•ผ ํ•จ
        return instance
    }
}

// ํ…Œ์ŠคํŠธ์šฉ ํด๋ž˜์Šค
data class Person(
    val name: String,
    val age: Int,
    @JsonProperty("emailAddress") val email: String,
    @JsonExclude val password: String
)

data class Team(
    val name: String,
    val members: List<Person>,
    val stats: Map<String, Int>
)

fun main() {
    val converter = SimpleJsonConverter()
    
    // ๊ฐ์ฒด -> JSON
    val person = Person("์ฝ”ํ‹€๋ฆฐ", 11, "kotlin@example.com", "secret123")
    val personJson = converter.toJson(person)
    println("Person JSON: $personJson")
    
    // ๋ณต์žกํ•œ ๊ฐ์ฒด -> JSON
    val team = Team(
        "์ฝ”ํ‹€๋ฆฐ ํŒ€",
        listOf(
            Person("์ฝ”ํ‹€๋ฆฐ", 11, "kotlin@example.com", "pass1"),
            Person("์ž๋ฐ”", 26, "java@example.com", "pass2")
        ),
        mapOf("wins" to 10, "losses" to 2)
    )
    val teamJson = converter.toJson(team)
    println("Team JSON: $teamJson")
    
    // JSON -> ๊ฐ์ฒด (์‹ค์ œ ๊ตฌํ˜„์—์„œ๋Š” JSON ํŒŒ์‹ฑ ํ›„ ๊ฐ์ฒด ์ƒ์„ฑ)
    val newPerson = converter.fromJson<Person>(personJson)
    println("์—ญ์ง๋ ฌํ™”๋œ Person: $newPerson")
}

๐Ÿ” ์˜ˆ์‹œ ๊ฒฐ๊ณผ:
Person JSON: {"name": "์ฝ”ํ‹€๋ฆฐ", "age": 11, "emailAddress": "kotlin@example.com"}
Team JSON: {"name": "์ฝ”ํ‹€๋ฆฐ ํŒ€", "members": [{"name": "์ฝ”ํ‹€๋ฆฐ", "age": 11, "emailAddress": "kotlin@example.com"}, {"name": "์ž๋ฐ”", "age": 26, "emailAddress": "java@example.com"}], "stats": {"wins": 10, "losses": 2}}
JSON ํŒŒ์‹ฑ ์ค‘: {"name": "์ฝ”ํ‹€๋ฆฐ", "age": 11, "emailAddress": "kotlin@example.com"} -> Person
์—ญ์ง๋ ฌํ™”๋œ Person: Person(name="", age=0, email="", password="")

์œ„ ์˜ˆ์ œ๋Š” ์‹ค์ œ JSON ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ณด๋‹ค ํ›จ์”ฌ ๋‹จ์ˆœํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ ์›๋ฆฌ๋Š” ๋™์ผํ•ด. ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ๊ฐ์ฒด์˜ ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ , ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ์ถ”์ถœํ•˜๊ฑฐ๋‚˜ ์„ค์ •ํ•˜๋Š” ๊ฑฐ์ง€.

ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ๊ตฌํ˜„ํ•˜๊ธฐ ๐Ÿงช

JUnit์ด๋‚˜ Kotest ๊ฐ™์€ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ:

import kotlin.reflect.KClass
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.full.functions
import kotlin.reflect.full.hasAnnotation
import kotlin.reflect.full.memberFunctions

// ํ…Œ์ŠคํŠธ ๊ด€๋ จ ์• ๋…ธํ…Œ์ด์…˜
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class TestClass

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class Test

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class BeforeEach

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class AfterEach

// ๊ฐ„๋‹จํ•œ ํ…Œ์ŠคํŠธ ๋Ÿฌ๋„ˆ
class SimpleTestRunner {
    fun runTests(testClass: KClass<*>) {
        // ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ํ™•์ธ
        if (!testClass.hasAnnotation<TestClass>()) {
            println("${testClass.simpleName}๋Š” @TestClass ์• ๋…ธํ…Œ์ด์…˜์ด ์—†์Šต๋‹ˆ๋‹ค.")
            return
        }
        
        println("ํ…Œ์ŠคํŠธ ์‹œ์ž‘: ${testClass.simpleName}")
        
        // ํ…Œ์ŠคํŠธ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
        val testInstance = testClass.constructors.first().call()
        
        // ๋ชจ๋“  ํ•จ์ˆ˜ ์ฐพ๊ธฐ
        val allFunctions = testClass.memberFunctions
        val beforeEachFunctions = allFunctions.filter { it.hasAnnotation<BeforeEach>() }
        val testFunctions = allFunctions.filter { it.hasAnnotation<Test>() }
        val afterEachFunctions = allFunctions.filter { it.hasAnnotation<AfterEach>() }
        
        var passed = 0
        var failed = 0
        
        // ๊ฐ ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜ ์‹คํ–‰
        for (testFunction in testFunctions) {
            println("  ์‹คํ–‰ ์ค‘: ${testFunction.name}")
            
            try {
                // BeforeEach ํ•จ์ˆ˜ ์‹คํ–‰
                for (before in beforeEachFunctions) {
                    before.call(testInstance)
                }
                
                // ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜ ์‹คํ–‰
                testFunction.call(testInstance)
                
                // ํ…Œ์ŠคํŠธ ์„ฑ๊ณต
                println("    โœ… ์„ฑ๊ณต")
                passed++
            } catch (e: Exception) {
                // ํ…Œ์ŠคํŠธ ์‹คํŒจ
                println("    โŒ ์‹คํŒจ: ${e.message}")
                failed++
            } finally {
                // AfterEach ํ•จ์ˆ˜ ์‹คํ–‰
                for (after in afterEachFunctions) {
                    try {
                        after.call(testInstance)
                    } catch (e: Exception) {
                        println("    โš ๏ธ AfterEach ์‹คํŒจ: ${e.message}")
                    }
                }
            }
        }
        
        // ๊ฒฐ๊ณผ ์ถœ๋ ฅ
        println("ํ…Œ์ŠคํŠธ ์™„๋ฃŒ: ์„ฑ๊ณต $passed, ์‹คํŒจ $failed")
    }
    
    // ํƒ€์ž… ์•ˆ์ „ํ•œ ๋ฒ„์ „
    inline fun <reified T : Any> runTests() = runTests(T::class)
}

// ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค ์˜ˆ์‹œ
@TestClass
class CalculatorTest {
    private lateinit var calculator: Calculator
    
    @BeforeEach
    fun setup() {
        calculator = Calculator()
        println("    ๐Ÿ”ง Calculator ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ๋จ")
    }
    
    @Test
    fun testAddition() {
        val result = calculator.add(2, 3)
        if (result != 5) {
            throw AssertionError("2 + 3 = 5์—ฌ์•ผ ํ•˜๋Š”๋ฐ, $result๊ฐ€ ๋ฐ˜ํ™˜๋จ")
        }
    }
    
    @Test
    fun testMultiplication() {
        val result = calculator.multiply(2, 3)
        if (result != 6) {
            throw AssertionError("2 * 3 = 6์ด์–ด์•ผ ํ•˜๋Š”๋ฐ, $result๊ฐ€ ๋ฐ˜ํ™˜๋จ")
        }
    }
    
    @Test
    fun testDivision() {
        val result = calculator.divide(6, 2)
        if (result != 3) {
            throw AssertionError("6 / 2 = 3์ด์–ด์•ผ ํ•˜๋Š”๋ฐ, $result๊ฐ€ ๋ฐ˜ํ™˜๋จ")
        }
    }
    
    @Test
    fun testDivisionByZero() {
        try {
            calculator.divide(6, 0)
            throw AssertionError("0์œผ๋กœ ๋‚˜๋ˆ„๊ธฐ ์‹œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•จ")
        } catch (e: ArithmeticException) {
            // ์˜ˆ์ƒ๋Œ€๋กœ ์˜ˆ์™ธ ๋ฐœ์ƒ
        }
    }
    
    @AfterEach
    fun cleanup() {
        println("    ๐Ÿงน ํ…Œ์ŠคํŠธ ์ •๋ฆฌ ์™„๋ฃŒ")
    }
}

// ํ…Œ์ŠคํŠธ ๋Œ€์ƒ ํด๋ž˜์Šค
class Calculator {
    fun add(a: Int, b: Int): Int = a + b
    fun multiply(a: Int, b: Int): Int = a * b
    fun divide(a: Int, b: Int): Int = a / b  // 0์œผ๋กœ ๋‚˜๋ˆ„๋ฉด ArithmeticException ๋ฐœ์ƒ
}

fun main() {
    val runner = SimpleTestRunner()
    runner.runTests<CalculatorTest>()
}

์ด ์˜ˆ์ œ๋Š” JUnit ๊ฐ™์€ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ๋ณด์—ฌ์ค˜. ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ํŠน์ • ์• ๋…ธํ…Œ์ด์…˜์ด ๋ถ™์€ ๋ฉ”์†Œ๋“œ๋ฅผ ์ฐพ์•„ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ์‹์ด์•ผ. ์‹ค์ œ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ํ›จ์”ฌ ๋” ๋งŽ์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๊ธฐ๋ณธ ์›๋ฆฌ๋Š” ์ด์™€ ๊ฐ™์•„.

๐Ÿ’ก ์ฐธ๊ณ : ์œ„ ์˜ˆ์ œ๋“ค์€ ๊ต์œก ๋ชฉ์ ์œผ๋กœ ๋งค์šฐ ๋‹จ์ˆœํ™”๋œ ๋ฒ„์ „์ด์•ผ. ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ๋Š” Spring, Jackson, JUnit ๊ฐ™์€ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„. ํ•˜์ง€๋งŒ ์ด๋Ÿฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์ด ๋‚ด๋ถ€์ ์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋  ๊ฑฐ์•ผ!

์‹ค์ „ ํ™œ์šฉ ์‚ฌ๋ก€๋ฅผ ํ†ตํ•ด ๋ฆฌํ”Œ๋ ‰์…˜์ด ์–ผ๋งˆ๋‚˜ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ธ์ง€ ์•Œ์•„๋ดค์–ด. ํ•˜์ง€๋งŒ ๊ฐ•๋ ฅํ•œ ๋งŒํผ ์ฃผ์˜ํ•ด์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ๋‹ค์Œ์œผ๋กœ๋Š” ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ ์‹œ ์„ฑ๋Šฅ๊ณผ ์ฃผ์˜์‚ฌํ•ญ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ๊ฒŒ! โš ๏ธ

8. ์„ฑ๋Šฅ๊ณผ ์ฃผ์˜์‚ฌํ•ญ โš ๏ธ

๋ฆฌํ”Œ๋ ‰์…˜์€ ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ง€๋งŒ, ๊ทธ๋งŒํผ ์‹ ์ค‘ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ์ด๋ฒˆ์—๋Š” ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ ์‹œ ๊ณ ๋ คํ•ด์•ผ ํ•  ์„ฑ๋Šฅ ๋ฌธ์ œ์™€ ์ฃผ์˜์‚ฌํ•ญ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ๊ฒŒ! ๐Ÿšง

์„ฑ๋Šฅ ์˜ํ–ฅ ๐Ÿ“‰

๋ฆฌํ”Œ๋ ‰์…˜์€ ์ผ๋ฐ˜์ ์ธ ์ง์ ‘ ์ฝ”๋“œ ํ˜ธ์ถœ๋ณด๋‹ค ๋А๋ ค. ๊ทธ ์ด์œ ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„:

  1. ๋™์  ํ•ด์„ ์˜ค๋ฒ„ํ—ค๋“œ - ๋ฆฌํ”Œ๋ ‰์…˜์€ ๋Ÿฐํƒ€์ž„์— ํƒ€์ž…๊ณผ ๋ฉ”์†Œ๋“œ๋ฅผ ํ•ด์„ํ•ด์•ผ ํ•ด์„œ ์ถ”๊ฐ€ ์ž‘์—…์ด ํ•„์š”ํ•ด.
  2. ์ตœ์ ํ™” ์ œํ•œ - ์ปดํŒŒ์ผ๋Ÿฌ์™€ JVM์ด ์ผ๋ฐ˜ ์ฝ”๋“œ์— ์ ์šฉํ•˜๋Š” ๋งŽ์€ ์ตœ์ ํ™”๊ฐ€ ๋ฆฌํ”Œ๋ ‰์…˜์—๋Š” ์ ์šฉ๋˜์ง€ ์•Š์•„.
  3. ๋ณด์•ˆ ๊ฒ€์‚ฌ - ์ ‘๊ทผ ์ œ์–ด(private ๋ฉค๋ฒ„ ์ ‘๊ทผ ๋“ฑ)๋ฅผ ์šฐํšŒํ•  ๋•Œ ์ถ”๊ฐ€ ๋ณด์•ˆ ๊ฒ€์‚ฌ๊ฐ€ ํ•„์š”ํ•ด.
  4. ์บ์‹œ ๋ถ€์žฌ - ๋ฆฌํ”Œ๋ ‰์…˜ ์ •๋ณด๋ฅผ ๋งค๋ฒˆ ์กฐํšŒํ•˜๋ฉด ์„ฑ๋Šฅ์ด ์ €ํ•˜๋ผ.
import kotlin.reflect.full.memberFunctions
import kotlin.system.measureTimeMillis

class PerformanceTest {
    fun directMethod(iterations: Int): Int {
        var sum = 0
        for (i in 0 until iterations) {
            sum += i
        }
        return sum
    }
}

fun main() {
    val test = PerformanceTest()
    val iterations = 1_000_000
    
    // ์ง์ ‘ ํ˜ธ์ถœ ์„ฑ๋Šฅ ์ธก์ •
    val directTime = measureTimeMillis {
        for (i in 0..100) {
            test.directMethod(iterations)
        }
    }
    println("์ง์ ‘ ํ˜ธ์ถœ ์‹œ๊ฐ„: $directTime ms")
    
    // ๋ฆฌํ”Œ๋ ‰์…˜ ํ˜ธ์ถœ ์„ฑ๋Šฅ ์ธก์ •
    val method = PerformanceTest::class.memberFunctions.first { it.name == "directMethod" }
    val reflectionTime = measureTimeMillis {
        for (i in 0..100) {
            method.call(test, iterations)
        }
    }
    println("๋ฆฌํ”Œ๋ ‰์…˜ ํ˜ธ์ถœ ์‹œ๊ฐ„: $reflectionTime ms")
    
    // ์ฐจ์ด ๊ณ„์‚ฐ
    val ratio = reflectionTime.toDouble() / directTime
    println("๋ฆฌํ”Œ๋ ‰์…˜์€ ์ง์ ‘ ํ˜ธ์ถœ๋ณด๋‹ค ์•ฝ ${String.format("%.2f", ratio)}๋ฐฐ ๋А๋ฆผ")
}

๐Ÿ” ์˜ˆ์‹œ ๊ฒฐ๊ณผ:
์ง์ ‘ ํ˜ธ์ถœ ์‹œ๊ฐ„: 124 ms
๋ฆฌํ”Œ๋ ‰์…˜ ํ˜ธ์ถœ ์‹œ๊ฐ„: 1872 ms
๋ฆฌํ”Œ๋ ‰์…˜์€ ์ง์ ‘ ํ˜ธ์ถœ๋ณด๋‹ค ์•ฝ 15.10๋ฐฐ ๋А๋ฆผ

์„ฑ๋Šฅ ๊ฐœ์„  ๋ฐฉ๋ฒ• ๐Ÿš€

๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์–ด:

import kotlin.reflect.KClass
import kotlin.reflect.KFunction
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberFunctions
import kotlin.reflect.full.memberProperties
import kotlin.system.measureTimeMillis

class ReflectionOptimization {
    // ๋ฆฌํ”Œ๋ ‰์…˜ ์ •๋ณด ์บ์‹ฑ
    class ReflectionCache<T : Any>(private val kClass: KClass<T>) {
        // ํ”„๋กœํผํ‹ฐ ์บ์‹œ
        private val propertyCache = mutableMapOf<String, KProperty1<T, *>>()
        
        // ํ•จ์ˆ˜ ์บ์‹œ
        private val functionCache = mutableMapOf<String, KFunction<*>>()
        
        // ํ”„๋กœํผํ‹ฐ ์กฐํšŒ (์บ์‹œ ํ™œ์šฉ)
        fun getProperty(name: String): KProperty1<T, *>? {
            return propertyCache.getOrPut(name) {
                kClass.memberProperties.find { it.name == name } ?: return null
            }
        }
        
        // ํ•จ์ˆ˜ ์กฐํšŒ (์บ์‹œ ํ™œ์šฉ)
        fun getFunction(name: String): KFunction<*>? {
            return functionCache.getOrPut(name) {
                kClass.memberFunctions.find { it.name == name } ?: return null
            }
        }
    }
    
    // ํ…Œ์ŠคํŠธ์šฉ ํด๋ž˜์Šค
    class User(val name: String, val age: Int) {
        fun greet(): String = "์•ˆ๋…•, ๋‚˜๋Š” $name์ด๊ณ , $age์‚ด์ด์•ผ."
        fun calculateBirthYear(currentYear: Int): Int = currentYear - age
    }
    
    fun main() {
        val user = User("์ฝ”ํ‹€๋ฆฐ", 11)
        val iterations = 10_000
        
        // ์บ์‹œ ์—†์ด ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ
        val noCacheTime = measureTimeMillis {
            for (i in 0 until iterations) {
                val nameProperty = User::class.memberProperties.find { it.name == "name" }
                val greetFunction = User::class.memberFunctions.find { it.name == "greet" }
                
                nameProperty?.get(user)
                greetFunction?.call(user)
            }
        }
        println("์บ์‹œ ์—†๋Š” ๋ฆฌํ”Œ๋ ‰์…˜ ์‹œ๊ฐ„: $noCacheTime ms")
        
        // ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•œ ๋ฆฌํ”Œ๋ ‰์…˜
        val cache = ReflectionCache(User::class)
        val cacheTime = measureTimeMillis {
            for (i in 0 until iterations) {
                val nameProperty = cache.getProperty("name")
                val greetFunction = cache.getFunction("greet")
                
                nameProperty?.get(user)
                greetFunction?.call(user)
            }
        }
        println("์บ์‹œ ์‚ฌ์šฉ ๋ฆฌํ”Œ๋ ‰์…˜ ์‹œ๊ฐ„: $cacheTime ms")
        
        // ๊ฐœ์„ ์œจ ๊ณ„์‚ฐ
        val improvement = (noCacheTime - cacheTime) * 100.0 / noCacheTime
        println("์„ฑ๋Šฅ ๊ฐœ์„ : ${String.format("%.2f", improvement)}%")
    }
}

์„ฑ๋Šฅ์„ ๊ฐœ์„ ํ•˜๋Š” ์ฃผ์š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•„:

  1. ๋ฆฌํ”Œ๋ ‰์…˜ ์ •๋ณด ์บ์‹ฑ - ํด๋ž˜์Šค, ๋ฉ”์†Œ๋“œ, ํ”„๋กœํผํ‹ฐ ์ •๋ณด๋ฅผ ํ•œ ๋ฒˆ๋งŒ ์กฐํšŒํ•˜๊ณ  ์žฌ์‚ฌ์šฉํ•ด.
  2. ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์‚ฌ์šฉ - ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ๋ฃจํ”„๋‚˜ ํ•ซ ๊ฒฝ๋กœ์—์„œ๋Š” ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ์„ ํ”ผํ•ด.
  3. ๋ฐ”์ธ๋”ฉ๋œ ์ฐธ์กฐ ์‚ฌ์šฉ - ๊ฐ€๋Šฅํ•˜๋ฉด KFunction.call() ๋Œ€์‹  ๋ฐ”์ธ๋”ฉ๋œ ํ•จ์ˆ˜ ์ฐธ์กฐ๋ฅผ ์‚ฌ์šฉํ•ด.
  4. ์ปดํŒŒ์ผ ํƒ€์ž„ ๋Œ€์•ˆ ๊ณ ๋ ค - ์ฝ”ํ‹€๋ฆฐ์˜ ์œ„์ž„์ด๋‚˜ ์ธ๋ผ์ธ ํ•จ์ˆ˜ ๊ฐ™์€ ์ปดํŒŒ์ผ ํƒ€์ž„ ๊ธฐ๋Šฅ์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ๊ฒ€ํ† ํ•ด.

์ฃผ์˜์‚ฌํ•ญ ๋ฐ ๋ชจ๋ฒ” ์‚ฌ๋ก€ โš ๏ธ

๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•  ๋•Œ ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ๋“ค์ด์•ผ:

โš ๏ธ ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์‚ฌํ•ญ

  1. ์บก์Аํ™” ์œ„๋ฐ˜ - private ๋ฉค๋ฒ„์— ์ ‘๊ทผํ•˜๋ฉด ํด๋ž˜์Šค ์„ค๊ณ„์ž์˜ ์˜๋„๋ฅผ ๋ฌด์‹œํ•˜๊ฒŒ ๋ผ.
  2. ํƒ€์ž… ์•ˆ์ „์„ฑ ์†์‹ค - ์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž… ๊ฒ€์‚ฌ๋ฅผ ์šฐํšŒํ•˜๋ฏ€๋กœ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„์ ธ.
  3. ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ์ €ํ•˜ - ๋ฆฌํ”Œ๋ ‰์…˜ ์ฝ”๋“œ๋Š” ์ผ๋ฐ˜ ์ฝ”๋“œ๋ณด๋‹ค ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šธ ์ˆ˜ ์žˆ์–ด.
  4. ์œ ์ง€๋ณด์ˆ˜ ์–ด๋ ค์›€ - ํด๋ž˜์Šค ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ๋ฆฌํ”Œ๋ ‰์…˜ ์ฝ”๋“œ๋„ ํ•จ๊ป˜ ์ˆ˜์ •ํ•ด์•ผ ํ•ด.
  5. ๋ณด์•ˆ ์œ„ํ—˜ - ์ ‘๊ทผ ์ œ์–ด๋ฅผ ์šฐํšŒํ•˜๋ฉด ๋ณด์•ˆ ์ทจ์•ฝ์ ์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด.
  6. ์„ฑ๋Šฅ ์ €ํ•˜ - ์•ž์„œ ์„ค๋ช…ํ–ˆ๋“ฏ์ด ๋ฆฌํ”Œ๋ ‰์…˜์€ ์ผ๋ฐ˜ ์ฝ”๋“œ๋ณด๋‹ค ๋А๋ ค.

๋ฆฌํ”Œ๋ ‰์…˜์„ ํšจ๊ณผ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋ชจ๋ฒ” ์‚ฌ๋ก€๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„:

โœ… ๋ฆฌํ”Œ๋ ‰์…˜ ๋ชจ๋ฒ” ์‚ฌ๋ก€

  1. ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉ - ์ผ๋ฐ˜์ ์ธ ์ฝ”๋“œ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ”ผํ•ด.
  2. ์บ์‹ฑ ํ™œ์šฉ - ๋ฆฌํ”Œ๋ ‰์…˜ ์ •๋ณด๋ฅผ ์บ์‹ฑํ•ด์„œ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ตœ์†Œํ™”ํ•ด.
  3. ์˜ˆ์™ธ ์ฒ˜๋ฆฌ - ๋ฆฌํ”Œ๋ ‰์…˜์€ ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ ์ ˆํžˆ ์ฒ˜๋ฆฌํ•ด.
  4. ํ…Œ์ŠคํŠธ ์ฒ ์ €ํžˆ - ๋ฆฌํ”Œ๋ ‰์…˜ ์ฝ”๋“œ๋Š” ๋” ๋งŽ์€ ํ…Œ์ŠคํŠธ๊ฐ€ ํ•„์š”ํ•ด.
  5. ๋ฌธ์„œํ™” - ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ ์ด์œ ์™€ ๋ฐฉ๋ฒ•์„ ๋ช…ํ™•ํžˆ ๋ฌธ์„œํ™”ํ•ด.
  6. ๋Œ€์•ˆ ๊ณ ๋ ค - ์ฝ”ํ‹€๋ฆฐ์˜ ์œ„์ž„, ์ธ๋ผ์ธ ํ•จ์ˆ˜, ๊ณ ์ฐจ ํ•จ์ˆ˜ ๋“ฑ์˜ ๋Œ€์•ˆ์„ ๊ฒ€ํ† ํ•ด.

๋ฆฌํ”Œ๋ ‰์…˜ ๋Œ€์•ˆ ๊ณ ๋ คํ•˜๊ธฐ ๐Ÿ”„

๋ฆฌํ”Œ๋ ‰์…˜ ๋Œ€์‹  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”ํ‹€๋ฆฐ์˜ ๊ธฐ๋Šฅ๋“ค์ด์•ผ:

import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

// 1. ์œ„์ž„ ํ”„๋กœํผํ‹ฐ (Delegated Properties)
class User(val map: MutableMap<String, Any?>) {
    var name: String by map
    var age: Int by map
}

// 2. ์ปค์Šคํ…€ ์œ„์ž„ (Custom Delegation)
class ValidatedProperty<T>(private var value: T, private val validator: (T) -> Boolean) : ReadWriteProperty<Any?, T> {
    override fun getValue(thisRef: Any?, property: KProperty<*>): T = value
    
    override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        if (validator(value)) {
            this.value = value
        } else {
            throw IllegalArgumentException("Invalid value for ${property.name}: $value")
        }
    }
}

// ์‚ฌ์šฉ ์˜ˆ์‹œ
class Person {
    var name: String by ValidatedProperty("") { it.isNotBlank() }
    var age: Int by ValidatedProperty(0) { it >= 0 }
}

// 3. ์ธ๋ผ์ธ ํ•จ์ˆ˜์™€ reified ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ
inline fun <reified T> Any.castTo(): T {
    return this as T
}

// 4. ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ ‘๊ทผ๋ฒ•
fun processObject(obj: Any, processor: (Any) -> Unit) {
    processor(obj)
}

fun main() {
    // ์œ„์ž„ ํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉ
    val userData = mutableMapOf(
        "name" to "์ฝ”ํ‹€๋ฆฐ",
        "age" to 11
    )
    val user = User(userData)
    println("์ด๋ฆ„: ${user.name}, ๋‚˜์ด: ${user.age}")
    
    // ๊ฐ’ ๋ณ€๊ฒฝ
    user.age = 12
    println("๋ณ€๊ฒฝ๋œ ๋งต: $userData")  // ๋งต๋„ ํ•จ๊ป˜ ์—…๋ฐ์ดํŠธ๋จ
    
    // ์ปค์Šคํ…€ ์œ„์ž„ ์‚ฌ์šฉ
    val person = Person()
    person.name = "์ฝ”ํ‹€๋ฆฐ"
    person.age = 11
    println("์‚ฌ๋žŒ: ์ด๋ฆ„=${person.name}, ๋‚˜์ด=${person.age}")
    
    try {
        person.age = -1  // ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ์‹คํŒจ
    } catch (e: IllegalArgumentException) {
        println("์˜ˆ์™ธ ๋ฐœ์ƒ: ${e.message}")
    }
    
    // ์ธ๋ผ์ธ ํ•จ์ˆ˜์™€ reified ํƒ€์ž… ํŒŒ๋ผ๋ฏธํ„ฐ
    val any: Any = "Hello"
    val str = any.castTo<String>()
    println("์บ์ŠคํŒ… ๊ฒฐ๊ณผ: $str")
    
    // ํ•จ์ˆ˜ํ˜• ์ ‘๊ทผ๋ฒ•
    processObject(user) { obj ->
        when (obj) {
            is User -> println("User ๊ฐ์ฒด: ์ด๋ฆ„=${obj.name}")
            is Person -> println("Person ๊ฐ์ฒด: ์ด๋ฆ„=${obj.name}")
            else -> println("์•Œ ์ˆ˜ ์—†๋Š” ๊ฐ์ฒด ํƒ€์ž…")
        }
    }
}

์ด๋Ÿฐ ๋Œ€์•ˆ๋“ค์€ ๋ฆฌํ”Œ๋ ‰์…˜๋ณด๋‹ค ํƒ€์ž… ์•ˆ์ „ํ•˜๊ณ  ์„ฑ๋Šฅ์ด ์ข‹์œผ๋ฉฐ ๊ฐ€๋…์„ฑ๋„ ๋†’์•„. ๊ฐ€๋Šฅํ•˜๋ฉด ์ด๋Ÿฐ ๋Œ€์•ˆ์„ ๋จผ์ € ๊ณ ๋ คํ•˜๊ณ , ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„.

๋ฆฌํ”Œ๋ ‰์…˜ ๋‹จ์  ์„ฑ๋Šฅ ์ €ํ•˜ ํƒ€์ž… ์•ˆ์ „์„ฑ ์†์‹ค ์บก์Аํ™” ์œ„๋ฐ˜ ์ฝ”๋“œ ๋ณต์žก์„ฑ ์ฆ๊ฐ€ ์„ฑ๋Šฅ ๊ฐœ์„  ๋ฐฉ๋ฒ• ๋ฆฌํ”Œ๋ ‰์…˜ ์ •๋ณด ์บ์‹ฑ ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์‚ฌ์šฉ ๋ฐ”์ธ๋”ฉ๋œ ์ฐธ์กฐ ํ™œ์šฉ ์ปดํŒŒ์ผ ํƒ€์ž„ ๋Œ€์•ˆ ๊ณ ๋ ค ๋Œ€์•ˆ ๊ธฐ์ˆ  ์œ„์ž„ ํ”„๋กœํผํ‹ฐ ์ธ๋ผ์ธ ํ•จ์ˆ˜ + reified ๊ณ ์ฐจ ํ•จ์ˆ˜ DSL ๊ตฌ์ถ• ๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ ์‹œ ๊ณ ๋ ค์‚ฌํ•ญ

๋ฆฌํ”Œ๋ ‰์…˜ ์‚ฌ์šฉ ์‹œ ์„ฑ๋Šฅ๊ณผ ์ฃผ์˜์‚ฌํ•ญ์— ๋Œ€ํ•ด ์•Œ์•„๋ดค์–ด. ์ด์ œ ๋งˆ์ง€๋ง‰์œผ๋กœ ์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜๊ณ , ์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ๋ฅผ ์†Œ๊ฐœํ• ๊ฒŒ! ๐Ÿ“

9. ๋งˆ๋ฌด๋ฆฌ ๋ฐ ์ถ”๊ฐ€ ์ž๋ฃŒ ๐Ÿ“

์šฐ๋ฆฌ๋Š” ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API์˜ ๋‹ค์–‘ํ•œ ์ธก๋ฉด์„ ์‚ดํŽด๋ดค์–ด. ์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ •๋ฆฌํ•˜๊ณ , ๋” ๊นŠ์ด ๊ณต๋ถ€ํ•  ์ˆ˜ ์žˆ๋Š” ์ž๋ฃŒ๋“ค์„ ์†Œ๊ฐœํ• ๊ฒŒ! ๐ŸŽ“

๋ฐฐ์šด ๋‚ด์šฉ ์š”์•ฝ ๐Ÿ“‹

  1. ๋ฆฌํ”Œ๋ ‰์…˜ ๊ธฐ๋ณธ ๊ฐœ๋… - ํ”„๋กœ๊ทธ๋žจ์ด ์ž์‹ ์˜ ๊ตฌ์กฐ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋Šฅ๋ ฅ
  2. ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API - ํด๋ž˜์Šค, ํ•จ์ˆ˜, ํ”„๋กœํผํ‹ฐ ๋“ฑ์˜ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•
  3. ํด๋ž˜์Šค์™€ ๊ฐ์ฒด ๋ถ„์„ - ํด๋ž˜์Šค ์ •๋ณด ํƒ์ƒ‰, ๊ฐ์ฒด ์ƒ์„ฑ, ํƒ€์ž… ์ฒดํฌ ๋ฐฉ๋ฒ•
  4. ํ•จ์ˆ˜์™€ ํ”„๋กœํผํ‹ฐ ๋‹ค๋ฃจ๊ธฐ - ํ•จ์ˆ˜ ํ˜ธ์ถœ, ํ”„๋กœํผํ‹ฐ ๊ฐ’ ์ฝ๊ธฐ/์“ฐ๊ธฐ ๋ฐฉ๋ฒ•
  5. ์• ๋…ธํ…Œ์ด์…˜ ํ™œ์šฉ - ์• ๋…ธํ…Œ์ด์…˜ ์ •์˜ ๋ฐ ๋ฆฌํ”Œ๋ ‰์…˜์œผ๋กœ ์ฝ๋Š” ๋ฐฉ๋ฒ•
  6. ์ œ๋„ค๋ฆญ๊ณผ ๋ฆฌํ”Œ๋ ‰์…˜ - ํƒ€์ž… ์†Œ๊ฑฐ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•
  7. ์‹ค์ „ ํ™œ์šฉ ์‚ฌ๋ก€ - DI ์ปจํ…Œ์ด๋„ˆ, JSON ๋ณ€ํ™˜, ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ๊ตฌํ˜„
  8. ์„ฑ๋Šฅ๊ณผ ์ฃผ์˜์‚ฌํ•ญ - ์„ฑ๋Šฅ ์˜ํ–ฅ, ๊ฐœ์„  ๋ฐฉ๋ฒ•, ์‚ฌ์šฉ ์‹œ ์ฃผ์˜์ 

์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API๋Š” ๊ฐ•๋ ฅํ•œ ๋„๊ตฌ์ง€๋งŒ, ๊ทธ๋งŒํผ ์ฑ…์ž„๊ฐ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐœ๋ฐœ, ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹œ์Šคํ…œ, ์ง๋ ฌํ™” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋“ฑ ํŠน์ • ์ƒํ™ฉ์—์„œ๋Š” ๋งค์šฐ ์œ ์šฉํ•˜์ง€๋งŒ, ์ผ๋ฐ˜์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ์—์„œ๋Š” ๊ฐ€๋Šฅํ•œ ํ•œ ์ง์ ‘์ ์ธ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„. ๋ฆฌํ”Œ๋ ‰์…˜์€ ๋งˆ์ง€๋ง‰ ์ˆ˜๋‹จ์œผ๋กœ ์ƒ๊ฐํ•˜๊ณ , ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์—†์„ ๋•Œ๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ํ˜„๋ช…ํ•ด.

์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ ๐Ÿ“š

์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜์— ๋Œ€ํ•ด ๋” ๊นŠ์ด ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ๋‹ค์Œ ์ž๋ฃŒ๋“ค์„ ์ฐธ๊ณ ํ•ด๋ด:

  1. ๊ณต์‹ ๋ฌธ์„œ - ์ฝ”ํ‹€๋ฆฐ ๊ณต์‹ ๋ฆฌํ”Œ๋ ‰์…˜ ๋ฌธ์„œ๋Š” ๊ฐ€์žฅ ์ •ํ™•ํ•˜๊ณ  ์ตœ์‹  ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ด.
  2. ์ฝ”ํ‹€๋ฆฐ ์ธ ์•ก์…˜ - ์ด ์ฑ…์˜ 10์žฅ์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜๊ณผ ์• ๋…ธํ…Œ์ด์…˜์— ๋Œ€ํ•ด ์ž์„ธํžˆ ๋‹ค๋ฃจ๊ณ  ์žˆ์–ด.
  3. GitHub ์˜ˆ์ œ - ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋‚ด๋ถ€ ๊ตฌํ˜„์„ ์ดํ•ดํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋ผ.
  4. Spring Framework - Spring์˜ ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ๋ถ„์„ํ•˜๋ฉด ์‹ค์ œ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ์—์„œ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์–ด.
  5. Jackson/Gson - JSON ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์†Œ์Šค ์ฝ”๋“œ๋„ ๋ฆฌํ”Œ๋ ‰์…˜ ํ™œ์šฉ์˜ ์ข‹์€ ์˜ˆ์‹œ์•ผ.

์žฌ๋Šฅ๋„ท์—์„œ ๋” ๋งŽ์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ง€์‹ ์–ป๊ธฐ ๐ŸŒŸ

์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜์ฒ˜๋Ÿผ ์‹ฌํ™”๋œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ง€์‹์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ๋‹ค์–‘ํ•œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ๋„์›€์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด. ์žฌ๋Šฅ๋„ท์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ, ๋””์ž์ธ, ๋งˆ์ผ€ํŒ… ๋“ฑ ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์˜ ์ „๋ฌธ๊ฐ€๋“ค์ด ๋ชจ์—ฌ ์žˆ๋Š” ์žฌ๋Šฅ ๊ณต์œ  ํ”Œ๋žซํผ์ด์•ผ. ์ฝ”ํ‹€๋ฆฐ ๊ฐœ๋ฐœ์— ๋Œ€ํ•œ ์งˆ๋ฌธ์ด ์žˆ๊ฑฐ๋‚˜ ํ”„๋กœ์ ํŠธ ๋„์›€์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์„ ํ†ตํ•ด ์ „๋ฌธ๊ฐ€๋ฅผ ๋งŒ๋‚˜๋ณผ ์ˆ˜ ์žˆ์–ด!

๐ŸŽฏ ๋งˆ์ง€๋ง‰ ์ƒ๊ฐ: ๋ฆฌํ”Œ๋ ‰์…˜์€ ๋งˆ๋ฒ• ๊ฐ™์€ ๋„๊ตฌ์ง€๋งŒ, ๋ชจ๋“  ๋งˆ๋ฒ•๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ํ˜„๋ช…ํ•˜๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ์ฝ”ํ‹€๋ฆฐ์˜ ๊ฐ•๋ ฅํ•œ ํƒ€์ž… ์‹œ์Šคํ…œ๊ณผ ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๊ธฐ๋Šฅ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜๊ณ , ์ •๋ง ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ๋ฆฌํ”Œ๋ ‰์…˜์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์€ ๊ฐœ๋ฐœ์ž์˜ ์ž์„ธ์•ผ. ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ†ตํ•ด ์ฝ”๋“œ์˜ ๊ฐ€๋Šฅ์„ฑ์„ ๋„“ํžˆ๋˜, ๊ทธ ์ฑ…์ž„๋„ ํ•จ๊ป˜ ์งŠ์–ด์งˆ ์ค€๋น„๊ฐ€ ๋˜์–ด ์žˆ์–ด์•ผ ํ•ด!

์ด์ œ ์ฝ”ํ‹€๋ฆฐ ๋ฆฌํ”Œ๋ ‰์…˜ API์˜ ์„ธ๊ณ„๋ฅผ ํƒํ—˜ํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์–ด! ์–ด๋–ค ๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๊ฒŒ ๋ ์ง€ ์ •๋ง ๊ธฐ๋Œ€๋ผ. ํ–‰์šด์„ ๋นŒ์–ด! ๐Ÿ‘‹

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.