메시지(문자열) 다루기
MessageSource 문자열 관리 인터페이스
<spring:message> 메시지 출력 태그
커맨드 객체의 값 검증과 에러 메시지 처리
<form:error> 에러 메시지 처리 태그
Validator (글로벌 범위, 컨트롤러 범위)
Bean Validation을 이용한 값 검증 처리
스프링은 locale(지역)에 상관없이 일관된 방법으로 문자열(메시지)를 관리할 수 있도록 MessageSource 인터페이스를 제공한다.
특정 지역에서 메시지를 사용할 때 getMessage()를 이용한다.
package org.springframework.context;
import java.util.Locale;
public interface MessageSource {
String getMessage(String code, Object[] args, String defaultMessage, Locale locale);
String getMessage(String code, Object[] args, Locale locale) throws NoSuchMessageException;
....
}
별도의 파일로 관리하는 문자열을 JSP 코드에 출력하려면 다음의 3단계를 따른다.
<spring:message> 태그는 스프링 설정에 등록된 'messageSource' 빈을 이용해 메시지를 구한다.
즉, 내부적으로 MessgaeSource의 getMessage() 메서드를 실행한다.
STEP 1. 메시지 파일 작성
메시지 파일은 자바의 프로퍼티 파일 형식으로 작성한다.
예제) src/main/resource 하위에 messages 폴더를 생성하고 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=메인으로 이동
STEP 2. 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.setBasenames("message.label", "message.error"); // 불러올 파일 목록(여러 개) 설정 가능
ms.setDefaultEncoding("UTF-8");
resturn ms;
}
STEP 3. MessageSource를 사용하도록 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>
arguments 속성: code로 지정한 프로퍼티(메시지)의 {인덱스} 위치에 삽입할 값을 설정한다.
// 프로퍼티 파일
register.done=<strong>{0}님 ({1})</strong>, 회원 가입을 완료했습니다.
// JSP 코드 - arguments 속성 이용
<spring:message code="register.done" arguments="${registerRequest.name},${registerRequest.email}" />
// JSP 코드 - <spring:argument> 태그 사용 (위와 동일한 코드)
<spring:message code="register.done">
<spring:argument value="${registerRequest.name}" />
<spring:argument value="${registerRequest.email}" />
</spring:message>
입력값 검증, 에러 메시지 처리는 애플리케이션을 개발할 때 놓쳐서는 안 된다.
스프링 MVC에서 커맨드 객체의 값이 올바른지 검사하려면 다음의 두 인터페이스를 사용한다.
* Errors와 ValidationUtils 클래스의 주요 메서드는 336p 참고
// Validator 인터페이스 정의
package org.springframework.validation;
public interface Validator {
boolean supports(Class<?> clazz); // Validator가 검증할 수 있는 타입인지(타입캐스팅 가능한지) 검사
void validate(Object target, Errors errors); // 첫 번째 파라미터로 전달받은 객체를 검증, 오류 결과를 Errors에 담는 기능을 정의
}
아래는 Validator를 구현한 코드이다.
// src/main/java/contorller/RegisterRequestValidator.java
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
...
public class RegisterRequestValidator implements Validator {
// 이메일 패턴을 검사하는 Pattern 객체 설정
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;
@Override
public RegisterRequestValidator() {
pattern = Pattern.compile(emailRegExp);
}
// 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");
}
}
}
}
위의 Validator를 사용하는 Controller의 코드이다.
// src/main/java/controller/ReeeegisterController.java
import org.springframework.validation.Errors;
@Controller
public class RegisterController {
private MemberRegisterService memberRegisterService;
...
@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";
}
}
}
<label><spring:message code="email" />:<br>
<form:input path="email" />
<form:errors path="email" />
</label>
사용 예
<form:errors path="userId" element="div" delimeter="" />
글로벌 범위 Validator를 위한 2가지 설정
// WebMvcConfigurer
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public Validator getValidator() {
return new RegisterRequestValidator();
}
// @Valid 애노테이션 이용 예
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) {
...
컨트롤러 범위 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());
}
Bean Validation 스펙에 포함된 애노테이션을 사용하는 것만으로 커맨드 객체의 값 검증을 처리할 수 있다.
STEP 1. 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>
STEP 2. 값 검증 규칙 설정
public class RegisterRequest {
@NotBlank
@Email
private String email;
@Size(min=6)
private String password;
@NotEmpty
private String confirmPassword;
@NotEmpty
private String name;
...
STEP 3. 요청 처리 메소드의 파라미터에 @Valid 애노테이션을 붙여 글로벌 범위 Validator로 검증한다.
1. 스프링은 locale(지역)에 상관없이 일관된 방법으로 문자열(메시지)를 관리할 수 있도록 (MessageSource 인터페이스)를 제공한다.
2. 메시지 파일은 자바의 (프로퍼티) 형식으로 작성한다.
3. MessageSource 타입의 빈을 추가할 때는 빈의 아이디를 (messageSource)로 해야 한다.
4. 스프링 MVC에서 커맨드 객체의 값이 올바른지 검사하려면 (Validator), (Errors) 인터페이스를 사용한다.
5. 글로벌 범위 Validator 설정: 설정 클래스에서 getValidator() 메서드가 Validator 구현 객체를 리턴하도록 하고, 커맨드 객체에 (@Valid) 애노테이션을 적용한다.
6. 컨트롤러 범위 Validator 설정: (@InitBinder) 애노테이션을 사용한다.
7. (Bean Validation) 스펙에 포함된 (@NotNull), (@Digits), (@Size) 등의 애노테이션을 사용하는 것만으로도 커맨드 객체의 값 검증을 처리할 수 있다.
코드 문제 1. 조건을 만족하는 <spring:messge> 태그를 작성하라.
다음과 같은 자바 프로퍼티가 있다.
signIn.done=<strong>{0}님 ({1})</strong>으로 로그인했습니다.
이 메시지의 0번 인자에 signInRequest.name, 1번 인자에 signInRequest.email의 값을 표시하려 한다.
답: <spring:message code="signIn.done" arguments="${signInRequest.name},${signInRequest.email}" />
코드 문제 2. 주어지는 글로벌 범위 Validator 대신 컨트롤러 범위 Validator를 사용하도록 코드를 추가하라.
// WebMvcConfigurer
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {
@Override
public Validator getValidator() {
return new RegisterRequestValidator();
}
// @Valid 애노테이션 이용 예
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) {
...
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) {
...
/* 이곳에 작성 */
}
Corner Spring #2
Editor : 유즈
[스프링2] 14장. MVC 4: 날짜 값 변환, @PathVariable, 익셉션 처리 (0) | 2023.01.12 |
---|---|
[스프링2] 13. MVC 3: 세션, 인터셉터, 쿠키 (0) | 2023.01.05 |
[스프링2] 11장. MVC 1 : 요청, 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델 (2) (0) | 2022.12.22 |
[스프링2] 11장. MVC 1: 요청 매핑, 커맨드 객체, 리다이렉트, 폼 태그, 모델 (1) (0) | 2022.12.22 |
[스프링2] 10장. 스프링 MVC 프레임워크 동작 방식 (0) | 2022.12.01 |