본문 바로가기

카테고리 없음

SimpleFormController에서 Validator를 등록하고 폼을 검증하는 예

사용자 삽입 이미지

SimpleFormController에 Validator를 등록하여 폼을 검증하도록 할 수 있다. Validator의 validate()메소드에서 폼을 검증하도록 validate()를 오버라이드 해 주어야 한다. 폼 데이터를 검증하는 중에 유효하지 못한 데이터가 있다면 errors.rejectValue()를 이용하여 FieldError를 추가해 주는데, 개발자가 에러를 추가하면 Spring프레임워크는 폼 데이터에 문제가 있는 것으로 인식하고 showForm()메소드를 호출하여 formView를 화면에 출력한다. showForm() 에서는 referenceData()를 호출하여 리턴된 Map객체를 뷰에 전달하여 뷰에서 그 데이터를 사용할 수가 있다.

폼을 검증하여 아무런 오류가 없다면 Spring프레임워크는 onSubmit() 를 호출하여 폼을 처리하게 된다. onSubmit()안에서는 폼 데이터를 사용하여 준비된 작업을 하면 되고, 만약 폼 데이터가 적절치 못한 값을 가진 것으로 판정될 경우에는 showForm()을 호출하여 다시 formView를 출력할 수도 있다.

아무런 문제도 없이 폼 데이터를 사용하여 작업을 마쳤다면, ModelAndView객체를 생성하고 ModelAndView에 successView를 설정하여 리턴하면 화면에 successView를 출력할 수 있다.

Validator의 validate()에서 추가한 필드에러(FieldError)가 referenceData()에서 확인되고 Map에 저장되어 formView에 다시 출력되는 과정을 주의해 보기 바란다. 여기서는 스프링의 커스텀 태그를 사용하지 않고 필드에러를 뷰에 출력하는 방법을 사용하였다.


dispatcher-servlet.xml


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean name="/hello.htm"
 class="test.HelloController"/>
 
<bean name="authenticator"
 class="test.Authenticator"/>

<bean name="loginCommandValidator"
 class="test.LoginCommandValidator"/>
 
<bean name="/login.htm"
     class="test.LoginFormController"
     p:validator-ref="loginCommandValidator"
     p:authenticator-ref="authenticator"
     p:commandClass="test.LoginCommand"
     p:commandName="loginCommand"
     p:successView="loginSuccess"
     p:formView="loginForm" />

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
 <property name="prefix" value="/"/>
 <property name="suffix" value=".jsp"/>       
</bean>

</beans>




loginForm.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>로그인 폼</title>
<script type="text/javascript">
<c:if test="${!empty loginFail}">
 alert("${loginFail}");
</c:if>
</script>
</head>
<body><center><br></br>

${loginFail}<br>
<form action="login.htm" method="post">
Course
 <select name="course">
  <c:forEach var="course" items="${courses}">
   <option value="${course}">${course}</option>
  </c:forEach>
 </select>
 <p>
ID: <input type="text" name="id" value="${loginCommand.id}" onclick="this.value='';">${IDrequired}<br><br>
PWD: <input type="text" name="pwd" value="${loginCommand.pwd}" onclick="this.value='';">${PWDrequired}<br>
<input type="submit" value="로그인">
</form>

</center>
</body>
</html>




LoginCommandValidator.java

package test;

import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

public class LoginCommandValidator implements Validator {

 @Override
 public boolean supports(Class arg0) {
  if(LoginCommand.class.isAssignableFrom(arg0)) return true;
  return false;
 }

 /* SimpleFormController의 경우에는 validate()메소드에서 검증에 실패하여
  * 개발자가 errors 에 오류를 추가하면 onSubmit()를 실행하지 않고
  * showForm(), referenceData()를 거쳐 formView를 화면에 출력한다.
  * showForm()이 실행되면서 referenceData()를 호출하여 formView에 전달할 데이터를 준비한다.
  * referenceData()는 Map객체를 showForm()에 리턴하여 formView에서 사용되도록 한다.
  * (non-Javadoc)
  * @see org.springframework.validation.Validator#validate(java.lang.Object, org.springframework.validation.Errors)
  */

 @Override
 public void validate(Object arg0, Errors errors) {
  System.out.println("validate()");
  LoginCommand command = (LoginCommand) arg0;
  String id = command.getId();

  if(id==null || id.equals("")) {
   /* FieldError 추가
    * ObjectError를 추가하려면 errors.reject()를 이용하고,
    * FieldError를 추가하려면 errors.rejectValue()를 이용한다*/

   errors.rejectValue("id", "IDrequired", "아이디를 입력해 주세요");
   System.out.println("id 검증실패");
  }
  String pwd = command.getPwd();
  if(pwd==null || pwd.equals("")) {
   /* FieldError 추가 */
   errors.rejectValue("pwd", "PWDrequired", "암호를 입력해 주세요");
   System.out.println("pwd 검증실패");
  }
 }
}




LoginFormController.java

package test;

import java.util.*;

import javax.servlet.http.*;

import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.*;
/* SimpleFormController클래스는 속성으로 successView, formView를 가지고 있기 때문에
 * 폼데이터 검증에 성공하면 successView로 등록된 JSP를 이용하여 성공 메시지출력할 수 있고,
 * 검증에 실패하면 formView로 등록된  JSP를 출력하여 다시 폼을 보여 줄 수 있다.
 * 폼 검증에 성공할 시에는 onSubmit()에서 정상적으로 ModelAndView객체를 리턴해 주면 되고,
 * 폼 검증에 실패시에는 onSubmit()에서 showForm()을 호출하여 리턴된 ModelAndView객체(formView가 설정됨)
 * 를 다시 리턴하면 폼이 다시 출력된다.
 * showForm()메소드는 referenceData()를 호출하여 폼을 구성하는 콤보박스등에서 사용될 리스트를 준비하는
 * 용도로 자주 사용된다.
 */
public class LoginFormController extends SimpleFormController {
 
 /*회원인증을 위해 개발자가 정의한 클래스*/
 private Authenticator authenticator;

 /*DI용 메소드*/
 public void setAuthenticator(Authenticator authenticator) {
  this.authenticator = authenticator;
 }

 @Override
 protected ModelAndView onSubmit(HttpServletRequest request,
   HttpServletResponse response, Object command, BindException errors)
   throws Exception {

  System.out.println("onSubmit()");
  LoginCommand cmd = (LoginCommand) command;
  try{
   /* DB와 연동하여 회원인증을 실행하고 인증에 실패하면 아래의 메소드는 Exception을 발생하도록 되어있다*/
   authenticator.authenticate(cmd.getId(), cmd.getPwd(), errors);
   
   ModelAndView mav = new ModelAndView();
   mav.setViewName(getSuccessView());
   mav.addAllObjects(errors.getModel());
   return mav; // 회원인증에 성공하면 successView가 화면에 출력된다
   /*위에서 리턴한 데이터는 successView에서 접근할 수가 있다.
   * successView에서 커맨드객체에 접근하려면 설정파일에 등록한 "commandName"속성의 값을 이용하면 된다.
   * 즉, request.getAttribute("loginCommand"), ${loginCommand.id}등을 사용하면 된다.
   */

  }catch(Exception e){
   System.out.println("catch()");
  }
  return showForm(request, response, errors);//회원인증에 실패시 formView가 화면에 출력된다.
 }
 /*
  * (non-Javadoc)
  * @see org.springframework.web.servlet.mvc.SimpleFormController#referenceData(javax.servlet.http.HttpServletRequest)
  * showForm() 메소드는 referenceData()를 호출해서 폼 페이지에서 보여주고자 하는 참조 데이터(주로 셀렉트박스, 체크박스 같은 유형)를 ModelAndView에 저장한다.
  * formView에서는 Model 데이터를 바탕으로 폼에 필요한 데이터를 채워서 표시한다.
  */

 @Override
 protected Map referenceData(HttpServletRequest request, Object command,
   Errors errors) throws Exception {

  /* 입력폼의 콤보박스를 구성하는  리스트를  생성한다*/
  System.out.println("referenceData()");
  List<String> course = new ArrayList<String>();
  course.add("Java Developer");
  course.add("Struts Programmer");
  course.add("Spring Professional");
  Map map = new HashMap();
  map.put("courses", course);
 
  /* ObjectError, FieldError의 발생을 확인하고 Map에 추가하여 뷰에서 출력될 수 있도록 한다 */
  List elist = errors.getAllErrors();
  System.out.println("에러수:"+elist.size());
  for(int i=0;i<elist.size();i++){
   String code = null;
   String message = null;
   if(elist.get(i) instanceof ObjectError){
    ObjectError oe = (ObjectError) elist.get(i);
    code = oe.getCode();
    message = oe.getDefaultMessage();
   }else if(elist.get(i) instanceof FieldError){
    FieldError fe = (FieldError) elist.get(i);
    code = fe.getCode();
    message = fe.getDefaultMessage();
   }

   System.out.println(code+":"+message);
   map.put(code, message);
  }
  return map;
  }

 /*
  * (non-Javadoc)
  * @see org.springframework.web.servlet.mvc.AbstractFormController#formBackingObject(javax.servlet.http.HttpServletRequest)
  * formBackingObject()는 GET방식(폼 출력요청)이나 POST방식(폼 전송)을 가리지 않고 항상 실행되며,
  * 커맨드객체를 생성하여 뷰에서 사용될 수 있도록 한다. 그러므로 개발자가 이 메소드를 오버라이드하여
  * GET방식 요청일 경우에는  폼에서 디폴트로 채워져야 하는 필드의 기본 데이터를 준비하는 용도로 사용하면 되고,
  * POST방식(폼을 전송한 경우)일 경우에는 커맨드클래스의 객체를 생성하여 리턴해주면  된다.
  * 이 메소드에서 리턴된 데이터를 뷰에서 사용하려면 설정파일에 등록한 "commandName" 속성의 값을 key로 사용하면 된다.
  * 즉, request.getAttribute("loginCommand")라고 하거나, ${loginCommand.id}등을 사용하면 된다.
  */
 @Override
 protected Object formBackingObject(HttpServletRequest request)
   throws Exception {

  if(!this.isFormSubmission(request)){ // GET방식일 경우...
   System.out.println("formBackingObject()-GET");
   LoginCommand cmd = new LoginCommand();
   cmd.setId("6자이상 영문");
   cmd.setPwd("영문, 숫자 혼용 6자이상");
   return cmd;
  }else{ // POST방식일 경우
   System.out.println("formBackingObject()-POST");
   return super.formBackingObject(request);
  }
 }
 
}

위의 referenceData()에서처럼 검증 오류 정보를 가공하지 않고 바로 뷰에서 출력할 수 있도록 하려면 Spring의 커스텀 태그를 사용하면 된다. Spring 커스텀 태그를 사용하려면 다음과 같이 web.xml 파일에 추가해야 한다.

 <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
 </context-param>

 <listener>
        <listener-class>
         org.springframework.web.context.ContextLoaderListener
        </listener-class>
 </listener>


위와 같이 설정한 후에는 JSP에서 Spring의 커스텀 태그를 사용할 수 있다. 여기서는 EL과 함께 사용하는 예를 들어 본다.

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ taglib prefix="form" uri="
http://www.springframework.org/tags/form" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
<title>사원 로그인 폼</title>
<style>
 table{border: 2px double black; }
 th{ text-align: right; }
 td{ text-align: left; }
 .btn { text-align: center; }
</style>
</head>
<body><br/><br/><center>
사원 로그인 폼<p/>

<spring:hasBindErrors name="emp">
 <c:forEach var="e" items="${errors.allErrors}">
  <c:if test="${!e.bindingFailure}">
   <font color="red">${e.defaultMessage}</font><br/>
  </c:if>
 </c:forEach>
</spring:hasBindErrors>

<form action="empFormCont.htm" method="post">
<table border="1" cellspacing="0" width="300" height="150" rules="none" cellpadding="5">
 <tr><th>이름</th><td> <input type="text" name="ename" value="${emp.ename}"/>

 </td></tr>
 <tr><th>사번</th><td> <input type="text" name="empno" value="${emp.empno}"/>

 </td></tr>
 <tr><td> </td><td> </td></tr>
 <tr><td colspan="2" class="btn">
  <input type="submit" value="로그인"/>
  <input type="reset" value="취 소"/>
  </td></tr>
</table>
</form>

</center>

</body>
</html>




Authenticator.java

package test;

import java.sql.*;

import org.springframework.validation.BindException;
/* 아이디, 암호를 받아서 데이터베이스와 연동하여 회원인증을 수행하며,
 * 회원인증에 통과하지 못한 경우에는 BindException에 ObjectError를 추가하고,
 * 예외를 발생한다. 검증에 통과하면 아무런 조치도 하지 않는다.
 */
public class Authenticator {
 
 public void authenticate(String id, String pwd, BindException errors)throws Exception{
  System.out.println("authenticate()="+id+"/"+pwd);
  Connection conn = null;
  PreparedStatement pstmt = null;
  ResultSet rs = null;

  String jdbc_driver = "oracle.jdbc.OracleDriver";
  String db_url = "jdbc:oracle:thin:@micropilot.co.kr:1521:ORCL";
 
  try{
    Class.forName(jdbc_driver);
    conn = DriverManager.getConnection(db_url,"scott","tiger");

    String sql = "select * from member where id=? and pwd=?";
    pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, id);
    pstmt.setString(2, pwd);
    rs = pstmt.executeQuery();
    if(!rs.next()) {
     System.err.println("회원인증실패");
     /* ObjectError 추가
     * ObjectError를 추가하려면 errors.reject()를 이용하고,
  * FieldError를 추가하려면 errors.rejectValue()를 이용한다*/

     errors.reject("loginFail", new Object[]{"로그인 실패"}, "ID, Password가 일치하지 않습니다");
     throw new Exception("회원인증 실패");
    }
    System.out.println("Authenticate:회원인증성공");
  }catch(SQLException sqle){
  sqle.printStackTrace();
  }finally{
   try{
    if(rs!=null) rs.close();
    if(pstmt!=null) pstmt.close();
    if(conn!=null) conn.close();
   }catch(SQLException e){}
  }
 }
}




LoginCommand.java

package test;

public class LoginCommand {
 
 private String id;
 private String pwd;
 
 public LoginCommand() {}

 public LoginCommand(String id, String pwd) {
  super();
  this.id = id;
  this.pwd = 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;
 }

}




loginSuccess.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
    pageEncoding="EUC-KR"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>로그인 성공</title>
</head>
<body>
<center>
   로그인 성공<br></br>
   ${loginCommand.id}<br></br>
   ${loginCommand.pwd}<br></br>

</center>
</body>
</html>




web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>*.htm</url-pattern>
  </servlet-mapping>

  <welcome-file-list>
    <welcome-file>
      index.jsp
    </welcome-file>
  </welcome-file-list>
 
  <filter>
   <filter-name>encodingFilter</filter-name>
 <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
   <init-param>
    <param-name>encoding</param-name>
    <param-value>EUC-KR</param-value>
   </init-param>
  </filter>
 
  <filter-mapping>
   <filter-name>encodingFilter</filter-name>
   <url-pattern>/*</url-pattern>
  </filter-mapping>

</web-app>