먼저 스프링에서 프로퍼티 값을 가져오는 방법을 알아본다. (Named, Inject 어노테이션을 통한 주입)
@Value
어노테이션으로 변수에 할당@Named
public class MyClass {
>
@Value("${prop.name}")
private String propName;
>
public void valueMethod() {
System.out.println(String.format("propName : %s", propName));
}
}
@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()));
}
}
@Named
public class MyClass {
@Inject
private Environment environment;
public void envMethod() {
System.out.println(String.format("propName : %s", environment.getProperty("prop.name")));
}
}
문자열을 별도 파일에 작성하고 JSP 코드에서 이를 사용하는 방법을 설명한다.
src/main/resources/message/label.properties
에 맞춰 파일 생성.
파일 설정을 UTF-8 인코딩을 하도록 바꿔야 한글 안꺠짐.
msg.test=테스트
term=약관
name=이름
tag.argument.one=<strong>{0}</strong>
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;
}
}
message 태그 사용 설정
태그 사용
Argument 설정
<%@ 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>
입력 받은 값에 대해서는 검증이 필요한데 스프링은 이를 위한 방법을 제공한다.
스프링 MVC에서는 커맨드 객체의 값 검증을 위해 두가지 인터페이스를 사용한다.
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");
}
}
}
}
검증을 한 값을 통해 컨트롤러에서 처리하는 코드이다.
@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";
}
}
우리는 여러 방법을 통해 Errors에 에러를 저장하고 이를 통해 처리를 한다.
Errors 인터페이스 메소드, ValidationUtils 클래스 메소드 통해 에러를 간단히 기록하는 방법들을 알아본다.
errorCode : 에러를 식별하기 위한 해당하는 문자열 코드
defaultMessage : 에러에 해당하는 Message가 없을 경우 기본 메시지
errorArgs : 메시지에 인덱스 기반 변수가 있을 시 삽입될 값 객체
field : 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는 모든 컨트롤러에 적용할 수 있는 Validator이다.
이는 두 가지 설정을 통해 가능하다.
getValidatior()
메소드가 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는 전체 컨트롤러에 적용하는 만큼, 공통적인 부분에만 사용하는게 좋다. 위 커맨드 객체의 경우 특정 상황에만 사용하므로 적절하지 않다.
컨트롤러 범위는 @InitBinder
어노테이션을 이용해 설정 가능하다. 이 또한 객체를 @Valid
어노테이션으로 지정해주어야한다.
@PostMapping("/test")
public String handleStep3(@Valid CommandObj com, Errors errors) {}
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new RegisterRequestValidator());
}
@Valid
어노테이션과 함께 Bean Validation이 제공하는 @NotNull
, @Digits
, @Size
등의 어노테이셔능ㄹ 통해 Validator 정의 없이 검증이 가능하다.
Bean Validation이 제공하는 어노테이션을 이용해 검증하는 방법은 아래와 같다.
예제의 의존성은 hibernate-validator를 사용한다.
@Getter
@Setter
public class RegisterRequest {
@NotBlank
@Email
private String email;
@Size(min = 6)
private String password;
@NotEmpty
private String confirmPassword;
@NotEmpty
private String name;
}
커맨드 객체를 검증할 ptionalValidatorFactoryBean 클래스를 등록한다.
@EnableWebMvc
어노테이션이 글로벌 범위로 자동으로 등록해주기 때문에 추가로 설정하지 않아도 된다.
글로벌 범위 Validator를 따로 사용하면 자동으로 등록해주지 않기 때문에 주의한다.
이는 기존의 방법처럼 하면 된다.
어노테이션 종류는 True/False 검사, 값의 크기, 자릿수, 길이, Null 등 매우 다양하다.
Java Bean Validation Basic 사이트에서 필요할 때 찾아 쓰면 된다.