Study/spring

[Java] Spring boot 3 @NotNull @NotEmpty @NotBlank 차이 비교

 

 

@NotNull, @NotEmpty, @NotBlank 어노테이션은 Spring에서 유효성 검사할 때 많이 사용하는 어노테이션입니다.

세 어노테이션의 차이와 용도에 대해 정리해보았습니다.

 

준비

import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import lombok.Getter;

@RestController
public class ValidAnnotationController {

    @GetMapping("/valid/notnull")
    public ValidObject checkNotNull(@Valid ValidObject object) {
        return object;
    }
    
    @PostMapping("/valid/notnull")
    public ValidObject checkNotNullPost(@Valid @RequestBody ValidObject object) {
        return object;
    }
    
    @GetMapping("/valid/notEmpty")
    public ValidObject checkNotEmpty(@Valid ValidObject object) {
        return object;
    }
    
    @PostMapping("/valid/notEmpty")
    public ValidObject checkNotEmptyPost(@Valid @RequestBody ValidObject object) {
        return object;
    }
    
    @GetMapping("/valid/notBlank")
    public ValidObject checkNotBlank(@Valid ValidObject object) {
        return object;
    }

    @PostMapping("/valid/notBlank")
    public ValidObject checkNotBlankPost(@Valid @RequestBody ValidObject object) {
        return object;
    }


    @Getter
    @AllArgsConstructor
    public static class ValidObject {

        private String string;
        private Integer integer;
        private Object object;
        private List<String> list;
        private CustomObject customObject;

    }

    @Getter
    @AllArgsConstructor
    public static class CustomObject {
        private Integer number;
    }
}

 

유효성 검사를 적용할 객체와 컨트롤러를 생성했습니다.

 

 

@NotNull

유효성 검사를 적용할 객체에 @NotNull 어노테이션을 붙여줍니다.

@Getter
@AllArgsConstructor
public static class ValidObject {

    @NotNull(message = "String is Not Null")
    private String string;
    @NotNull(message = "Integer is Not Null")
    private Integer integer;
    @NotNull(message = "Object is Not Null")
    private Object object;
    @NotNull(message = "List is Not Null")
    private List<String> list;
    @NotNull(message = "CustomObject is Not Null")
    private CustomObject customObject;

}

@Getter
@AllArgsConstructor
public static class CustomObject {
    private Integer number;
}

 

 

 

 

 

 

먼저, 요청 값 없이 API를 실행해보겠습니다.

요청 값 없이 GET API 실행(Param 방식)
요청 값 없이 GET API 실행(Param 방식)
요청 값 없이 POST API 실행(RequestBody)
요청 값 없이 POST API 실행(RequestBody)

 

@NotNull 어노테이션이 붙어있기 때문에 값을 보내지 않고 요청을 실행하면 오류가 반환됩니다.

@NotNull 어노테이션은 값이 Null일 때 오류를 반환하기 때문에 Null로 들어온 변수의 개수만큼 에러카운트가 집계되었습니다.

 

주의사항

@NotNull을 GET Param 방식으로 사용했을 때 문제점이 있습니다.

Integer 타입을 제외한 모든 변수가 @NotNull 제약 조건을 통과
Integer 타입을 제외한 모든 변수가 @NotNull 제약 조건을 통과

 

Param에 변수만 넣고 값을 넣지 않았을 때 @NotNull 제약조건을 통과하는 경우가 있습니다.

key= 형식으로 사용하게 되면 빈 문자열이 들어가기 때문에 @NotNull 조건을 통과하게 됩니다.

하지만 Integer 타입은 빈 문자열을 숫자로 변환할 수가 없어서 @NotNull 제약 조건에 걸렸습니다.

 

정리하면, @NotNull은 값이 Null일 때만 검사를 합니다.

빈 문자열이든 무엇이든 값이 있기만 하면 검사를 통과할 수 있습니다.

 

@NotEmpty

@NotEmpty 어노테이션은 Null이나 빈 문자열 등 비어있는 상태를 체크합니다.

 

유효성 검사를 체크할 객체를 준비합니다.

@Getter
@AllArgsConstructor
public static class ValidObject {

    @NotEmpty(message = "String is Not Empty")
    private String string;
    @NotEmpty(message = "List is Not Empty")
    private List<String> list;

}

@Getter
@AllArgsConstructor
public static class CustomObject {
    private Integer number;
}

 

이번에는 Integer 타입과 Object 타입, 사용자 정의 객체 타입을 제외했습니다.

그 이유는 @NotEmpty는 유효성 검사를 진행할 때 길이 값을 사용하는데, Integer같은 숫자 타입이나 Object 타입은 길이가 없어서 @NotEmpty를 사용할 수 없기 때문입니다.

@NotEmpty를 쓸 수 없는 타입에 사용하면 이런 오류가 발생합니다.
HV000030: No validator could be found for constraint 'jakarta.validation.constraints.NotEmpty' validating type 'java.lang.Object'. Check configuration for '<변수명>'

 

@NotEmpty 사용 가능 여부는 @NotEmpty 유효성 검사보다 먼저 진행되기 때문에 변수에 @NotEmpty를 사용할 수 있는지 없는지 여부를 먼저 확인해야 합니다.

 

@NotEmpty Param 방식 GET API 요청 결과
@NotEmpty Param 방식 GET API 요청 결과
@NotEmpty Param 값이 비어있을 때 API 요청 결과
@NotEmpty Param 값이 비어있을 때 API 요청 결과
@NotEmpty RequestBody 값이 비어있을 때 유효성 검사 결과
@NotEmpty RequestBody 값이 비어있을 때 유효성 검사 결과

값이 Null이거나 빈 문자열이거나 빈 컬렉션일 때 유효성 검사를 통과하지 못합니다.

빈 문자열이나 빈 컬렉션은 길이가 0이기 때문입니다.

 

정리하면, @NotEmpty는 null이 아니거나 길이가 0보다 커야 유효성 검사를 통과할 수 있습니다.

 

 

 

 

 

 

@NotBlank

@NotBlank를 사용하기 위한 객체입니다.

@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class ValidObject {

    @NotBlank(message = "String is Not Blank")
    private String string;

}

 

이전과는 다르게 String 변수만 존재합니다.

 

그 이유는 @NotBlank는 문자열, 정확하게 말하면 CharSequence 타입에만 사용할 수 있기 때문입니다.

@NotBlank 역시 사용할 수 없는 타입에 사용하게되면 오류가 발생합니다.

HV000030: No validator could be found for constraint 'jakarta.validation.constraints.NotBlank' validating type 'java.util.List<java.lang.String>'. Check configuration for '<변수명>'

 

 

@NotBlank가 적용된 결과입니다.

POST API RequestBody @NotEmpty 유효성 검사 결과

 

@NotBlank는 이름 그대로 공백을 허용하지 않는다는 의미입니다.

그래서 길이가 0을 넘겨도 공백 문자만 있으면 유효성 검사를 통과하지 못합니다.  

 

 

결론

3가지 어노테이션의 용도와 차이점을 정리하면,

@NotNull -> Null 여부만 체크합니다. 그래서 Null만 아니면 통과할 수 있습니다.

@NotEmpty -> Null 여부와 길이를 체크합니다. 그래서 길이를 체크할 수 있는 타입(String, Collection 등)만 사용 가능합니다.

@NotBlank -> Null 여부와 공백 여부를 체크합니다. 문자열 타입만 사용할 수 있습니다.