[Spring] 메시지, 커맨드 객체 검증

Fortice·2021년 2월 19일
0

Spring

목록 보기
10/13
post-thumbnail
post-custom-banner

스프링 프로퍼티 값 사용하기

먼저 스프링에서 프로퍼티 값을 가져오는 방법을 알아본다. (Named, Inject 어노테이션을 통한 주입)

1. @Value 어노테이션으로 변수에 할당

@Named
public class MyClass {
>
    @Value("${prop.name}")
    private String propName;
 >
    public void valueMethod() {
        System.out.println(String.format("propName    : %s", propName));
    }
}

2. @ConfigurationProperties 어노테이션을 사용하여 Bean으로 매핑

@Data
@Named 
@ConfigurationProperties(prefix = "prop")
public class AppConfig {
    private String name;
}

@Named
public class MyClass {
 
    @Inject
    private AppConfig appConfig;
 
    public void configMethod() {
        System.out.println(String.format("appName    : %s", appConfig.getName()));
    }
}

3. Environment를 주입받아 property key 로 직접 획득

@Named
public class MyClass {
 
    @Inject
    private Environment environment;
 
    public void envMethod() {
        System.out.println(String.format("propName    : %s", environment.getProperty("prop.name")));
    }
}

메시지

문자열을 별도 파일에 작성하고 JSP 코드에서 이를 사용하는 방법을 설명한다.

  • 문자열을 담은 메시지 파일 작성 (property 파일)
  • 메시지 파일에서 값을 읽어오는 MessageSource 빈을 설정
  • JSP 코드에서 <spring-message>태그를 사용해서 메시지 출력

1. 메시지 파일 생성

src/main/resources/message/label.properties에 맞춰 파일 생성.
파일 설정을 UTF-8 인코딩을 하도록 바꿔야 한글 안꺠짐.

msg.test=테스트
term=약관
name=이름
tag.argument.one=<strong>{0}</strong>

2. MessageSource타입의 빈 추가

src.main/java/config/MvcConfig.java

@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
    ... 생략

    @Bean //빈의 아이디는 무조건 messageSource로 해야함
    public MessageSource messageSource() {
    	ResourceBundleMessageSource ms = new ResourceBundleMessageSource();
        ms.setBasenames("message.label"); //message.프로퍼티파일 이름
        ms.setDefaultEncoding("UTF-8"); //인코딩 설정
        return ms;
    }
}

3. JSP 코드에 사용

message 태그 사용 설정

태그 사용

  • <spring:message code="property 이름" />

Argument 설정

  • <spring:message code="tag.argument.one" arguments="value"/>
  • 여러개일 경우
    • argument 값을 콤마로 구분
    • 객체 배열 사용
    • message 태그 안에 <spring:argument value="value"/> 태그 사용
<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="term" /></title>
</head>
<body>
    <h2>
        <spring:message code="msg.test" />
        <spring:message code="tag.argument.one" arguments="${registerRequest.name}"/>
        <spring:message code="tag.argument.one">
            <spring:argument value="${registerRequest.name}"/>
        <spring:message/>
    </h2>
</body>
</html>

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

입력 받은 값에 대해서는 검증이 필요한데 스프링은 이를 위한 방법을 제공한다.

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

1. 커맨드 객체의 값 검증

스프링 MVC에서는 커맨드 객체의 값 검증을 위해 두가지 인터페이스를 사용한다.

  • org.springframework.validation.Validator
  • org.springframework.validation.Errors
public interface Validator {
    boolean supports(Class<?> clazz);
    void validate(Object target, Errors errors);
}

supports 메소드로 검증이 가능한 타입인지 검사하고, validate 메소드로 객체를 검증한 후 오류 결과를 Errors 객체에 담는다.

아래는 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;

    public RegisterRequestValidator() {
	pattern = Pattern.compile(emailRegExp);
	System.out.println("RegisterRequestValidator#new(): " + this);
    }

    @Override
    public boolean supports(Class<?> clazz) {
        //검증이 가능한지 검사
    	return RegisterRequest.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
    	System.out.println("RegisterRequestValidator#validate(): " + this);
        //타겟 주입
	RegisterRequest regReq = (RegisterRequest) target;
        //값이 null이거나 빈칸인지 검사
	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");
	    }
	}
	//위 작업과 같지만, ValidationUtils를 이용해 간단하게 설정.
	ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "required");
	ValidationUtils.rejectIfEmpty(errors, "password", "required");
	if (!regReq.getPassword().isEmpty()) {
	    if (!regReq.isPasswordEqualToConfirmPassword()) {
	        errors.rejectValue("confirmPassword", "nomatch");
	    }
	}
    }
}

2. 컨트롤러 처리

검증을 한 값을 통해 컨트롤러에서 처리하는 코드이다.

@PostMapping("/register/step3")
public String handleStep3(RegisterRequest regReq, Errors errors) {
    // 검증 객체를 통한 검증
    new RegisterRequestValidator().validate(regReq, errors);
    // Errors 객체를 넘겨 이 객체에 Error를 기록하고, errors의 내용을 통해 매핑해줌
    if (errors.hasErrors())
        return "register/step2";
    try {
        memberRegisterService.regist(regReq);
        return "register/step3";
    } catch (DuplicateMemberException ex) {
        errors.rejectValue("email", "duplicate");
        return "register/step2";
    }
}

3. Errors, ValidationUtils

우리는 여러 방법을 통해 Errors에 에러를 저장하고 이를 통해 처리를 한다.
Errors 인터페이스 메소드, ValidationUtils 클래스 메소드 통해 에러를 간단히 기록하는 방법들을 알아본다.

  • Errors

    • reject(String errorCode, @Nullable String defaultMessage)
    • reject(String errorCode, Object[] errorArgs, String defaultMessage)
    • rejectValue(String field, String errorCode, @Nullable String defaultMessage)
    • rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage)
  • ValidationUtils

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

errorCode : 에러를 식별하기 위한 해당하는 문자열 코드
defaultMessage : 에러에 해당하는 Message가 없을 경우 기본 메시지
errorArgs : 메시지에 인덱스 기반 변수가 있을 시 삽입될 값 객체
field : JSP의 에러메시지 출력에 해당되는 태그 프로퍼티 명

4. JSP에 에러 메시지 출력

스프링이 제공하는 <form:errors path="field"/>를 통해 에러 메시지를 출력 가능하다.

<%@ page contentType="text/html; charset=utf-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>
<html>
<head>
    <title><spring:message code="member.register" /></title>
</head>
<body>
    <h2><spring:message code="member.info" /></h2>
    <form:form action="step3" modelAttribute="registerRequest">
    <p>
        <label><spring:message code="email" />:<br>
        <form:input path="email" />
        <form:errors path="email"/>
        </label>
    </p>
    <p>
        <label><spring:message code="name" />:<br>
        <form:input path="name" />
        <form:errors path="name"/>
        </label>
    </p>
    <input type="submit" value="<spring:message code="register.btn" />">
    </form:form>
</body>
</html>

에러는 에러코드, 커맨드객체이름, 필드명, 필드타입 등에 의해 찾아서 해당되는 메시지를 출력해준다. 코드 중첩 시 순서는 작은 범위에서 점차 넓어진다고 보면 된다.

위에 출력될 메시지는 프로퍼티를 통해 등록한다.

required=필수항목입니다.
bad.email=이메일이 올바르지 않습니다.
duplicate.email=중복된 이메일입니다.
nomatch.confirmPassword=비밀번호와 확인이 일치하지 않습니다.

Validator 범위 (글로벌, 컨트롤러)

1. 글로벌 범위 (getValidator(), @Valid)

글로벌 범위의 Validator는 모든 컨트롤러에 적용할 수 있는 Validator이다.
이는 두 가지 설정을 통해 가능하다.

  • 설정 클래스에서 WebMvcConfigurer의 getValidatior()메소드가 Validator 구현 객체를 리턴하도록 구현.
  • 글로벌 범위 Validator가 검증할 커맨드 객체에 @Valid 어노테이션 적용
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

	@Override
	public Validator getValidator() {
		return new RegisterRequestValidator();
	}
}

@Valid 어노테이션은 검증할 객체에 붙이면 된다. 대신 Errors 객체가 없으면 검증 실패 시 400에러를 응답하게되므로 넣어주는게 좋다.

@PostMapping("/test")
public String handleStep3(@Valid CommandObj com, Errors errors) {}

글로벌 Validator는 전체 컨트롤러에 적용하는 만큼, 공통적인 부분에만 사용하는게 좋다. 위 커맨드 객체의 경우 특정 상황에만 사용하므로 적절하지 않다.

2. 컨트롤러 범위 (@InitBinder, @Valid)

컨트롤러 범위는 @InitBinder 어노테이션을 이용해 설정 가능하다. 이 또한 객체를 @Valid 어노테이션으로 지정해주어야한다.

@PostMapping("/test")
public String handleStep3(@Valid CommandObj com, Errors errors) {}

@InitBinder
protected void initBinder(WebDataBinder binder) {
    binder.addValidators(new RegisterRequestValidator());
}

Bean Validation

@Valid 어노테이션과 함께 Bean Validation이 제공하는 @NotNull, @Digits, @Size 등의 어노테이셔능ㄹ 통해 Validator 정의 없이 검증이 가능하다.

Bean Validation이 제공하는 어노테이션을 이용해 검증하는 방법은 아래와 같다.

  • Bean Validation 관련 의존 설정
  • 커맨드 객체에 어노테이션을 이용해 검증 규칙 설정

예제의 의존성은 hibernate-validator를 사용한다.

1. 어노테이션 설정하기

@Getter
@Setter
public class RegisterRequest {
	@NotBlank
	@Email
	private String email;
	@Size(min = 6)
	private String password;
	@NotEmpty
	private String confirmPassword;
	@NotEmpty
	private String name;
}

2. OptionalValidatorFactoryBean 클래스 빈 등록

커맨드 객체를 검증할 ptionalValidatorFactoryBean 클래스를 등록한다.
@EnableWebMvc 어노테이션이 글로벌 범위로 자동으로 등록해주기 때문에 추가로 설정하지 않아도 된다.

글로벌 범위 Validator를 따로 사용하면 자동으로 등록해주지 않기 때문에 주의한다.

3. @Valid 어노테이션으로 검증 설정

이는 기존의 방법처럼 하면 된다.

4. 어노테이션 종류

어노테이션 종류는 True/False 검사, 값의 크기, 자릿수, 길이, Null 등 매우 다양하다.
Java Bean Validation Basic 사이트에서 필요할 때 찾아 쓰면 된다.

profile
서버 공부합니다.
post-custom-banner

0개의 댓글