java.validation의 @Valid 어노테이션 사용법 정리 글입니다.

Spring Boot 라이브러리에서 기본적으로 탑재된 기능이며 따로 dependency해 줄 필요가 없습니다.

Spring Boot Version은 2.2.2.RELEASE 입니다.


1. java.validation 어노테이션 설명

Anotation 제약조건
@NotNull Null 불가
@Null Null만 입력 가능
@NotEmpty Null, 빈 문자열 불가
@NotBlank Null, 빈 문자열, 스페이스만 있는 문자열 불가
@Size(min=,max=) 문자열, 배열등의 크기가 만족하는가?
@Pattern(regex=) 정규식을 만족하는가?
@Max(숫자) 지정 값 이하인가?
@Min(숫자) 지정 값 이상인가
@Future 현재 보다 미래인가?
@Past 현재 보다 과거인가?
@Positive 양수만 가능
@PositiveOrZero 양수와 0만 가능
@Negative 음수만 가능
@NegativeOrZero 음수와 0만 가능
@Email 이메일 형식만 가능
@Digits(integer=, fraction = ) 대상 수가 지정된 정수와 소수 자리 수 보다 작은가?
@DecimalMax(value=)  지정된 값(실수) 이하인가?
@DecimalMin(value=) 지정된 값(실수) 이상인가?
@AssertFalse false 인가?
@AssertTrue true 인가?

2. Member.java

import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

public class Member {

    @NotNull(message = "id는 필수 값입니다.")
    @Size(min = 5, max = 10)
    private String id;

    @Max(value = 25, message = "25세 이하만 가능합니다.")
    @Min(value = 18, message = "18살 이상만 가능합니다.")
    private int age;

    @Pattern(regexp = "[a-zA-z0-9]+@[a-zA-z]+[.]+[a-zA-z.]+")
    private String email;
    
    ..setter/getter 생략..
}

3. TestController.java

import javax.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "/member")
public class TestController {
    
    @PostMapping
    @ResponseBody
    public ResponseEntity saveMember(@Valid Member member, BindingResult bindingResult) {
        
        if (bindingResult.hasErrors()) {
            return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(bindingResult.getAllErrors());
        }
        
        /*
        	save Memeber
        */
        
        return ResponseEntity.ok(member);
    }
}
  • [post] /member요청 시, member를 저장하는 코드입니다.
  • @Valid 어노테이션을 통해 Member가 유효한 객체인지 검사합니다.
  • Member 객체가 유효하지 않으면 bindingResult.hasErrors() 메소드에서 true 값이 반환됩니다.
  • Member가 유효하지 않다면, ResponseEntity에 BAD_REQUEST와 bindingResult.getAllErrors() 값을 넣고 생성하여 반환합니다.

4. 테스트

POST http://localhost:8080/{contextPath}/member?id=ki&age=17&email=bamdule@navercom

Reponse 400 Bad Request

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

@SpringBootApplication
public class BamduleApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(BamduleApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(BamduleApplication.class, args);
    }

}

Spring Boot 실행 클래스에서 SpringBootServletInitializer 클래스를 상속 받는 것을 볼 수 있습니다.

SpringBootServletInitializer 클래스를 왜 상속 받아야 할까요?

Spring개발 시  web.xml에 DispatcherServlet을 등록하는 작업이 필요했습니다.

하지만 Servlet 3.0으로 업데이트 되면서 web.xml이 없어도 배포가 가능해졌습니다.

그 이유는 web.xml 설정을 WebApplicationInitializer 인터페이스를 구현하여 대신 할 수 있고,
프로그래밍적으로 Spring IoC 컨테이너를 생성하여 ServletContext에 추가할 수 있도록 변경 되었기 때문입니다.

SpringBootServletInitializer 클래스는 WebApplicationInitializer 인터페이스의 구현체입니다. 

결과적으로 SpringBootServletInitializer 상속 한다는 것은 Spring Boot 애플리케이션 동작이 가능 하도록 웹 애플리케이션 컨텍스트(IoC 방식으로 Bean을 관리하는 컨테이너) 구성 한다는 의미입니다.

1. XXXApplication.java

스프링부트는 main 메소드가 선언된 클래스를 기준으로 실행됩니다.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class BamduleApplication {

	public static void main(String[] args) {
		SpringApplication.run(BamduleApplication.class, args);
	}

}
@SpringBootApplication 어노테이션은 스프링 부트의 가장 기본적인 설정을 선언해 줍니다.
해당 어노테이션을 보면 아래와 같은 어노테이션이 다시 선언되어 있습니다.

2. @SpringBootApplication

@Target(value = {ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
    @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}),
    @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})})
public @interface SpringBootApplication {

    @AliasFor(annotation = EnableAutoConfiguration.class)
    public Class<?>[] exclude() default {};

    @AliasFor(annotation = EnableAutoConfiguration.class)
    public String[] excludeName() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
    public String[] scanBasePackages() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
    public Class<?>[] scanBasePackageClasses() default {};

    @AliasFor(annotation = ComponentScan.class, attribute = "nameGenerator")
    public Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    @AliasFor(annotation = Configuration.class)
    public boolean proxyBeanMethods() default true;
}
여기서 눈여겨 볼 설정은 @ComponentScan@EnableAutoConfiguration 입니다. 

3. @ComponentScan

@ComponentScan@component 어노테이션 및 @Service, @Repository, @Controller 등의 어노테이션을 스캔하여 Bean으로 등록해주는 어노테이션입니다.


4. @EnableAutoConfiguration

@EnableAutoConfiguration은 사전에 정의한 라이브러리들을  Bean으로 등록해 주는 어노테이션입니다. 
사전에 정의한 라이브러리들 모두가 등록되는 것은 아니고 특정 Condition(조건)이 만족될 경우에 Bean으로 등록합니다.

사전 정의 파일 위치
Dependencies > spring-boot-autoconfigure > META-INF > spring.factories
# spring.factories 파일 내용

# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.autoconfigure.BackgroundPreinitializer

# Auto Configuration Import Listeners
org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\
org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener

# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\
...
"org.springframework.boot.autoconfigure.EnableAutoConfiguration=" 에 등록된 클래스들이 자동으로 등록되는 Bean 입니다.  각 Bean은 OnBeanCondition, OnClassCondition, OnWebApplicationCondition 어노테이션의 조건에 의해 등록 여부가 결정됩니다.
@OnBeanCondition : 특정 Bean이 사전에 생성되어있지 않을 경우에 조건이 만족됩니다.
@ConditionalOnBean : 특정 Bean이 이미 생성되어있을 경우에 조건이 만족됩니다.
@ConditionalOnClass : Classpath에 특정 class가 존재할 경우에 조건이 만족됩니다.

1. Spring Boot란

Spring Boot는 Spring 프레임워크를 간편하게 설정하고, 별도에 어려운 설정 없이 바로 개발에 들어갈 수 있도록 만든 프레임 워크입니다.

강력한 기능을 가진 라이브러리들을 Spring Boot 라이브러리에 내장하였고, 내장형 톰캣 등을 탑재하여 단독 실행이 가능해졌습니다.

그리고 프로젝트 생성 시 기존 Spring은 복잡한 설정 파일을 요구했는데, Spring boot는 aplication.yml 설정 파일로 간단하게 설정이 가능하게 되었습니다,

2. 사용하는 라이브러리

...
<!--Spring Boot Version-->	
  <version>2.2.2.RELEASE</version>
...
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

3. 프로젝트 생성

아래 주소에서 Spring Boot 프로젝트를 생성합니다.

https://start.spring.io/

src 폴더와 pom.xml을 제외한 나머지 파일은 사용하지 않으니 삭제해주세요.


4. application.yml 설정

application.yml 파일은 스프링의 설정 정보를 저장하는 파일입니다.

기본적으로 application.profiles파일이 저장되어 있습니다. yml, profiles 두가지 타입의 파일로 설정이 가능합니다.

이번에는 조금 더 보기좋고 설정하기 편한 yml 파일로 설정해보겠습니다. 

파일 경로는 src/main/resources/ 입니다.

spring:
    thymeleaf:
        prefix : classpath:templates/
        suffix : .html
        cache : false

우선 기본적인 thymeleaf 설정만 하였습니다.

html 파일의 위치와 확장자 명, 캐시 유무를 설정하였습니다.


5. HomeController 생성

@SpringBootApplication를 선언한 클래스 패키지의 하위 패키지에 HomeController를 생성합니다.

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping(value = "/")
public class HomeController {

    @GetMapping(value = "/")
    public ModelAndView home() {
        ModelAndView modelAndView = new ModelAndView();
        
        modelAndView.setViewName("home");
        
        Map<String, Object> map = new HashMap<>();
        map.put("name", "Bamdule");
        map.put("date", LocalDateTime.now());
        
        modelAndView.addObject("data", map);
        
        return modelAndView;
    }
}

modelAndView.setViewName("home"); 은 application.yml에서 설정한 "src/main/resources/" 값이 생략되어있고, 
확장자 .html 또한 생략되어있습니다. 이 값을 통해 ViewResolver는 view를 생성합니다.

그리고 modelAndView.addObject("data",map); 를 통해 화면에 데이터를 렌더링합니다.


6. home.html 생성

src/main/resources/templates/ 경로에 home.html을 생성해줍니다.

<!DOCTYPE html>
<html>
    <head>
        <title>title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>
    <body>
        <div th:text="${data.name}"></div>
        <th:block th:text="${data.date}"/>
    </body>
</html>

"th:" 가 포함된 tag attribute는 thymeleaf 문법입니다.

data.name과 data.date 값을 화면에 뿌려줍니다.


7. 결과

위와 같이 하셨나면 톰캣을 실행해 줍니다. 

build하면 target 폴더에 war 파일이 생성되고, war파일을 설치한 톰캣 또는 IDE툴을 통해서 실행하면 다음과 같은 화면이 보여집니다.


도움이 되셨다면 공감 한번씩 눌러주시면 감사하겠습니다.

+ Recent posts