티스토리 뷰
# 이슈
annotation class CustomAnnotation
위와 같이 커스텀 애노테이션을 정의한다. 애노테이션은 다양하게 활용할 수 있지만 내 의도는 프로퍼티에 사용하려는 의도라고 가정한다.
class Person(
name: String,
) {
@CustomAnnotation
val name: String = name
}
@Test
fun findAnnotation() {
val person = Person3("LichKing")
val properties = person::class.memberProperties
for (property in properties) {
if (property.name == "name") {
assertThat(property.hasAnnotation<CustomAnnotation>()).isTrue()
return
}
}
fail<Unit>("Must be not called")
}
Person 클래스를 정의하고, 프로퍼티에 애노테이션을 붙였다. 그리고 kotlin reflection 을 통해 해당 애노테이션이 달렸는지 체크하는 테스트 코드를 작성했다. 테스트는 문제없이 통과한다.
하지만 현재 Person 과 같은 클래스를 정의할때 저런식으로 프로퍼티와 생성자를 분리해서 정의하는 것보다 코틀린은 좀 더 간결한 표현을 지원한다. 그리고 아마도 위와 같은 방식보단 아래에서 논의할 방식을 더 많이 쓰지 않을까싶다.
class Person(
@CustomAnnotation
val name: String,
)
문제는 이렇게 작성하면 위에서 통과한 테스트가 실패한다는 점이다.
# 해결
처음 이 문제를 맞닥뜨렸을때는 사실 잘 납득이 안돼서 꽤나 많은 삽질을 했었다. 찾아낸 해결법은 두 가지이다.
1. property:
class Person(
@property:CustomAnnotation
val name: String,
)
코틀린은 애노테이션을 사용할때 use-site targets 이라는 기능을 지원하는데 위 코드처럼 해당 애노테이션이 어떤 곳에서 사용되는지를 명시적으로 지정하는 것이다. 코틀린이 문법적으로 유연한 기능들을 많이 제공하기때문에 몇가지 모호한 상황들이 발생하는데, 그럴때 명시적으로 지정하는 기능이다. 저걸 붙여주면 해당 애노테이션이 property 에 사용된다는걸 인지하게된다.
2. @Target
1번 방식으로 해결된다는 점에서 문제의 원인이 뭔지 알 수 있게 되는데, @CustomAnnotation 이 내가 의도한 곳이 아니라 다른 곳에 적용되고 있다는 점이다. 어디에 적용되고 있는 것일까? 코틀린 annotation 문서를 보면 이런 내용이 있다.
If you don't specify a use-site target, the target is chosen according to the @Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
param
property
field
코틀린 애노테이션은 애노테이션 정의를 좀 더 간결하게 하기위해 @Target 이 없으면 관례에 따라 동작하게 되는데, 그 관례에서 우선순위가 param 이 제일 높다. 그래서 생성자와 프로퍼티를 동시에 정의하는 형태의 클래스 문법에서는 생성자의 파라미터에 붙게 되는것이다.
그러다보니 리플렉션을 통해 프로퍼티를 탐색해봤자 애노테이션을 구할 수 없던 것이다.
참고로 제일 첫번째 클래스는 생성자와 프로퍼티가 분리되어있고, 프로퍼티에 애노테이션을 붙였기때문에 모호한 상황없이 프로퍼티로 동작할 수 있다.
그래서 @Target 을 명시적으로 지정해주면 문제를 해결할 수 있다.
@Target(AnnotationTarget.PROPERTY)
annotation class CustomAnnotation
class Person(
@CustomAnnotation
val name: String,
)
1번으로 해결할지, 2번으로 해결할지는 상황마다 다를것 같다. 타겟을 정적으로 명시할 수 있다면 2번을 해도 될것이고, 유연한 애노테이션으로 다양한 타겟을 지정하고싶을땐 1번으로 해결하면 될 것 같다. 개인적으로는 1번처럼 사용하는걸 별로 안좋아한다. ㅎㅎ
'kotlin' 카테고리의 다른 글
kotlin(+JPA) entity 에서 setter 를 막는 법 (4) | 2022.08.27 |
---|---|
코틀린에서 logger 사용 (1) | 2022.06.01 |
jpa entity 에 data class 를 사용해도 될까? (3) | 2021.07.17 |
kotlin 의 json 라이브러리 kotlinx.serialization (0) | 2021.07.17 |
data class 에서 kotlin-allopen plugin 사용 (0) | 2021.05.27 |
- Total
- Today
- Yesterday
- code
- JPA
- MySQL
- Design Pattern
- http
- java8
- Jackson
- 정규표현식
- OOP
- frontcode
- programming
- spring cloud
- generics
- EffectiveJava
- Git
- frontend개발환경
- DesignPattern
- Spring
- toby
- java
- TEST
- db
- JavaScript Core
- javascript
- Kotlin
- servlet
- go-core
- clean code
- mariadb
- backend개발환경
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |