Spring 에서 데이터 검증 오류를 JSON 문자열로 클라이언트에게 전송하는 예
개요
클라이언트에서 AJAX 요청을 하는 경우에는 화면의 일부를 갱신할 목적이기 때문에 응답 데이터의 포맷으로 JSON 형식을 선택하는 것이 보통일 것이다.
스프핑에서는 콘트롤러 클래스에서 @ResponseBody 라는 Annotation 을 사용하면 리턴되는 Map 이나 도메인 오브젝트의 속성들이 자동으로 JSON 형식으로 변환되어 클라이언트에게 전달되는 기능이 있다.
여기서는 서버로 데이터를 전송할 경우 그 데이터를 JSR-303 검증을 거쳐 오류가 있을 때는 오류 메시지를 JSON 으로 전송하는 기능을 작성해보려고 한다.
WEB-INF/spring/servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Process annotations on registered beans like @Autowired... -->
<context:annotation-config/>
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven/>
<context:component-scan base-package="org.kdea.java" />
<context:component-scan base-package="org.kdea.service" />
<context:component-scan base-package="org.kdea.hibernate" />
<context:component-scan base-package="org.kdea.json" />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
<beans:property name="contentType" value="text/html; charset=UTF-8"/>
</beans:bean>
<beans:bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basename" value="messages/messages"/>
</beans:bean>
<beans:bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
</beans:beans>
메시지 리소스를 이용하는 방법은 다른 페이지를 참조하세요
pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.0.4</version>
</dependency>
JsonController.java
package org.kdea.json;
import java.util.List;
import java.util.Locale;
import java.util.*;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
/*
Jackson 라이브러리를 설치한다
메소드에 @ResponseBody 를 적용한다
<mvc:annotation-driven/> 을 servlet-context.xml 에 선언한다
이렇게 설정하면 스프링은 리턴되는 객체(도메인 오브젝트, Map 등)를 json 문자열로 자동으로
변환하여 클라이언트에게 전송한다.
이 경우 별도의 jsp 와 같은 View를 연결하지 않아도 된다
*/
@Controller
public class JsonController {
private MessageSource messageSource;
@Autowired
public JsonController(MessageSource messageSource) {
this.messageSource = messageSource;
}
@RequestMapping(value="/json/login", method=RequestMethod.GET)
public String login() {
return "json/ajax_client";
}
@RequestMapping(value="/json/login", method=RequestMethod.POST, produces="application/json; charset=utf8")
@ResponseBody //이 메소드가 리턴하는 값을 json 문자열로 변환하여 클라이언트에게 전송한다
public Map<String,String> login(@Valid User user, BindingResult result) {
// 데이터 검증실패의 경우, 오류 메시지를 JSON 문자열로 클라이언트에게 전송한다
Map<String,String> map = null;
if(result.hasErrors()){
map = new HashMap<String,String>();
List<FieldError> list = result.getFieldErrors();
for(int i=0;i<list.size();i++) {
FieldError fe = list.get(i);
System.out.println("오류필드:"+fe.getField());
if(fe.getField().equals("id")){
String idErr = messageSource.getMessage("NotEmpty.user.id", null, Locale.getDefault());
map.put("idErr", idErr);
}
if(fe.getField().equals("pwd")){
String pwdErr = messageSource.getMessage("NotEmpty.user.pwd", null, Locale.getDefault());
map.put("pwdErr", pwdErr);
}
}
return map;
}
//데이터 검증오류가 없는 경우, 입력한 아이디, 암호를 JSON 문자열로 전송한다
map = new HashMap<String,String>();
map.put("id", user.getId());
map.put("pwd", user.getPwd());
return map; // 뷰가 연결되는 것이 아니라 응답 데이터에 해당하는 객체를 리턴한다
}
}
User.java
package org.kdea.json;
import org.hibernate.validator.constraints.NotEmpty;
public class User {
@NotEmpty
private String id;
@NotEmpty
private String pwd;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
}
ajax_client.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script src="<c:url value="/resources/jquery-2.1.1.min.js"/>"></script>
<script type="text/javascript">
function toJson_submit(){
ajax_request();
}
function ajax_request(){
var usrId = loginForm.id.value;
var usrPwd = loginForm.pwd.value;
$.ajax(
{
url:'login.do',
type:'POST',
data:{'id':usrId, 'pwd':usrPwd},
dataType:'json',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
success :function(resData){
if(resData.idErr || resData.pwdErr) {
alert("에러:"+resData.idErr+','+resData.pwdErr);
}else{
alert("성공:"+resData.id+','+resData.pwd);
}
},
error :function(errMsg){ alert("에러:"+errMsg); }
}
);
}
</script>
</head>
<body>
<form name="loginForm">
ID <input type="text" name="id">
PWD <input type="password" name="pwd">
<input type="button" value="SUBMIT" onclick="toJson_submit()">
</form>
</body>
</html>