본문 바로가기
IT

[Java/Spring Boot 3] 유효성 검사 어노테이션 완벽 비교: @NotNull, @NotEmpty, @NotBlank 차이점과 사용법

by 굿센스굿 2025. 5. 9.
반응형

 

Spring Boot 3 프로젝트에서 데이터를 검증하는 데 있어 가장 많이 사용되는 어노테이션 중 세 가지가 있습니다. 바로 @NotNull, @NotEmpty, @NotBlank입니다. 이 어노테이션들은 모두 Jakarta Bean Validation(Jakarta Validation API)의 일부로, 클라이언트로부터 받은 데이터를 서버 측에서 검증할 때 강력한 도구로 활용됩니다. 하지만 이 세 가지 어노테이션은 이름이 비슷한 만큼 혼동하기 쉽고, 사용 목적과 적용 대상이 미묘하게 다릅니다.

이번 포스팅에서는 세 어노테이션의 개념, 적용 대상, 실무 예제, 오류 메시지, 주의사항까지 모두 정리하여 Spring Boot 개발자라면 누구나 쉽게 이해하고 사용할 수 있도록 설명드리겠습니다.


✅ 유효성 검사 어노테이션 요약

어노테이션 Null 허용 여부 빈 값 허용 여부 공백 허용 여부 적용 가능한 타입

@NotNull ❌ 불가 ⭕ 허용 ⭕ 허용 모든 참조 타입
@NotEmpty ❌ 불가 ❌ 불가 ⭕ 허용 문자열, 컬렉션, 배열 등 길이 있는 타입
@NotBlank ❌ 불가 ❌ 불가 ❌ 불가 문자열(CharSequence) 타입

각 어노테이션이 검사하는 대상이 다르기 때문에 잘못 사용하면 유효성 검사가 제대로 이루어지지 않을 수 있습니다. 따라서 상황에 맞는 어노테이션을 선택하는 것이 중요합니다.


🧪 테스트 환경 및 샘플 클래스 구성

먼저 아래와 같은 컨트롤러와 DTO 클래스들을 통해 각 어노테이션의 작동 방식을 비교 실험해볼 수 있습니다.

import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
public class ValidAnnotationController {

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

    @PostMapping("/valid/notempty")
    public ValidObjectEmpty checkNotEmpty(@Valid @RequestBody ValidObjectEmpty object) {
        return object;
    }

    @PostMapping("/valid/notblank")
    public ValidObjectBlank checkNotBlank(@Valid @RequestBody ValidObjectBlank object) {
        return object;
    }

    @Getter
    @AllArgsConstructor
    public static class ValidObject {
        @NotNull(message = "String cannot be null")
        private String string;

        @NotNull(message = "Integer cannot be null")
        private Integer integer;

        @NotNull(message = "Object cannot be null")
        private Object object;

        @NotNull(message = "List cannot be null")
        private List<String> list;

        @NotNull(message = "CustomObject cannot be null")
        private CustomObject customObject;
    }

    @Getter
    @AllArgsConstructor
    public static class ValidObjectEmpty {
        @NotEmpty(message = "String cannot be empty")
        private String string;

        @NotEmpty(message = "List cannot be empty")
        private List<String> list;
    }

    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class ValidObjectBlank {
        @NotBlank(message = "String cannot be blank")
        private String string;
    }

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

🟡 @NotNull - 단순히 Null 여부만 체크

@NotNull은 해당 필드가 null이 아니기만 하면 유효성 검사를 통과합니다. 따라서 빈 문자열이나 빈 리스트도 통과합니다.

✅ 예시

@NotNull(message = "Name must not be null")
private String name;

🔎 사용 대상

  • 모든 참조 타입(Object, String, List, Map 등)
  • 숫자 타입 (Integer, Long 등) 포함 가능

❗ 주의사항

  • GET 요청 파라미터의 경우 ?name= 처럼 빈 값을 전달하면 null이 아닌 빈 문자열이기 때문에 유효성 검사를 통과할 수 있습니다.
  • 특히 문자열, 리스트, 맵 등 길이를 가질 수 있는 타입은 null이 아니더라도 값이 없는 상태일 수 있습니다.

📌 실습 결과

{
  "string": "",
  "integer": 5,
  "object": null,
  "list": [],
  "customObject": null
}
  • string은 빈 문자열이라도 통과
  • list는 비어있지만 통과
  • object, customObject가 null이므로 유효성 검사 실패

🟠 @NotEmpty - null과 빈 값을 모두 차단

@NotEmpty는 단순히 null뿐 아니라, **길이가 0인 상태(빈 문자열, 빈 컬렉션)**도 유효성 검사를 실패로 처리합니다.

✅ 예시

@NotEmpty(message = "List must not be empty")
private List<String> tags;

🔎 사용 대상

  • String, List, Set, Map, 배열 등 길이를 가질 수 있는 타입
  • Object, Integer, Double 등 길이가 없는 타입에는 사용 불가

❗ 주의사항

  • null이거나 "", []이면 모두 유효성 검사에 실패합니다.
  • Object, Integer 같은 타입에 사용할 경우 No validator found 예외 발생

📌 실습 결과

{
  "string": "",
  "list": []
}

→ 둘 다 유효성 검사 실패 (""와 빈 리스트는 길이가 0)


🟢 @NotBlank - 공백 문자까지 체크

@NotBlank는 null, 빈 문자열(""), 공백 문자(" ") 모두를 거부합니다. 즉, 실제 내용이 존재하는 텍스트만 허용합니다.

✅ 예시

@NotBlank(message = "Comment must not be blank")
private String comment;

🔎 사용 대상

  • 오직 문자열(CharSequence 하위 타입)만 가능
  • 공백 제거 후 길이가 0인 경우 실패 처리

❗ 주의사항

  • List, Map, Integer 등에 적용하면 No validator found 예외 발생
  • 사용자 입력을 받는 텍스트 필드 검증에 가장 적합

📌 실습 결과

{
  "string": "   "
}

→ 유효성 검사 실패 (공백만 포함)


🚧 잘못된 사용 예시

다음은 각각의 어노테이션을 잘못 적용한 예시입니다. 이처럼 어노테이션마다 허용하는 타입이 다르므로 정확한 이해가 필수입니다.

❌ @NotEmpty on Integer

@NotEmpty
private Integer age;

→ 실행 시 HV000030 오류 발생
해결 방법: @NotNull로 변경


📚 정리: 언제 어떤 어노테이션을 사용할까?

사용 목적 추천 어노테이션

값이 null인지 여부만 체크할 때 @NotNull
컬렉션이나 문자열이 비어있는지 체크할 때 @NotEmpty
문자열이 공백 포함 여부까지 체크할 때 @NotBlank

✅ 실무 팁

  • 사용자 입력 텍스트: @NotBlank 추천
  • 리스트/맵 필수 입력값: @NotEmpty 추천
  • DB 키나 ID: @NotNull 추천

💬 마무리

Spring Boot 3에서 유효성 검사는 API의 신뢰성과 안전성을 보장하는 중요한 과정입니다. @NotNull, @NotEmpty, @NotBlank는 각각의 목적에 맞게 올바르게 사용하면 불필요한 오류를 방지하고 사용자에게 친절한 메시지를 전달할 수 있습니다.

이 세 어노테이션은 단순한 문법 이상으로, 서비스의 품질을 좌우할 수 있는 요소입니다. 이번 포스팅을 통해 각 어노테이션의 역할을 명확히 이해하고, 실무에서 자신 있게 활용해보시기 바랍니다.

 

반응형