Kotlin in action 2/e: 어노테이션과 리플렉션(1) 코틀린의 어노테이션

2026. 2. 20. 02:34·Kotlin

어노테이션을 적용하면 선언에 표지를 남길 수 있다. 

코틀린에서는 @과 어노테이션 이름을 선언 앞에 넣어서 어노테이션을 적용할 수 있다. 책의 예제에서도 처음에 등장하지만, 나 또한 이를 보고, JUnit의 @Test가 생각이 났다. 

 

import kotlin.test.*
class MyTest {
    @Test
    fun testTrue() {
        assertTrue(1 + 1 == 2)
    }
}

 

다음과 같이 함수 선언 위에 @Test 어노테이션을 붙여주게 되면, JUnit 프레임워크에게 이 메서드를 테스트로 호출하라고 지시하게 된다. 

 

@Deprecated 어노테이션

상당히 흥미로운 어노테이션이라고 할 수 있겠다. 해당 어노테이션은 선언이 더이상 쓰이지 않게 될 이유를 사용자에게 제공해줄 수 있으며, 새 버전으로 전환할 수 있도록 지원해주고, 컴파일되게 할 것인지 말 것인지 등도 정할 수 있다고 한다. 

@Deprecated("Use removeAt(index) instead.", ReplaceWith(removeAt(index)"))
fun remove(index: Int) { /* ... */ }

실제로 함수에 선이 그어지며 설정해둔 대로 수정 방안도 잘 제공해주는 것을 확인할 수 있었다. 

 

코틀린의 어노테이션의 인자 사용법은 자바와 다르다. 

사실 자바의 어노테이션의 사용법은 공부하지 않아 잘 모른다... 때문에 여기서는 자바와 다르다는 사실만 짚고 우선은 코틀린의 어노테이션만 정리해보려고 한다. 

 

클래스를 어노테이션 인자로 지정할 때 ::class를 클래스 이름 뒤에 넣어야 한다.

 

다른 어노테이션을 인자로 지정할 떄 이름 앞에 @를 붙이면 안된다. 

 

배열을 인자로 지정할 때 대괄호를 사용한다. 

 

어노테이션 인자를 컴파일 타임에 알 수 있어야 한다. 즉 const val을 붙인 상수여야 하고, 파일 최상위나 object 안에 선언해야 한다. 

 

코틀린 소스코드에서 한 선언을 컴파일한 결과가 여러 자바 선언과 대응하는 경우

코틀린 소스코드에서 한 선언을 컴파일한 결과가 여러 자바 선언과 대응하는 경우 코틀린 선언에 대응하는 각각의 자바 선언에 어노테이션을 붙여야 하는 때가 있다고 한다. 

 

예를 들면 코틀린에서는 프로퍼티이지만, 자바에서는 게터, 세터 등등 다른 코드와 대응되는 경우라던가, 주 생성자에서 프로퍼티를 선언하는 경우 등이 있을 것이다. 

 

결론적으로 

class Person (val name: String)

 

위와 같은 코틀린 코드만 작성하더라도, 게터, 파라미터 선언, 주 생성자 등등 여러 자바 요소에 대응된다는 것이다!

때문에 아래처럼 어노테이션을 사용해서 자바에서 사용될 게터와 세터의 이름도 지정해줄 수 있다

class CertificateManager {
    @get:JvmName("obtainCertificate")
    @set:JvmName("putCertificate")
    var certificate: String = "-----BEGIN PRIVATE KEY-----"
}

 

어노테이션으로 JSON 직렬화 제어하기

사실 위의 기능을 위해서 어노테이션을 사용하기에는 뭔가 아쉽다는 생각이 들었다. 다행히 다음에 바로 직렬화를 제어하는데 어노테이션을 사용한다는 설명이 나온다. 

 

직렬화(Serialization)와 역직렬(Deserialization)

직렬화는 객체를 저장 장치에 저장하거나 네트워크를 통해 전송하기 위해 텍스트나 이진 형식으로 변환하는 것을 말한다. 역직렬화는 당연히 이러한 텍스트, 이진 형식의 값을 다시 객체로 복원하는 직업이다. 

 

코틀린에서 사용 가능한 객체 <-> JSON 변환 라이브러리

코틀린 팀에서 개발한 kolinx.serialization, 특히 잭슨, 지슨 등 자바 객체를 JSON으로 변환하기 위해 설계된 라이브러리도 완벽하게 호환된다고 한다, 상호 운용성이 이러한 측면에서도 드러나는 것이 대단하다는 생각이 들었다,, 

 

어노테이션 클래스 선언하기

annotaion class JsonExclude

 

어노테이션은 위와 같이 선언할 수 있다. 특히 본문이 정의되어있지 않다 때문에 언뜻 보면 클래스 선언 특히 데이터 클래스 선언과 비슷해보이기도 한다. 

 

어노테이션 클래스는 본문을 정의할 수 없다. 

하지만 코틀린의 데이터 클래스 본문은 선택이었지만, 어노테이션 클래스는 주 생성자만 정의할 수 있고 본문을 정의할 수 없다. 정말 구조만 정의할 수 있게 되어있는 것으로 보인다. 다음과 같이 사용할 수 있겠다.

 

annotaion class JsonName(val name: String)

 

주 생성자의 모든 파라미터는 불변이어야 한다.

또한 모든 파라미터는 val이어야 한다. 생각해봤을 때, 불변성을 깨면서 메타데이터를 바꾸는 것 보다 차라리 새로운 어노테이션 클래스를 만들도록 의도하는 것이 훨씬 합리적이다. 

 

자바 어노테이션 선언 vs 코틀린 어노테이션 선언

public @interface JsonName {
    String value();
}

 

우선 위 코틀린 어노테이션 선언과 동일한 동작을 하는 자바 어노테이션 선언은 위 예제와 같다! 특별히 짚고 넘어가야 할 부분은 value() 메서드인데, 이 메서드를 붙이면 value를 제외한 모든 인자에 이름 붙은 인자 구문을 사용해야만 한다. 

 

하지만 코틀린에서는 일반적인 생성자 구문과 같이 인자의 이름을 선택적으로 명시 가능하다. 하지만 자바에서 선언한 어노테이션을 코틀린에서 적용할 때는 자바에서 사용하던 방법과 동일하게 사용해야 한다. 즉 value 메서드가 붙은 어노테이션을 제외한 모든 인자에 대해 이름 붙은 인자 구문을 사용해야한다. 

 

어노테이션 클래스에 어노테이션 붙이기: 메타 어노테이션

사실 어노테이션에 어노테이션을 붙인다고 하니 직관적으로 이해가 잘 되지는 않았던 것 같다. 우선 어노테이션중 어노테이션 클래스에 적용할 수 있는 녀석들을 메타 어노테이션이라 부른다. 

 

책에서는 @Target 메타 어노테이션을 예시로 설명해주는데, 

@Target(AnnotationTarget.PROPERTY)
annotaion class JsonExclue

 

위와 같이 사용할 수 있다. @Target 어노테이션은 어노테이션을 적용할 수 있는 요소의 유형을 미리 지정한다. 문법을 딱히 사전에 알지 않아도 잘 해석할만 한다 위의 코드는 JsonExclude 어노테이션의 적용 타깃을 지정할 수 있다!

 

결론적으로 메타어노테이션은 컴파일러가 어노테이션을 처리하는 방법을 제어한다고 할 수 있다.

사실 이를 어떻게 제어하는지는 잘 와닿지 않는다. 이에 대해서는 추후 더 학습하여 정리해보도록 하겠다. 

 

어노테이션 파라미터로 클래스 사용 

어노테이션 파라미터에 클래스를 넣는 경우도 있다고 한다. 인터페이스 타입인 프로퍼티에 대한 역직렬화를 제어할 때 어떤 클래스를 사용하여 인터페이스를 구현할지를 지정할 수 있어야 하기 때문이라고 한다. 

 

생각해보면 다음과 같은 이유일 것이라 생각된다. 자바, 코틀린에서는 인터페이스에 대해 new 즉 객체를 만들 수 없기 때문에 역직렬화 시 어떤 클래스로 new를 해서 객체로 돌려놓을지에 대한 정보를 전달하는 것으로 생각된다. 

 

어노테이션 파라미터로 제네릭 클래스 받기

interface ValueSerializer<T> {
    fun toJsonValue(value: T): Any?
    fun fromJsonValue(jsonValue: Any?): T
}

 

위와 같은 직렬화 클래스 인터페이스를 인자로 받는 커스텀 직렬화 클래스는 다음과 같이 선언할 수 있다. 

annotation class CustomSerializer(
    val serializerClass: KClass<out ValueSerializer<*>>
)

 

사실 <T>를 사용할 줄 알았으나, 어노테이션은 제네릭을 가질 수 없다고 한다. 또한 이 어노테이션이 어떤 타입에 쓰일지 전혀 알 수 없으므로 그러한 관점에서 스타 프로젝션을 사용하는게 맞다.

사실 이를 결정하는 것에 대해 개념적으로는 뭔가 이해가 되지만 실제 사용은 할 수 있을지 잘 모르겠다....

 

런타임에 객체 관찰하기: 리플렉션

사실 처음에 이를 보고 디버깅이 생각났다, 디버깅도 유사하게 사용자가 직접 객체를 검색하고, 런타임에 조사할 수 있기 때문이었다. 그렇지만 책에서는 직렬화와 관련된 내용으로 이에 대해 설명한다. 

 

방금도 어떤 타입이던 받기 위해 스타 프로젝션을 사용했듯이 직렬화 라이브러리는 특정 클래스나 프로퍼티를 위한 것이 아니기 때문에 타입과 관계없이 객체를 다뤄야 한다. 

이러한 구현을 위해 리플렉션을 사용하는 것으로 생각된다, 코틀린에서는 리플렉션 API를 제공한다고 한다. 주목할 점은 해당 API가 다른 JVM 언어에서 생성한 바이트코드도 충분히 다룰 수 있다는 점이었다. 

물론 당연히 자바의 리플렉션 API에서 코틀린을 컴파일한 바이트코드를 다룰 수 있다. 

 

 

 

'Kotlin' 카테고리의 다른 글

Kotlin in action 2/e: 코루틴(1) 코루틴이란 무엇인가?  (0) 2026.02.23
Kotlin in action 2/e: DSL 만들기(1) DSL이란 무엇인가?  (0) 2026.02.23
Kotlin in action 2/e: 고차함수: 람다를 파라미터와 반환값으로 사용(2) 인라인 함수, 코틀린 표준 라이브러리의 인라이닝  (0) 2026.02.19
Kotlin in action 2/e:고차함수: 람다를 파라미터와 반환값으로 사용(1) 고차함수, 스마트 스테핑, 함수 타입의 파라미터, 함수를 반환하는 함수  (1) 2026.02.18
Kotlin in action 2/e: 널이 될 수 있는 값(2): not null assertion(!!), let(), lateinit()  (0) 2026.02.16
'Kotlin' 카테고리의 다른 글
  • Kotlin in action 2/e: 코루틴(1) 코루틴이란 무엇인가?
  • Kotlin in action 2/e: DSL 만들기(1) DSL이란 무엇인가?
  • Kotlin in action 2/e: 고차함수: 람다를 파라미터와 반환값으로 사용(2) 인라인 함수, 코틀린 표준 라이브러리의 인라이닝
  • Kotlin in action 2/e:고차함수: 람다를 파라미터와 반환값으로 사용(1) 고차함수, 스마트 스테핑, 함수 타입의 파라미터, 함수를 반환하는 함수
Kirbyyy
Kirbyyy
개인적인 일상과 회고를 기록하는 블로그입니다.
  • Kirbyyy
    커브볼의 생존일지
    Kirbyyy
  • 전체
    오늘
    어제
    • 분류 전체보기 (53)
      • 우아한테크코스 (8)
      • 프로덕트 빌드 (0)
      • Problem Solving (20)
      • C++ (0)
      • Kotlin (19)
      • Java (3)
      • CS (2)
        • AI (2)
      • 취미생활 (0)
        • 서평 (0)
        • 프라모델 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준 16173
    백준 RGB 거리
    너비 우선 탐색
    다이나믹 프로그래밍
    백준 16174
    백준 31575
    백준
    Problem Solving
    그리디 알고리즘
    백준 연속 합
    백준 알고리즘
    백준 1356
    BFS
    분할 정복
    백준 11123
    ProblemSolving
    백준 파도반 수열
    C++
    백준 33272
    우테코 8기
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
Kirbyyy
Kotlin in action 2/e: 어노테이션과 리플렉션(1) 코틀린의 어노테이션
상단으로

티스토리툴바