1. 코틀린 스프링에서 validation을 적용하다 특이한 경우를 만났다.


package com.tony.string.controller.request

import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotNull
import jakarta.validation.constraints.Positive

data class CreatePostRequestDTO(
    val id: Long?,

    @field:NotBlank(message = "제목은 필수입니다.")
    val title: String,

    @field:NotBlank(message = "내용은 필수입니다.")
    val content: String,

    @field:NotNull(message = "작성자는 필수입니다.")
    @field:Positive(message = "작성자는 양수여야 합니다.")
    val memberId: Long,

    val guildId: Long?,
)

2. 이게 뭘까?


코틀린에서 애노테이션을 프로퍼티에 적용할 때 @field:, @get:, @set:, @param: 등의 사용 위치 지정자(use-site target)를 사용하지 않으면, 컴파일러가 애노테이션을 적용할 위치를 추론해야 합니다. 이때, 애노테이션은 기본적으로 프로퍼티의 게터 메소드에 적용됩니다. 따라서 @Positive@NotBlank와 같은 검증 애노테이션을 필드에 직접 적용하려면 명시적으로 @field:를 사용해야 합니다.

예를 들어, 아래와 같이 애노테이션을 작성할 경우:

import jakarta.validation.constraints.Positive

data class CreatePostRequestDTO(
    val memberId: Long
)

여기서 @Positive 애노테이션을 단순히 @Positive로 적용하면:

@Positive
val memberId: Long

이 애노테이션은 memberId 프로퍼티의 게터 메소드에 적용됩니다. 게터 메소드에 적용된 @Positive는 실행 시점에 해당 게터가 호출될 때 검증 로직이 적용되는데, 이는 일반적으로 원하는 동작이 아닙니다. 필드 검증을 위해 애노테이션을 필드 자체에 적용하고자 한다면 다음과 같이 명시적으로 @field: 사용 위치 지정자를 사용해야 합니다:

@field:Positive
val memberId: Long