Ver2.0 ๐ Kotlin ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ API ์์ ์ ๋ณต: ๋ฐํ์์ ์ฝ๋ ๋ถ์ํ๊ณ ์กฐ์ํ๋ ๋ง๋ฒ ๊ฐ์ ๊ธฐ์

๐ Kotlin ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ API ์์ ์ ๋ณต: ๋ฐํ์์ ์ฝ๋ ๋ถ์ํ๊ณ ์กฐ์ํ๋ ๋ง๋ฒ ๊ฐ์ ๊ธฐ์
์๋ , ์ฝํ๋ฆฐ ๊ฐ๋ฐ์ ์น๊ตฌ๋ค! ์ค๋์ 2025๋ 3์, ์ฝํ๋ฆฐ ์ธ๊ณ์์ ๊ฝค๋ ๊ฐ๋ ฅํ์ง๋ง ์ ๋ชจ๋ฅด๋ ์ฌ๋๋ ๋ง์ ๋ฆฌํ๋ ์ API์ ๋ํด ํจ๊ป ์์๋ณผ ๊ฑฐ์ผ. ๋ง์น ๋ง๋ฒ์ฒ๋ผ ๋ฐํ์์ ์ฝ๋๋ฅผ ๋ค์ฌ๋ค๋ณด๊ณ ์กฐ์ํ ์ ์๋ ์ด ๊ธฐ์ , ์ด๋ ต๊ฒ๋ง ๋๊ปด์ก๋ค๋ฉด ์ด์ ์น๊ตฌ์ฒ๋ผ ์ฝ๊ฒ ์ค๋ช ํด ์ค๊ฒ! ๐งโโ๏ธโจ
๐ ๋ชฉ์ฐจ
- ๋ฆฌํ๋ ์ ์ด ๋ญ์ผ? ์ ํ์ํ ๊ฑฐ์ง? ๐ค
- ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ API ์์ํ๊ธฐ ๐
- ํด๋์ค์ ๊ฐ์ฒด ๋ถ์ํ๊ธฐ ๐
- ํจ์์ ํ๋กํผํฐ ๋ค๋ฃจ๊ธฐ โ๏ธ
- ์ ๋ ธํ ์ด์ ํ์ฉํ๊ธฐ ๐ท๏ธ
- ์ ๋ค๋ฆญ๊ณผ ๋ฆฌํ๋ ์ ๐ซ
- ์ค์ ํ์ฉ ์ฌ๋ก ๋ฐ ํ๋ก์ ํธ ๐ผ
- ์ฑ๋ฅ๊ณผ ์ฃผ์์ฌํญ โ ๏ธ
- ๋ง๋ฌด๋ฆฌ ๋ฐ ์ถ๊ฐ ์๋ฃ ๐
1. ๋ฆฌํ๋ ์ ์ด ๋ญ์ผ? ์ ํ์ํ ๊ฑฐ์ง? ๐ค
๋ฆฌํ๋ ์ ์ด๋ผ๋ ๋จ์ด๋ฅผ ๋ค์ด๋ดค์ด? ์์ด๋ก๋ 'reflection'์ธ๋ฐ, ๊ฑฐ์ธ์ ๋น์น ๋ชจ์ต์ฒ๋ผ ํ๋ก๊ทธ๋จ์ด ์๊ธฐ ์์ ์ ๋ค์ฌ๋ค๋ณด๊ณ ๋ถ์ํ ์ ์๋ ๋ฅ๋ ฅ์ ๋งํด. ์ฝ๊ฒ ๋งํ๋ฉด, ์ฝ๋๊ฐ ์คํ ์ค์ ์๊ธฐ ์์ ์ ๊ตฌ์กฐ๋ฅผ ํ์ ํ๊ณ ์ฌ์ง์ด ์์ ํ ์๋ ์๋ ๋ง๋ฒ ๊ฐ์ ๊ธฐ๋ฅ์ด์ผ! ๐ฒ
์๋ฅผ ๋ค์ด, ๋๊ฐ ์ด๋ค ํด๋์ค์ ์ด๋ฆ๋ง ์๊ณ ์๋๋ฐ ๊ทธ ํด๋์ค์ ๋ชจ๋ ๋ฉ์๋๋ ํ๋กํผํฐ๋ฅผ ์๊ณ ์ถ๋ค๋ฉด? ๋๋ ๋ฐํ์์ ํน์ ์ ๋ ธํ ์ด์ ์ด ๋ถ์ ํจ์๋ค๋ง ์ฐพ์์ ์คํํ๊ณ ์ถ๋ค๋ฉด? ์ด๋ด ๋ ๋ฆฌํ๋ ์ ์ด ํ์ํ ๊ฑฐ์ง!
๋ฆฌํ๋ ์ ์ด ํ์ํ ์ํฉ๋ค ๐ ๏ธ
- ํ๋ ์์ํฌ ๊ฐ๋ฐ - Spring, Ktor ๊ฐ์ ํ๋ ์์ํฌ๋ค์ ๋ฆฌํ๋ ์ ์ ํ์ฉํด ์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํด.
- ํ๋ฌ๊ทธ์ธ ์์คํ - ๋ฐํ์์ ์๋ก์ด ์ฝ๋๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋ก๋ํ ๋ ์ ์ฉํด.
- ์ง๋ ฌํ/์ญ์ง๋ ฌํ - JSON ํ์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๊ฐ์ฒด ๊ตฌ์กฐ๋ฅผ ๋ถ์ํ ๋ ์ฌ์ฉํด.
- ํ ์คํธ ๋๊ตฌ - ๋ชจํน ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํ ์คํธ ํ๋ ์์ํฌ์์ ๋ง์ด ํ์ฉ๋ผ.
- ์ฝ๋ ์์ฑ - ์ปดํ์ผ ํ์์ ์ฝ๋๋ฅผ ์๋ ์์ฑํ๋ ๋๊ตฌ๋ฅผ ๋ง๋ค ๋ ํ์ํด.
๐ ์ฐธ๊ณ : ๋ฆฌํ๋ ์ ์ ๊ฐ๋ ฅํ์ง๋ง, ๋จ์ฉํ๋ฉด ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์ด. ๊ผญ ํ์ํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํ๋ ๊ฒ ์ข์! ์ผ๋ฐ์ ์ธ ๊ฐ๋ฐ์์๋ ์ฝํ๋ฆฐ์ ๊ฐ๋ ฅํ ํ์ ์์คํ ์ ํ์ฉํ๋ ๊ฒ์ด ๋ ์์ ํ๊ณ ํจ์จ์ ์ด์ง.
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์ด์ด์ผ.
}
์ ์ฝ๋์์ ๋ณผ ์ ์๋ฏ์ด, ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ 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 ์ฐ์ฐ์์ ์ค๋งํธ ์บ์คํธ ๋๋ถ์ ํ์
์ฒดํฌ๊ฐ ๋งค์ฐ ํธ๋ฆฌํด. ํ์ง๋ง ๋ฆฌํ๋ ์
์ ์ฌ์ฉํ๋ฉด ๋ ๋์ ์ธ ํ์
๊ฒ์ฌ๊ฐ ๊ฐ๋ฅํด์ ธ.
์ด๋ ๊ฒ ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ๋ถ์ํ๋ ๊ธฐ๋ณธ์ ์ธ ๋ฐฉ๋ฒ์ ์์๋ดค์ด. ๋ค์์ผ๋ก๋ ํจ์์ ํ๋กํผํฐ๋ฅผ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณผ๊ฒ! ๐
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๋ก ์ค์ ํด์ผ ํด. ํ์ง๋ง ์ด๋ ์บก์ํ๋ฅผ ๊นจ๋ ํ์์ด๋ฏ๋ก ์ ๋ง ํ์ํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉํด์ผ ํด!
ํจ์์ ํ๋กํผํฐ๋ฅผ ๋ฆฌํ๋ ์ ์ผ๋ก ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ ์์๋ดค์ด. ์ด์ ์ ๋ ธํ ์ด์ ์ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ดํด๋ณผ๊ฒ! ๐ท๏ธ
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 ๊ฐ์ ํ๋ ์์ํฌ๋ค์ด ์ ๋ ธํ ์ด์ ๊ธฐ๋ฐ ๋ผ์ฐํ ์ ๊ตฌํํ๋ค๋ ๊ฑธ ์ ์ ์์ด. ๋ฆฌํ๋ ์ ๊ณผ ์ ๋ ธํ ์ด์ ์ ์กฐํฉ์ ์ ๋ง ๊ฐ๋ ฅํ ๋๊ตฌ์ผ!
์ด์ ์ ๋ ธํ ์ด์ ๊ณผ ๋ฆฌํ๋ ์ ์ ํจ๊ป ํ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ดค์ด. ๋ค์์ผ๋ก๋ ์ ๋ค๋ฆญ๊ณผ ๋ฆฌํ๋ ์ ์ ๊ด๊ณ๋ฅผ ์ดํด๋ณผ๊ฒ! ๐ซ
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
}
์ ๋ค๋ฆญ๊ณผ ๋ฆฌํ๋ ์ ์ ํจ๊ป ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์์๋ดค์ด. ํ์ ์๊ฑฐ๋ผ๋ ์ ์ฝ์ด ์์ง๋ง, 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. ์ฑ๋ฅ๊ณผ ์ฃผ์์ฌํญ โ ๏ธ
๋ฆฌํ๋ ์ ์ ๊ฐ๋ ฅํ ๋๊ตฌ์ง๋ง, ๊ทธ๋งํผ ์ ์คํ๊ฒ ์ฌ์ฉํด์ผ ํด. ์ด๋ฒ์๋ ๋ฆฌํ๋ ์ ์ฌ์ฉ ์ ๊ณ ๋ คํด์ผ ํ ์ฑ๋ฅ ๋ฌธ์ ์ ์ฃผ์์ฌํญ์ ๋ํด ์์๋ณผ๊ฒ! ๐ง
์ฑ๋ฅ ์ํฅ ๐
๋ฆฌํ๋ ์ ์ ์ผ๋ฐ์ ์ธ ์ง์ ์ฝ๋ ํธ์ถ๋ณด๋ค ๋๋ ค. ๊ทธ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์:
- ๋์ ํด์ ์ค๋ฒํค๋ - ๋ฆฌํ๋ ์ ์ ๋ฐํ์์ ํ์ ๊ณผ ๋ฉ์๋๋ฅผ ํด์ํด์ผ ํด์ ์ถ๊ฐ ์์ ์ด ํ์ํด.
- ์ต์ ํ ์ ํ - ์ปดํ์ผ๋ฌ์ JVM์ด ์ผ๋ฐ ์ฝ๋์ ์ ์ฉํ๋ ๋ง์ ์ต์ ํ๊ฐ ๋ฆฌํ๋ ์ ์๋ ์ ์ฉ๋์ง ์์.
- ๋ณด์ ๊ฒ์ฌ - ์ ๊ทผ ์ ์ด(private ๋ฉค๋ฒ ์ ๊ทผ ๋ฑ)๋ฅผ ์ฐํํ ๋ ์ถ๊ฐ ๋ณด์ ๊ฒ์ฌ๊ฐ ํ์ํด.
- ์บ์ ๋ถ์ฌ - ๋ฆฌํ๋ ์ ์ ๋ณด๋ฅผ ๋งค๋ฒ ์กฐํํ๋ฉด ์ฑ๋ฅ์ด ์ ํ๋ผ.
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)}%")
}
}
์ฑ๋ฅ์ ๊ฐ์ ํ๋ ์ฃผ์ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์:
- ๋ฆฌํ๋ ์ ์ ๋ณด ์บ์ฑ - ํด๋์ค, ๋ฉ์๋, ํ๋กํผํฐ ์ ๋ณด๋ฅผ ํ ๋ฒ๋ง ์กฐํํ๊ณ ์ฌ์ฌ์ฉํด.
- ํ์ํ ๊ณณ์๋ง ์ฌ์ฉ - ์ฑ๋ฅ์ด ์ค์ํ ๋ฃจํ๋ ํซ ๊ฒฝ๋ก์์๋ ๋ฆฌํ๋ ์ ์ฌ์ฉ์ ํผํด.
- ๋ฐ์ธ๋ฉ๋ ์ฐธ์กฐ ์ฌ์ฉ - ๊ฐ๋ฅํ๋ฉด
KFunction.call()๋์ ๋ฐ์ธ๋ฉ๋ ํจ์ ์ฐธ์กฐ๋ฅผ ์ฌ์ฉํด. - ์ปดํ์ผ ํ์ ๋์ ๊ณ ๋ ค - ์ฝํ๋ฆฐ์ ์์์ด๋ ์ธ๋ผ์ธ ํจ์ ๊ฐ์ ์ปดํ์ผ ํ์ ๊ธฐ๋ฅ์ผ๋ก ๋์ฒดํ ์ ์๋์ง ๊ฒํ ํด.
์ฃผ์์ฌํญ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก โ ๏ธ
๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ ๋ ์ฃผ์ํด์ผ ํ ์ ๋ค์ด์ผ:
โ ๏ธ ๋ฆฌํ๋ ์ ์ฌ์ฉ ์ ์ฃผ์์ฌํญ
- ์บก์ํ ์๋ฐ - private ๋ฉค๋ฒ์ ์ ๊ทผํ๋ฉด ํด๋์ค ์ค๊ณ์์ ์๋๋ฅผ ๋ฌด์ํ๊ฒ ๋ผ.
- ํ์ ์์ ์ฑ ์์ค - ์ปดํ์ผ ํ์ ํ์ ๊ฒ์ฌ๋ฅผ ์ฐํํ๋ฏ๋ก ๋ฐํ์ ์ค๋ฅ ๊ฐ๋ฅ์ฑ์ด ๋์์ ธ.
- ์ฝ๋ ๊ฐ๋ ์ฑ ์ ํ - ๋ฆฌํ๋ ์ ์ฝ๋๋ ์ผ๋ฐ ์ฝ๋๋ณด๋ค ์ดํดํ๊ธฐ ์ด๋ ค์ธ ์ ์์ด.
- ์ ์ง๋ณด์ ์ด๋ ค์ - ํด๋์ค ๊ตฌ์กฐ๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ฆฌํ๋ ์ ์ฝ๋๋ ํจ๊ป ์์ ํด์ผ ํด.
- ๋ณด์ ์ํ - ์ ๊ทผ ์ ์ด๋ฅผ ์ฐํํ๋ฉด ๋ณด์ ์ทจ์ฝ์ ์ด ๋ฐ์ํ ์ ์์ด.
- ์ฑ๋ฅ ์ ํ - ์์ ์ค๋ช ํ๋ฏ์ด ๋ฆฌํ๋ ์ ์ ์ผ๋ฐ ์ฝ๋๋ณด๋ค ๋๋ ค.
๋ฆฌํ๋ ์ ์ ํจ๊ณผ์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํ ๋ชจ๋ฒ ์ฌ๋ก๋ ๋ค์๊ณผ ๊ฐ์:
โ ๋ฆฌํ๋ ์ ๋ชจ๋ฒ ์ฌ๋ก
- ํ์ํ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉ - ์ผ๋ฐ์ ์ธ ์ฝ๋๋ก ํด๊ฒฐํ ์ ์๋ค๋ฉด ๋ฆฌํ๋ ์ ์ ํผํด.
- ์บ์ฑ ํ์ฉ - ๋ฆฌํ๋ ์ ์ ๋ณด๋ฅผ ์บ์ฑํด์ ์ฑ๋ฅ ์ ํ๋ฅผ ์ต์ํํด.
- ์์ธ ์ฒ๋ฆฌ - ๋ฆฌํ๋ ์ ์ ๋ค์ํ ์์ธ๋ฅผ ๋ฐ์์ํฌ ์ ์์ผ๋ฏ๋ก ์ ์ ํ ์ฒ๋ฆฌํด.
- ํ ์คํธ ์ฒ ์ ํ - ๋ฆฌํ๋ ์ ์ฝ๋๋ ๋ ๋ง์ ํ ์คํธ๊ฐ ํ์ํด.
- ๋ฌธ์ํ - ๋ฆฌํ๋ ์ ์ฌ์ฉ ์ด์ ์ ๋ฐฉ๋ฒ์ ๋ช ํํ ๋ฌธ์ํํด.
- ๋์ ๊ณ ๋ ค - ์ฝํ๋ฆฐ์ ์์, ์ธ๋ผ์ธ ํจ์, ๊ณ ์ฐจ ํจ์ ๋ฑ์ ๋์์ ๊ฒํ ํด.
๋ฆฌํ๋ ์ ๋์ ๊ณ ๋ คํ๊ธฐ ๐
๋ฆฌํ๋ ์ ๋์ ์ฌ์ฉํ ์ ์๋ ์ฝํ๋ฆฐ์ ๊ธฐ๋ฅ๋ค์ด์ผ:
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("์ ์ ์๋ ๊ฐ์ฒด ํ์
")
}
}
}
์ด๋ฐ ๋์๋ค์ ๋ฆฌํ๋ ์ ๋ณด๋ค ํ์ ์์ ํ๊ณ ์ฑ๋ฅ์ด ์ข์ผ๋ฉฐ ๊ฐ๋ ์ฑ๋ ๋์. ๊ฐ๋ฅํ๋ฉด ์ด๋ฐ ๋์์ ๋จผ์ ๊ณ ๋ คํ๊ณ , ์ ๋ง ํ์ํ ๊ฒฝ์ฐ์๋ง ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์.
๋ฆฌํ๋ ์ ์ฌ์ฉ ์ ์ฑ๋ฅ๊ณผ ์ฃผ์์ฌํญ์ ๋ํด ์์๋ดค์ด. ์ด์ ๋ง์ง๋ง์ผ๋ก ์ง๊ธ๊น์ง ๋ฐฐ์ด ๋ด์ฉ์ ์ ๋ฆฌํ๊ณ , ์ถ๊ฐ ํ์ต ์๋ฃ๋ฅผ ์๊ฐํ ๊ฒ! ๐
9. ๋ง๋ฌด๋ฆฌ ๋ฐ ์ถ๊ฐ ์๋ฃ ๐
์ฐ๋ฆฌ๋ ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ API์ ๋ค์ํ ์ธก๋ฉด์ ์ดํด๋ดค์ด. ์ด์ ๋ฐฐ์ด ๋ด์ฉ์ ์ ๋ฆฌํ๊ณ , ๋ ๊น์ด ๊ณต๋ถํ ์ ์๋ ์๋ฃ๋ค์ ์๊ฐํ ๊ฒ! ๐
๋ฐฐ์ด ๋ด์ฉ ์์ฝ ๐
- ๋ฆฌํ๋ ์ ๊ธฐ๋ณธ ๊ฐ๋ - ํ๋ก๊ทธ๋จ์ด ์์ ์ ๊ตฌ์กฐ๋ฅผ ๋ถ์ํ๊ณ ์์ ํ ์ ์๋ ๋ฅ๋ ฅ
- ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ API - ํด๋์ค, ํจ์, ํ๋กํผํฐ ๋ฑ์ ๋ฉํ๋ฐ์ดํฐ์ ์ ๊ทผํ๋ ๋ฐฉ๋ฒ
- ํด๋์ค์ ๊ฐ์ฒด ๋ถ์ - ํด๋์ค ์ ๋ณด ํ์, ๊ฐ์ฒด ์์ฑ, ํ์ ์ฒดํฌ ๋ฐฉ๋ฒ
- ํจ์์ ํ๋กํผํฐ ๋ค๋ฃจ๊ธฐ - ํจ์ ํธ์ถ, ํ๋กํผํฐ ๊ฐ ์ฝ๊ธฐ/์ฐ๊ธฐ ๋ฐฉ๋ฒ
- ์ ๋ ธํ ์ด์ ํ์ฉ - ์ ๋ ธํ ์ด์ ์ ์ ๋ฐ ๋ฆฌํ๋ ์ ์ผ๋ก ์ฝ๋ ๋ฐฉ๋ฒ
- ์ ๋ค๋ฆญ๊ณผ ๋ฆฌํ๋ ์ - ํ์ ์๊ฑฐ ๋ฌธ์ ์ ํด๊ฒฐ ๋ฐฉ๋ฒ
- ์ค์ ํ์ฉ ์ฌ๋ก - DI ์ปจํ ์ด๋, JSON ๋ณํ, ํ ์คํธ ํ๋ ์์ํฌ ๊ตฌํ
- ์ฑ๋ฅ๊ณผ ์ฃผ์์ฌํญ - ์ฑ๋ฅ ์ํฅ, ๊ฐ์ ๋ฐฉ๋ฒ, ์ฌ์ฉ ์ ์ฃผ์์
์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ API๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ง๋ง, ๊ทธ๋งํผ ์ฑ ์๊ฐ ์๊ฒ ์ฌ์ฉํด์ผ ํด. ํ๋ ์์ํฌ ๊ฐ๋ฐ, ํ๋ฌ๊ทธ์ธ ์์คํ , ์ง๋ ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฑ ํน์ ์ํฉ์์๋ ๋งค์ฐ ์ ์ฉํ์ง๋ง, ์ผ๋ฐ์ ์ธ ์ ํ๋ฆฌ์ผ์ด์ ์ฝ๋์์๋ ๊ฐ๋ฅํ ํ ์ง์ ์ ์ธ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์. ๋ฆฌํ๋ ์ ์ ๋ง์ง๋ง ์๋จ์ผ๋ก ์๊ฐํ๊ณ , ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ํด๊ฒฐํ ์ ์์ ๋๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ํ๋ช ํด.
์ถ๊ฐ ํ์ต ์๋ฃ ๐
์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ ์ ๋ํด ๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด ๋ค์ ์๋ฃ๋ค์ ์ฐธ๊ณ ํด๋ด:
- ๊ณต์ ๋ฌธ์ - ์ฝํ๋ฆฐ ๊ณต์ ๋ฆฌํ๋ ์ ๋ฌธ์๋ ๊ฐ์ฅ ์ ํํ๊ณ ์ต์ ์ ๋ณด๋ฅผ ์ ๊ณตํด.
- ์ฝํ๋ฆฐ ์ธ ์ก์ - ์ด ์ฑ ์ 10์ฅ์์ ๋ฆฌํ๋ ์ ๊ณผ ์ ๋ ธํ ์ด์ ์ ๋ํด ์์ธํ ๋ค๋ฃจ๊ณ ์์ด.
- GitHub ์์ - ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ค ์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด ๋ด๋ถ ๊ตฌํ์ ์ดํดํ๋ ๋ฐ ๋์์ด ๋ผ.
- Spring Framework - Spring์ ์์ค ์ฝ๋๋ฅผ ๋ถ์ํ๋ฉด ์ค์ ํ๋ก๋์ ํ๊ฒฝ์์ ๋ฆฌํ๋ ์ ์ ์ด๋ป๊ฒ ํ์ฉํ๋์ง ๋ฐฐ์ธ ์ ์์ด.
- Jackson/Gson - JSON ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์ค ์ฝ๋๋ ๋ฆฌํ๋ ์ ํ์ฉ์ ์ข์ ์์์ผ.
์ฌ๋ฅ๋ท์์ ๋ ๋ง์ ํ๋ก๊ทธ๋๋ฐ ์ง์ ์ป๊ธฐ ๐
์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ ์ฒ๋ผ ์ฌํ๋ ํ๋ก๊ทธ๋๋ฐ ์ง์์ด ํ์ํ๋ค๋ฉด, ์ฌ๋ฅ๋ท์์ ๋ค์ํ ์ ๋ฌธ๊ฐ๋ค์ ๋์์ ๋ฐ์ ์ ์์ด. ์ฌ๋ฅ๋ท์ ํ๋ก๊ทธ๋๋ฐ, ๋์์ธ, ๋ง์ผํ ๋ฑ ๋ค์ํ ๋ถ์ผ์ ์ ๋ฌธ๊ฐ๋ค์ด ๋ชจ์ฌ ์๋ ์ฌ๋ฅ ๊ณต์ ํ๋ซํผ์ด์ผ. ์ฝํ๋ฆฐ ๊ฐ๋ฐ์ ๋ํ ์ง๋ฌธ์ด ์๊ฑฐ๋ ํ๋ก์ ํธ ๋์์ด ํ์ํ๋ค๋ฉด ์ฌ๋ฅ๋ท์ ํตํด ์ ๋ฌธ๊ฐ๋ฅผ ๋ง๋๋ณผ ์ ์์ด!
๐ฏ ๋ง์ง๋ง ์๊ฐ: ๋ฆฌํ๋ ์ ์ ๋ง๋ฒ ๊ฐ์ ๋๊ตฌ์ง๋ง, ๋ชจ๋ ๋ง๋ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ํ๋ช ํ๊ฒ ์ฌ์ฉํด์ผ ํด. ์ฝํ๋ฆฐ์ ๊ฐ๋ ฅํ ํ์ ์์คํ ๊ณผ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฅ์ ์ต๋ํ ํ์ฉํ๊ณ , ์ ๋ง ํ์ํ ๊ฒฝ์ฐ์๋ง ๋ฆฌํ๋ ์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข์ ๊ฐ๋ฐ์์ ์์ธ์ผ. ๋ฆฌํ๋ ์ ์ ํตํด ์ฝ๋์ ๊ฐ๋ฅ์ฑ์ ๋ํ๋, ๊ทธ ์ฑ ์๋ ํจ๊ป ์ง์ด์ง ์ค๋น๊ฐ ๋์ด ์์ด์ผ ํด!
์ด์ ์ฝํ๋ฆฐ ๋ฆฌํ๋ ์ API์ ์ธ๊ณ๋ฅผ ํํํ ์ค๋น๊ฐ ๋์์ด! ์ด๋ค ๋ฉ์ง ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๊ฒ ๋ ์ง ์ ๋ง ๊ธฐ๋๋ผ. ํ์ด์ ๋น์ด! ๐
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.