본문 바로가기
자바와 코틀린

코틀린에서 어노테이션 사용시 주의사항

by pius712 2025. 3. 9.

요약

코틀린의 프로퍼티는 선언하는 방식에 따라, 자바에서의 여러 요소에 매핑될 수 있다.

따라서 코틀린의 프로퍼티에 어노테이션 사용시에는 use-site target 을 지정해주어야한다.

해당 글에서는 코틀린 프로퍼티와 자바의 필드 사이의 차이점에 대해 다루며, 어노테이션이나, bean validation 에 대해 국한된 내용도 아니고 관련해서 딥다이브 하는 포스트는 아니라서 관련된 내용에 대해서 자세히 다루지 않는다.

문제상황 분석

문제 상황

Bean Validation 기능을 통해 request dto 를 검증하려고 한다.

하지만, 아래의 코드는 동작하지 않는다.

@RestController
class TestController {
    private val logger = LoggerFactory.getLogger(javaClass)
    
    @PostMapping("/test")
    fun test(
        @Valid @RequestBody request: TestRequestDto
    ) {
        logger.info("name: ${request.name}")
    }
}

data class TestRequestDto(
    @NotEmpty
    val name:String,
)

위 코드에서는 TestRequestDto의 name 프로퍼티에 @NotEmpty 어노테이션이 붙어있지만, 실제 동작시에는 어노테이션이 적용되지 않아 유효성 검증이 제대로 동작하지 않는다.

자바로 변환

위의 TestRequestDto 를 자바코드로 디컴파일해보면 아래의 코드가 나온다.

바로 name 프로퍼티의 @NotEmpty 어노테이션이 생성자의 파라미터에 붙어있다.

public final class TestRequestDto {
   @NotNull
   private final String name;

	 // @NotEmpty 어노테이션이 생성자에 붙어있다.
   public TestRequestDto(@NotEmpty @NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      super();
      this.name = name;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   @NotNull
   public final String component1() {
      return this.name;
   }

   @NotNull
   public final TestRequestDto copy(@NotEmpty @NotNull String name) {
      Intrinsics.checkNotNullParameter(name, "name");
      return new TestRequestDto(name);
   }
 }

Bean Validation 이 기대하는 위치는 필드나 getter 지만, 생성된 코드에는 생성자 파라미터에 어노테이션이 적용되어 제대로 동작하지 않는 것이다.

상황에 대한 이해

위 상황에 대해 이해하기 위해서는 코틀린의 프로퍼티와 어노테이션에 대해 이해해야한다.

코틀린의 프로퍼티는 자바의 필드와 비슷하지만 같지 않다.

코틀린에서 프로퍼티를 선언하면 암묵적으로 field, getter, setter 가 생성된다.

그리고 코틀린의 문법을 통해서 생성자에서 프로퍼티 선언과 동시에 초기화를 할 수 있다.

즉, 코틀린의 생성자에서 프로퍼티를 선언한다면 자바에서는 4개의 element 가 생성된다.

  • 생성자 파라미터
  • 필드
  • getter
  • setter (var 의 경우)

그리고 생성자에 프로퍼티를 선언하고 어노테이션을 붙이는 경우, 어노테이션의 Target 리스트 중 아래 순서대로 적용된다.

  • parameter
  • property
  • field

NotEmpty 어노테이션은 아래의 Target 을 지원하므로, parameter 에 어노테이션이 적용되어 버린 것이다.

@Target({ElementType.METHOD, 
  ElementType.FIELD, 
  ElementType.ANNOTATION_TYPE, 
  ElementType.CONSTRUCTOR, 
  ElementType.PARAMETER, 
	ElementType.TYPE_USE})

해결법

이 문제를 해결하기 위해서는 어노테이션을 명시적으로 원하는 위치에 붙이도록 use-site target을 지정해주어야 한다.

예를 들어, 필드에 직접 어노테이션을 적용하고자 한다면 아래와 같이 use-site target 을 지정해주어야한다.

data class TestRequestDto(
    @field:NotEmpty // or @get:NotEmpty
    val name: String,
)

위와 같이 @field:NotEmpty를 사용하면, 어노테이션이 생성된 자바 코드에서 필드에 직접 마킹이 되어서 Bean Validation이 올바르게 동작한다.

결론

코틀린의 언어 특성상, 프로퍼티의 개념이 자바의 필드와는 다르다.

포스트에서는 BeanValidation 에 대해서 다루었지만, ORM 이나 SDK 을 사용할 때에도 마찬가지로 적용이 된다.

따라서 코틀린 프로젝트에서 프로퍼티에 어노테이션을 적용할 때는 어노테이션이 어느 위치에 붙는지 이해해야한다.

'자바와 코틀린' 카테고리의 다른 글

JWT  (0) 2024.11.17
코루틴 - 코루틴 스레드 양보와 실행 스레드  (0) 2024.09.17
코루틴 - 중단함수  (0) 2024.09.17
코루틴 - 구조화된 동시성  (2) 2024.09.17
코루틴 - 코루틴 빌더와 Job  (0) 2024.09.17