상세 컨텐츠

본문 제목

[스프링1] 12장. MVC2: 메시지, 커맨드 객체 검증

22-23/22-23 Spring 1

by YUZ 유즈 2022. 12. 1. 10:00

본문

728x90

해당 포스트는 초보 웹 개발자를 위한 스프링 5 프로그래밍 입문 [최범균 저] 책 내용을 참고하였습니다.

❗ <spring:message>태그로 메시지 출력하기

홈페이지를 개발할 때 다국어 지원이 필요하다면 우리는 각 언어별로 사용할 문자열을 파일에 보관하고 알맞을 때 꺼내 쓸 필요가 있다. 스프링은 이 기능을 <spring: message> 태그로 제공하고 있다. 전체적인 방법은 다음과 같다.

  • 문자열을 담은 메시지 파일을 작성한다.
  • 메시지 파일에서 값을 읽어오는 MessageSource 빈을 설정한다.
  • JSP 코드에서 <spring: message> 태그를 사용하여 메시지를 출력한다.

📌메시지 파일 작성

메시지 파일은 프로퍼티 파일 형식으로 작성한다.

src/main/resources 에 message 폴더를 생성하고 label.properties 파일을 생성한다. 인코딩은 'UTF-8'로 설정해야 한다.

member.register=회원가입

term=약관
term.agree=약관동의
next.btn=다음단계

member.info=회원정보
email=이메일
name=이름
password=비밀번호
password.confirm=비밀번호 확인
register.btn=가입 완료

register.done=<strong>{0}님 ({1})</strong>, 회원 가입을 완료했습니다.

go.main=메인으로 이동

📌MessageSource 타입 빈 추가

MvcConfig 설정 클래스에 MessageSource 타입의 빈을 추가하자.

import org.springframework.context.MessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;


@Bean
public MessageSource messageSource() {
	ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
    ms.setBasenames("message.label"); // message 패키지 내에 label 프로퍼티 파일을 불러옴
    ms.setDefaultEncoding("UTF-8");
    resturn ms;
}

📌JSP 코드에서 메시지 불러오기

다음과 같이 작성해주면 된다.

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!-- <spring:message> 커스텀 태그를 사용하기 위해 태그 라이브러리 설정 추가 -->
...
<head>
	<title><spring:message code="member.register"/></title>
</head>
...
<body>
	<p>
    	<spring:message code="register.done"
        	arguments="${registerRequest.name}, ${registerRequest.email}" />
    </p>

 

📌 MessageSource와 <spring:message> 태그

public interface MessageSource {
	String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
    String getMessage(String code, Object[] args, Locale locale)
    	throws NoSuchMessageException;
    ....
}
  • code : 메시지를 구분하기 위한 파라미터
  • locale : 지역 구분을 위한 파라미터
  • args : 프로퍼티 파일에 {0} 과 같은 인덱스 기반 변수에 들어갈 파라미터 배열

 

❗ 커맨드 객체의 값 검증과 에러 메시지 처리

회원 가입 처리를 할 때 비정상 값이 입력될 경우를 대비하여 우리는 입력한 값을 검증해야 할 필요가 있다.

값을 검증하고, 잘못된 값일 경우 적절한 에러 메시지를 출력하는 방법을 알아보자. 다음과 같은 방법으로 진행된다.

 

  • 커맨드 객체를 검증하고 결과를 에러 코드로 저장
  • JSP에서 에러 코드로부터 메시지를 출력

📌 커맨드 객체 검증과 에러 코드 지정하기

다음의 두 인터페이스를 활용하여 커맨드 객체의 값을 검사할 수 있다.

  • org.springframework.validation.Validator
  • org.springframework.validation.Errors

RegisterRequestValidator.java

import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;

...

public class RegisterRequestValidator implements Validator {
	private static final String emailRegExp =
    	"^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@" +
        "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2.}$";
     private Pattern pattern;
     
     // clazz 객체가 RegisterRequest 클래스로 타입 변환이 가능한지 확인한다
     @Override
     public boolean supports(Class<?> clazz) {
     	retrn RegisterRequest.class.isAssignableFrom(clazz);
     }
     
     // target 파라미터 : 검사 대상 객체
     // errors 파라미터 : 검사 결과 에러 코드를 설정하기 위한 객체
     @Override
     public void validate(Object target, Errors errors) {
     	RegisterRequest regReq = (RegisterRequest) target;
        // "email"프로퍼티의 값이 유효한지 검사
        if(regReq.getEmail() == null || regReq.getEmail().trim().isEmpty()) { // 검사
        	errors.rejectValue("email", "required"); // 에러 코드 저장
        } else {
        	Matcher matcher = pattern.matcher(regReq.getEmail());
            if(!matcher.matches()) {
            	errors.rejectValue("email", "bad"); // 에러 코드 저장
            }
        }
        // "name" 프로퍼티가 null이거나 공백이면 에러코드로 "required" 추가
        ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required");
        ValidationUtils.rejectIfEmpty(errors, "password", "required");
        ValidationUtils.rejectIfEmpty(errors, "confirmPassword", "required");
        if(!regReq.getPassword().isEmpty()) {
        	if(!regReq.isPasswordEqualToConfirmPassword()) {
            	errors.rejectValue("confirmPassword", "nomatch");
            }
        }
    }
}
@PostMapping("/register/step3")
// Errors 객체는 커맨드 객체의 특정 프로터리 값을 구할 수 있는 getFieldValue() 메서드 제공
// ValidationUtils.rejectIfEmptyOrWhitespace() 메서드는 커맨드 객체를 전달받지 않아도
// Errors 객체의 getFieldValue("name") 메서드를 실행하여 프로퍼티 값 접근 가능
public String handleStep3(RegisterRequest regReq, Errors errors) {
	// 검사
	new RegisterRequestValidator().validate(regReq, errors);
    // 한번이라도 유효하지 않은 값이 존재하면 실행
    if(errors.hasErrors())
    	return "register/step2";

참고 : Errors  타입 파라미터는 반드시 커맨드 객체를 위한 파라미터 다음에 위치해야 한다.

 

📌 Erros와 ValidationUtils 클래스의 주요 클래스

Erros 인터페이스의 에러 코드 추가 메서드

  • reject(String errorCode)
  • reject(String errorCode, String defaultMessage)
  • reject(String errorCode, Object[] errorArgs, String defaultMessage)
  • reject(String field, String errorCode)
  • reject(String field, String errorCode, String defulatMessage)
  • reject(String field, String errorCode, Object[] errorArgs, String defaultMessage)

인덱스 기반 변수를 포함하고 있으면 Object 타입의 errorArgs 파라미터를 이용하면 된다.

defaultMessage는 에러 코드가 존재하지 않을 때 해당 메시지를 출력한다.

 

ValidationUtils 클래스의 메서드

  • rejectIfEmpty(Errors errors, String field, String errorCode)
  • rejectIfEmpty(Errors errors, String field, String errorCode, Object[] errorArgs)
  • rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode)
  • rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, Object[] errorArgs)

rejectIfEmpty는 field 값이 null이거나 빈 문자열이면 errorCode 추가

rejectIfEmptyOrWhitespace는 field값이 null이거나 빈 문자열, 공백문자로만 구성된 문자열일 경우에 errorCode를 추가한다.

 

❗ 글로벌 범위 Validator와 컨트롤러 범위 Validator

스프링 MVC는 모든 컨트롤러에 적용할 수 있는 글로벌 Validator와 단일 컨트롤러에만 적용할 수 있는 Validator를 설정할 수 있게 하였다.

 

📌 글로벌 범위 Validator 설정

두 가지를 선행해야 한다.

  • 설정 클래스에서 WebMvcConfigurer의 getValidator 메서드가 Validator 구현 객체를 리턴하도록 구현
  • 글로벌 범위 Validator가 검증할 커맨드 객체에 @Valid 애노테이션 적용

WebMvcConfigurer

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
	@Override
    public Validator getValidator() {
    		return new RegisterRequestValidator();
    }

적용 방법

import javax.validation.Valid;

@PostMapping("/register/step3")
public String functions(@Valid RegsterRequest regReq, Errors errors) {
	...

@Valid 애노테이션 사용 시 Errors 타입 파라미터가 없으면 검증 실패 시 http 400 에러를 발생시킨다.

 

📌 @InitBinder 애노테이션을 이용한 컨트롤러 범위 Validator

import javax.validation.Valid;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;

@PostMapping("/register/step3")
public String functions(@Valid RegsterRequest regReq, Errors errors) {
	...
    
@initBinder
protected void initBinder(WebDataBinder binder) {
	binder.setValidator(new RegisterRequestValidator());
}

매핑 요청을 처리하기 전에 initBinder가 실행되어 Validator를 설정한다.

 

 

❗ Bean Validation

Bean Validation이 제공하는 애노테이션을 이용하면 굳이 Validator를 작성하지 않더라도 커맨드 객체 값을 검증할 수 있다.

 

방법은 다음과 같다.

  • Bean Validation과 관련된 의존을 설정에 추가한다.
  • 커맨드 객체에 @NotNull, @Digits 등의 애노테이션을 추가하여 검증 규칙을 작성한다.

📌 의존 관계 추가

pom.xml

<dependency>
	<groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>1.1.0.Final</version>
</dependency>
<dependency>
	<groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>5.4.2.Final</version>
</dependency>

📌 검증 규칙 작성

public class RegisterRequest {
	@NotBlank
    @Email
    private String email;
    @Size(min=6)
	private String password;
    @NotEmpty
    private String confirmPassword;
    @NotEmpty
    private String name;
  • @NotMepty : 빈 값인지
  • @Size : 지정한 크기를 갖는지
  • @AssertTrue : 값의 true, false 값 검사
  • @DecimalMax, @DecimalMin : 최대값 또는 최솟값 검사
  • @Max, @Min : 지정한 값보다 작거나 같은지, 또는 크거나 같은지 검사

 

이전처럼 @Valid 애노테이션을 붙인 클래스에 한해 자동으로 직접 작성한 RegisterRequest가 연결된다.


Spring 1

EDITOR: PORO

 

728x90

관련글 더보기