본문 바로가기
JAVA/Spring

Spring MVC - BindingResult 사용법

by 설총이 2018. 9. 13.





[CreateAccountController.java]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package spring.controller;
 
import javax.servlet.http.HttpServletRequest;
 
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
import spring.model.Address;
import spring.model.MemberInfo;
import spring.validator.MemberInfoValidator;
 
@Controller
@RequestMapping("/account/create.do")
public class CreateAccountController {
 
    @ModelAttribute("command")
    public MemberInfo formBacking(HttpServletRequest request) {
        // equalsIgnoreCase 메서드는 
// 대소문자 관계 없이(ignore case) equals 검사를 해줍니다.
        // 따라서 "A".equals("a")는 false, "A".equalsIgnoreCase("a")는 true
        if (request.getMethod().equalsIgnoreCase("GET")) {
            MemberInfo mi = new MemberInfo();
            Address address = new Address();
            address.setZipcode(autoDetectZipcode(request.getRemoteAddr()));
            System.out.println("request.getRemoteAddr : " + autoDetectZipcode(request.getRemoteAddr()));
            // getRemoteAddr :: 클라이언트의IP를 얻는 메서드
            mi.setAddress(address);
            return mi;
            // 처음 GET방식때에는 Zipcode를 저장한 address객체를 => MemberInfo(DTO)에 저장
        } else {
            return new MemberInfo();
            // Post방식일때에는 새로운 주소값을 가진 DTO객체 생성
        }
    }
 
    private String autoDetectZipcode(String remoteAddr) {
        // return "000-000";
        return remoteAddr;
        //왜 0:0:0:0:0:0:0:0:1로 나오지?
//이유 :: ipv6의 주소를 가져와서 0:0:0:0:0:0:0:1로 가져오게된다.
//해결법 :: http://leinger.blogspot.com/2012/05/requestgetremoteaddr-ip-00000001.html 
 
    }
 
    @RequestMapping(method = RequestMethod.GET)
    public String form() {
        return "account/creationForm";
    }
 
    // BindingResult 의 경우 ModelAttribute 을 이용해 매개변수를 Bean 에 binding 할 때,
    // 발생한 오류 정보를 받기 위해 선언해야 하는 애노테이션
    
    /*
       RequestParam 도 그렇지만, 매개변수 binding 이 실패하면 400 오류가 발생합니다.
       이게 500.1 같은 오류가 발생해야 맞을 것 같지만, Spring 입장에선 요청 자체가 
       잘못된 것이 뭔가 설계가 잘못 되어서 혹은 잘못된 경로를 호출해서 발생한 걸로 판단하기 때문에 
       400 으로 오류를 발생시키는 것입니다.
      
     */
    
    @RequestMapping(method = RequestMethod.POST)
    public String Submit(@ModelAttribute("command") MemberInfo memberInfo, BindingResult result) {
 
        new MemberInfoValidator().validate(memberInfo, result);
        if (result.hasErrors()) {
            return "account/creationForm";
        }
        return "account/created";
    }
}
 
cs



BindingResult result

.validate()  

.hasErrors()  에 대해선 밑에 따로 설명을 덧붙힌다.



.validate(MemberInfo memberInfo, BindingResult result);


.validate메서드의 수행이 끝난후 돌아와서

Errors.hasErrors()메서드를 이용해 에러가 존재하는 지 확인하고,

에러가 존재할 경우 알맞은 처리를 수행할 수 있다.


if(result.hasErrors()){ 

//에러처리 

//에러가 없을때의 처리





 - Spring 인터페이스 유효성 검사


- Validator 


Validator 인터페이스

:: Validator가 해당 클래스에 대한 값 검증을 지원하는 지의 여부를 리턴



1
2
3
4
public boolean supports(Class<?> clazz) {
    return MemberInfo.class.isAssignableFrom(clazz);
}
//MemberInfo 커맨드 객체에 대한 값 검증을 지원하는 지의 여부를 리턴한다.
cs


:: target객체에 대한 검증에 대한 true / false 값을 가져오는 메서드이다. 

무조건 써줘야하는 메서드라고 생각하면 편하

검증을 실행하고 바로 밑에있는 validate 메서드를 실행한다.



resources/messages/validation.properties 파일을 가져온


[dispatcher-servlet.xml]



1
2
3
4
5
6
7
8
<bean id="messageSource"
    class="org.springframework.context.support.ResourceBundleMessageSource">
    <property name="basenames">
        <list>
            <value>messages.validation</value>
        </list>
    </property>
</bean>
cs


명시할때에는 .properties 확장자를 명시 안해주었음을 기억하자.





[MemberInfoValidator.java]


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package spring.validator;
 
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
 
import spring.model.Address;
import spring.model.MemberInfo;
 
//앞에서 new 로 객체화하기때문에 implements를 안적어도 가능하지만,
//어노테이션으로 가져올때에는 구현한 객체만 가져오기때문에 implements로 선언해주어야한다
public class MemberInfoValidator implements Validator {
 
    // MemberInfo 커맨드 객체에 대한 값 검증을 지원하는 지의 여부를 리턴한다.
    public boolean supports(Class<?> clazz) {
        return MemberInfo.class.isAssignableFrom(clazz);
    }
 
    // properties파일이 어떻게 여기 String으로 가져오는지 에 대한 설명 ::
 
    // BindingResult 에는 모델의 바인딩 작업 중에 발생한 타입 변환 오류정보와
    // 검증 작업에서 발생한 검증 오류 정보가 모두 저장된다.
    // 이 오류 정보는 보통 컨트롤러에 의해 폼을 다시 띄울 때 활용된다.
    // 폼을 출력할 때 BindingResult 에 담긴 오류 정보를 활용해서
    // 에러 메시지를 생성할 수 있다.
    // 스프링은 기본적으로 messages.properties 와 같은
    // 프로퍼티 파일에 담긴 메시지를 가져와 에러 메시지로 활용한다.
    // 여기선 BindingResult를 errors로 사용하였다.
 
    public void validate(Object target, Errors errors) {
        MemberInfo memberInfo = (MemberInfo) target;
        
        //.trim().isEmpty() 
        //==> "____" 가져온 값의 앞뒤로 공백을 지우고, 가져온값이 공백이냐 체크할수있음
        if (memberInfo.getId() == null || memberInfo.getId().trim().isEmpty()) {
            errors.rejectValue("id""required.loginCommand.userId");
            // void rejectValue(String field, String errorCode)
            // 메서드 오버로딩.
            // String errorCode부분을 프로퍼티스파일에서 key값으로 꺼낼 수 있음
        }
        if (memberInfo.getName() == null || memberInfo.getName().trim().isEmpty()) {
            errors.rejectValue("name""required.name");
        }
        Address address = memberInfo.getAddress();
        if (address == null) {
            errors.rejectValue("address""required");
        }
        if (address != null) {
            errors.pushNestedPath("address");
            //address.zipcode , address.address1으로 들어가야할 작업을
            //pushNestedPath가 대신 해주고있기때문에, 안으로 들어가서 바로
            //필드명으로 호출할 수 있게 된다.
            try {
                if (address.getZipcode() == null || address.getZipcode().trim().isEmpty()) {
                    errors.rejectValue("zipcode""required");
                }
                if (address.getAddress1() == null || address.getAddress1().trim().isEmpty()) {
                    errors.rejectValue("address1""required");
                }
            } finally {
                errors.popNestedPath();
                //이전 url을 다시 띄워준다.
            }
        }
    }
}
 
cs




void validate(Object target,Errors errors) 


이것도 무조건 써줘야하는 메서드라고 생각하면 편하다

위의 true / false 값을 가져와서, 검증 결과 문제가 있을 경우 errors객체에 어떤 문제인지 에 대한 정보를 저장한다



- Errors(최상위 에러 객체)


를 상속 받고있는 - BindResult


1. 전체 객체에 대한 글로벌 에러코드 추가 :: reject(String errorCode);


만약 errorCode가 없을때에는 properties에 일치하는 key가 없다고 에러페이지가뜨는데,

없을때를 대비해서 defaultMessage를 지정할 수 있다.

==> reject(String errorCode, String defaultMessage);


2. 필드에 대한 에러코드 추가 :: rejectValue(String field, String errorCode);


위와 마찬가지로 일치하는 에러코드가 없을때 defaultMessage를 보여줄 수 있다.

==> rejectValue(String field, String errorCode, String defaultMessage);

'JAVA > Spring' 카테고리의 다른 글

String MVC - 뷰(ViewResolver) 원칙  (0) 2018.09.19
Spring MVC - ajax + responseBody  (0) 2018.09.17
Spring - RESTful 예제  (2) 2018.09.13
@ModelAttribute 추가 공부내용.  (0) 2018.09.12
Spring - @ModelAttribute + @Controller  (0) 2018.09.12