학습
Kotlin Kotlin의 Reflection 기초
Kotlin Kotlin의 Reflection 기초 — 중급
Kotlin의 Reflection 기초
1. 도입 (왜 배워야 하는가)
Kotlin의 Reflection은 런타임에 클래스와 객체의 정보를 탐색하고 조작할 수 있는 기능을 제공합니다. 이는 다양한 상황에서 유용하게 사용될 수 있습니다. 예를 들어, 동적으로 클래스의 속성을 읽거나 수정하고, 메소드를 호출할 수 있습니다. 실무에서는 의존성 주입, 프레임워크 설계, API 통신에서의 데이터 바인딩 등 다양한 곳에서 Reflection을 활용합니다.
만약 Reflection을 모른다면, 하드코딩된 방식으로만 데이터를 처리해야 하므로 유연성과 재사용성이 떨어지고 코드가 복잡해질 수 있습니다. 예를 들어, 다양한 타입의 객체를 처리하는 기능을 구현할 때, Reflection을 활용하면 코드의 가독성을 높이고 유지보수를 용이하게 할 수 있습니다.
2. 핵심 개념 설명
Reflection은 객체의 메타데이터를 조회하고 수정할 수 있는 능력을 의미합니다. 객체의 클래스 정보, 메소드, 속성 등을 런타임에 동적으로 접근할 수 있습니다. 이를 이해하기 쉽게 설명하자면, Reflection은 마치 건축가가 건물의 구조를 파악하기 위해 도면을 검사하는 것과 비슷합니다. 도면이 없으면 건물의 구조를 정확히 이해하기 어려운 것처럼, Reflection 없이는 객체의 구조를 명확하게 알기 어렵습니다.
| 용어 | 정의 |
|---|---|
| Class | 객체의 청사진으로, 객체가 어떤 속성과 메소드를 가질지를 정의합니다. |
| Property | 클래스에 포함된 속성으로, 객체의 상태를 표현합니다. |
| Method | 클래스에 정의된 함수로, 객체의 행동을 정의합니다. |
3. 기본 코드 예제
Reflection의 기본적인 사용법을 보여주는 간단한 예제를 작성해 보겠습니다.
import kotlin.reflect.full.memberProperties
data class Person(val name: String, val age: Int)
fun main() {
val person = Person("Alice", 30) // Person 객체 생성
val kClass = person::class // 객체의 클래스 정보 가져오기
println("클래스 이름: ${kClass.simpleName}") // 클래스 이름 출력
// "클래스 이름: Person"이 출력됩니다.
// 클래스의 속성 정보 가져오기
kClass.memberProperties.forEach { property ->
println("속성 이름: ${property.name}, 값: ${property.get(person)}")
// 속성 이름과 값을 출력합니다.
}
}
실행 결과
클래스 이름: Person
속성 이름: name, 값: Alice
속성 이름: age, 값: 30
코드 한 줄씩 동작 설명
data class Person(val name: String, val age: Int)-Person이라는 데이터를 표현하는 클래스를 정의합니다.val person = Person("Alice", 30)-Person객체를 생성하며, 이름과 나이를 초기화합니다.val kClass = person::class- 생성된person객체의 클래스 정보를 가져옵니다.println("클래스 이름: ${kClass.simpleName}")- 클래스의 이름을 출력합니다.kClass.memberProperties.forEach { property -> ... }- 클래스의 모든 속성을 반복하며 정보를 출력합니다.- 주의: 속성 이름은 정확히 맞아야 합니다. 오타가 나면 NullPointerException이 발생할 수 있습니다.
- 주의: immutable 속성은 수정할 수 없습니다. 변경 가능한 속성에서만 사용하십시오.
- Reflection은 객체의 메타데이터를 런타임에 탐색하고 조작할 수 있는 기능입니다.
- 클래스, 속성, 메소드에 대한 동적 접근을 가능하게 합니다.
- 실무에서 유용하게 활용될 수 있는 다양한 패턴이 있습니다.
- 다음에는 Kotlin의 고급 기능인 AOP(Aspect-Oriented Programming)이나 DSL(Domain Specific Language) 설계에 대해 학습하는 것을 추천합니다.
4. 실전 활용 예제
예제 1: 동적 속성 변경
쇼핑몰에서 사용자 정보를 관리하는 상황을 가정해 봅시다. 사용자의 정보를 동적으로 변경해야 하는 경우에 Reflection을 활용할 수 있습니다.
data class User(var username: String, var email: String)
fun updateUserProperty(user: User, propertyName: String, newValue: Any) {
val kClass = user::class
val property = kClass.members.find { it.name == propertyName } as? kotlin.reflect.KMutableProperty1<User, Any>
property?.set(user, newValue) // 속성 값 변경
}
fun main() {
val user = User("john_doe", "[email protected]")
updateUserProperty(user, "email", "[email protected]") // 이메일 변경
println(user) // User(username=john_doe, [email protected])
}
예제 2: 메소드 동적 호출
어떤 상황에서는 특정 조건에 따라 메소드를 동적으로 호출해야 할 수도 있습니다.
class Calculator {
fun add(a: Int, b: Int): Int = a + b
fun subtract(a: Int, b: Int): Int = a - b
}
fun invokeMethod(calculator: Calculator, methodName: String, vararg args: Any?): Any? {
val method = calculator::class.members.find { it.name == methodName } as? kotlin.reflect.KFunction<>
return method?.call(calculator, args)
}
fun main() {
val calculator = Calculator()
val result = invokeMethod(calculator, "add", 5, 3) // add 메소드 호출
println(result) // 8 출력
}
5. 흔한 실수와 주의사항
실수 1: 잘못된 속성 이름 사용
// 잘못된 코드
updateUserProperty(user, "user_email", "[email protected]") // 잘못된 속성 이름
// 올바른 코드
updateUserProperty(user, "email", "[email protected]") // 올바른 속성 이름
실수 2: Immutable Property 수정
data class Person(val name: String) // immutable 속성
// 잘못된 코드
fun updateName(person: Person, newName: String) {
person::class.memberProperties.find { it.name == "name" }?.set(person, newName) // 오류 발생
}