*Chapter11은 jsp에 대한 내용을 다루므로 블로깅은 스킵
<spring:message> 태그로 메시지 출력하기
문자열을 별도 파일에 작성하고 JSP 코드에서 이를 시용하려면 다음 작업을 하면 된다.
- 문자열을 담은 메시지 파일을 작성한다.
- 메시지 파일에서 값을 읽어오는 MessageSource 빈을 설정한다.
- JSP 코드에서 spring:message 태그를 사용해서 메시지를 출력한다.
예제
- 메시지 파일 작성(lable.properties)
member.register=회원가입
term=약관
term.agree=약관동의
next.btn=다음단계
member.info=회원정보
email=이메일
name=이름
password=비밀번호
password.confirm=비밀번호 확인
register.btn=가입 완료
register.done=<strong>{0}님</strong>,회원 가입을 완료했습니다.
go.main=메인으로 이동
- 빈 등록(MvcConfig)
@Bean
public MessageSource messageSource() { //무조건 메소드이름을 messageSource로 지정해야한다.
ResourceBundleMessageSource ms =
new ResourceBundleMessageSource();
ms.setBasenames("message.label");
ms.setDefaultEncoding("UTF-8");
return ms;
}
- JSP파일 수정
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ 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="term" /></h2>
<p>약관 내용</p>
<form action="step2" method="post">
<label>
<input type="checkbox" name="agree" value="true">
<spring:message code="term.agree" />
</label>
<input type="submit" value="<spring:message code="next.btn" />" />
</form>
</body>
</html>
다국어를 지원하려면 각 프로퍼티 파일 이름에 언어에 해당하는 로케일 문자를 추가하여 메시지를 관리하면 된다.
label_ko.properties, label_en.properties
커맨드 객체의 값 검증과 에러 메시지 처리
폼에 입력한 값을 검증하지 않으면 잘못된 값이 시스템에 입력되어 어플리케이션이 비정상으로 동작할 수 있다. 또한 에러 메시지를 제대로 보여주지 않으면 사용자는 서비스를 제대로 이용할 수 없게 된다.
스프링은 이 두가지 문제를 처리하기 위해 다음 방법을 제공하고 있다.
- 커맨드 객체를 검증하고 결과를 에러 코드로 저장
- JSP에서 에러 코드로부터 메시지를 출력
커맨드 객체 검증과 에러 코드 지정하기
스프링 MVC에서 커맨드 객체의 값이 올바른지 검사하려면 다음의 두 인터페이스를 사용한다.
- org.springframework.validation.Validator
- org.springframework.validation.Errors
예제
- 검증 및 에러 반환 클래스 생성
package controller;
import org.springframework.validation.*;
import spring.RegisterRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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);
}
// RegisterRequest 클래스로 타입 변환이 가능한지 확인한다.
@Override
public boolean supports(Class<?> clazz) {
return RegisterRequest.class.isAssignableFrom(clazz),
}
// terget: 검사 대상 객체, errors: 검사 결과 에러 코드 설정
// 검사 대상 객체의 특정 프로퍼티나 상태가 올바른지 검사, 올바르지 않다면, Errors의 rejectValue()메서드로 에러코드 저장
@Override
public void validate(Object target, Errors errors) {
RegisterRequest regReq = (RegisterRequest) target;
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.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")
public String handleStep3(RegisterRequest regReq, Errors errors){
new RegisterRequestValidator().validate(regReq, errors);
if(errors.hasErrors()) return "register/step2";
try {
memberRegisterService.regist(regReq);
return "register/step3";
}catch (DuplicateMemberException ex){ // 동일한 이메일이 존재하면
errors.rejectValue("email","duplicate");
return "register/step2";
}
}
위 처럼 Errors 타입 파라미터가 뒤에 위치하면, 스프링 MVC는 메서드를 호출할 때 커맨드 객체와 연결된 Errors 객체를 생성해서 파라미터로 전달한다.
//errors 객체의 getFieldValue("name") 메서드를 실행해서
//커맨드 객체의 name 프로퍼티 값을 구함.
//따라서 커맨드 객체를 직접 전달하지 않아도 값 검증을 할 수 있음
ValidationUtils.rejectlfEmptyOrWhitespace(errors, "name", "required");
커맨드 객체의 에러 메시지 출력하기
member.register=회원가입
term=약관
term.agree=약관동의
next.btn=다음단계
member.info=회원정보
email=이메일
name=이름
password=비밀번호
password.confirm=비밀번호 확인
register.btn=가입 완료
register.done=<strong>{0}님</strong>,회원 가입을 완료했습니다.
go.main=메인으로 이동
required= 필수항목입니다.
bad.email=이메일이 올바르지 않습니다.
duplicate.email=중복된 이메일입니다.
nomatch.confirmPassword=비밀번호와 확인이 일치하지 않습니다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="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>
<p>
<label><spring:message code="password" />:<br>
<form:password path="password" />
<form:errors path="password"/>
</label>
</p>
<p>
<label><spring:message code="password.confirm" />:<br>
<form:password path="confirmPassword" />
<form:errors path="confirmPassword"/>
</label>
</p>
<input type="submit" value="<spring:message code="register.btn" />">
</form:form>
</body>
</html>
글로벌 범위 Validator와 컨트롤러 범위 Validator
스프링 MVC는 모든 컨트롤러에 적용할 수 있는 글로벌 Validator와 단일 컨트롤러 에 적용할 수 있는 Validator 설정하는 방법을 제공한다. 이를 사용하면 @Valid 애노테이션을 사용해서 커맨드 객체에 검증 기능을 적용할 수 있다.
글로벌 범위 Validator 설정과 @Valid 애노테이션
- 설정 클래스에서 WebMvcConfigurer의 getValidator() 메서드가 Validator 구현 객체를 리턴하도록 구현
- 글로벌 범위 Validator가 검증할 커맨드 객체에 @Valid 애노테이션 적용
예제
- MvcConfig에서 오버라이딩
@Override
public Validator getValidator()
{
return new RegisterRequestValidator();
}
// 의존성 추가
implementation group: 'javax.validation', name: 'validation-api', version: '1.1.0.Final'
@PostMapping("/register/step3")
public String handleStep3(@Valid RegisterRequest regReq, Errors errors){
//new RegisterRequestValidator().validate(regReq, errors);
if(errors.hasErrors()) return "register/step2";
try {
memberRegisterService.regist(regReq);
return "register/step3";
}catch (DuplicateMemberException ex){ // 동일한 이메일이 존재하면
errors.rejectValue("email","duplicate");
return "register/step2";
}
}
@Valid 애노테이션을 붙이면 글로벌 범위 Validator가 해당 타입을 검증할 수 있는지 확인한다. 검증 가능하면 실제 검증을 수행하고 그 결과를 Errors에 저장한다. 이는 요청 처리 메서드 수행 전에 적용된다.
@lnitBinder 애노테이션을 이용한 컨트롤러 범위 Validator
@InitBinder 애노테이션을 이용하면 컨트롤러 범위 Validator를 설정할 수 있다.
- RegisterController에 해당 메서드 생성
@InitBinder
protected void initBinder(WebDataBinder binder){
binder.setValidator(new RegisterRequestValidator());
}
커맨드 객체 파라미터에 @Valid 애노테이션을 적용하고 있다. 글로벌 범위 Validator 사용할 때와 마찬가지로 handleStep3() 메서드에는 Validator 객체의 validate() 메서드를 호출하는 코드가 없다.
어떤 Validator가 커맨드 객체를 검증할지는 initBinder() 메서드가 결정한다. @InitBinder 애노데이션을 적용한 메서드는 WebDataBinder 타입 파라미터를 갖는데 WebDataBinder#setValidator() 메서드를 이용해서 컨트롤러 범위에 적용할 Validator을 설정할 수 있다.
Bean Validation을 이용한 값 검증 처리
Bean Validation이 제공하는 애노테이션을 이용해서 커맨드 객체의 값을 검증하는 방법은다음과같다.
- Bean Validation 과 관련된 의존을 설정에 추가한다.
- 커맨드 객체에 @NotNull, @Digits 등의 테이션을 이용해서 검증 규칙을 설정한다.
// 의존성 추가
implementation 'org.hibernate:hibernate-validator:5.4.2.Final'
public class RegisterRequest {
@NotBlank // 공백까지 허용하지 않음
@Email
private String email;
@Size(min = 6)
private String password;
@NotEmpty // null "" 허용하지 않음
private String confirmPassword;
@NotEmpty
private String name;
다음으론 Bean Validation 애노테이션을 적용한 커맨드 객체를 검증할 수 있는OptionalValidatorFactoryBean 클래스를 빈으로 등록하는 것이다. @EnableWebMvc 애노테이션을 사용하면 OptionalValidatorFactoryBean을 글로벌 범위 Validator로 등록하므로 @EnableWebMvc 애노테이션을 설정했다면 추가로 설정한 것은 없다.
만약 글로벌 범위 Validator를 따로 설정했다면 해당 설정을 삭제하자.
@Override
public Validator getValidator() // 삭제 해야함
{
return new RegisterRequestValidator();
}
- label.properties에서 메시지를 따로 설정. 애노테이션(message=)로도 설정가능
NotBlank=필수 항목입니다. 공백 문자는 허용하지 않습니다.
NotEmpty=필수 항목입니다
Size.password=암호 길이는 자 이상이어야 합니다.
Email= 올바른 이메일 주소 를 입력해야 합니다.
Bean Validation 의 주요 애노테이션
Reference
초보 웹 개발자를 위한 스프링5 프로그래밍 입문
'Spring > 스프링5 프로그래밍 입문' 카테고리의 다른 글
Chapter 14. MVC 4: 날짜 값 변환, @PathVariable, 익셉션 처리 (0) | 2023.04.11 |
---|---|
Chapter 13. 세션, 인터셉터, 쿠키 (0) | 2023.04.10 |
Chapter 10. 스프링 MVC 프레임워크 동작 방식 (0) | 2023.04.03 |
Chapter8. DB연동 (0) | 2023.02.26 |
Chapter7. AOP 프로그래밍 (0) | 2023.02.22 |