본문 바로가기

백엔드 개발/SpringMVC

@ModelAttribute 와 WebDataBinder

1. @ModelAttribute

(1)기능

@ModelAttribute가 붙어 있는 적용 대상을 자동으로 모델의 어트리뷰트로 넣어준다. 

(2) 적용 대상

  a. Controller Method의 매개 변수

// 매개변수 앞에 붙는다. 
public String main(@ModelAttribute MyDate date, Model m) { /... }

//public String main(@ModelAttribute("myDate") MyDate date, Model m) {/...}

  밑의 주석도 위와 동일한 뜻이다. 어노테이션 뒤 ()에 Model 의 Key 값으로 쓰일 이름을 지정해 넣을 수 있다. 

이는 생략 가능하며, 생략하면, 클래스의 첫글자를 소문자로 한 문자열이 Key 값이 된다. 

Model안에 Attribute 저장하는 Map
Key Value
mydate date 객체의 주소
//... //...

 이런 식으로 들어간다. 

  b. 일반 Method

// 매소드의 반환형 앞에 붙는다. 
// 해당 매소드의 반환값은 무조건 모델의 어트리뷰트로 들어간다는 뜻이다.
// 이 경우에는 Model의 Map 속 Key로 쓰일 값을 무조건 적어줘야 한다.
private @ModelAttribute ("yoil") char getYoil(MyDate date) {
		return getYoil(date.getYear(), date.getMonth(), date.getDay())
}

이 경우 ()안에 적은 값이 Key가 되고 해당 매소드의 호출 결과가 Value로 저장된다. 

Model의 Map
Key Value
yoil getYoil에 date 객체 넣었을 때 반환 값

   c. 해당 어노테이션으로 생긴 코드의 경제성

// 1.	모델 객체를 컨트롤러 매소드의 매개변수로 받을 필요가 없다!
			// >> 어짜피 @ModelAttribute 어노테이션이 자동으로 모델에 다 값을 저장해주기 때문이다.

	public String main(@ModelAttribute MyDate date) {
		
		if(!isVaild(date)) {
			return "yoilError";
		}
		
        //2.	 어노테이션을 일반 매소드 반환형 앞에 쓴 경우 이 코드는 필요가 없다.
        // @ModelAttribute("yoil") getYoil(...){...}은 밑의 과정을 포함한다.
		//		char yoil = getYoil(date);
		
        //3.	어노테이션의 자동 값 저장 능력으로 해당 코드들이 필요 없어졌다.
		//		model.addAttribute("myDate",date);
		//		model.addAttribute("yoil", yoil);
		
		return "yoil";
	}
    
	private @ModelAttribute("yoil") char getYoil(MyDate date) {
		
		return getYoil(date.getYear(), date.getMonth(), date.getDay());
	}

(3) 어노테이션을 생략 가능한 경우 

참조형 변수의 경우 @ModelAttribute를 생략 가능하다. 

//	public String main( @ModelAttribute("mydate")MyDate date) 아래와 동일
	public String main(MyDate date) {

따라서 그냥 참조형 변수 선언만 해도 Spring FrameWork가 알아서 값을 Map에 저장시켜준다. 

 

**참고** 

기본형이나 String 매개변수일 경우 앞에 @RequestParam 어노테이션이 생략되어 있었다. 

해당 어노테이션을 안 적어도 스프링 프레임워크가 자동으로 요청의 파라미터와 기본형,String 매개변수를 Binding 해줬다.

 

참조형일 경우 앞에 @ModelAttribute가 생략되어 있어서 자동으로 참조형 변수 값을 Model의 Map에 저장시켜 줬다.

 

이러한 차이가 있는 이유가 무엇일까? 

먼저, 기본형이나 String 매개변수는 모델에 넣을 이유가 없다. 

왜냐면, View 또한 요청의 parameter에 접근할 수 있기 때문이다. 요청의 파라미터는 입력 부분에 있기 때문에 Controller든 view든 직접 사용할 수 있다. 따라서 모델에 넣을 필요가 없다. 

 

반면 참조형 변수 (ex - 객체)는 입력에 존재하지 않고, 컨트롤러 영역에 존재하는 녀석이다. 따라서 View에서도 해당 변수들을 사용하려면, 컨트롤러와 View의 매개체인 Model에 꼭 있어야한다. 

SpringFramWork는 이를 고려하여 편의를 제공한 것이다. 

2. WebDataBinder

(1) 기능 

WebDataBinder는 요청의 파라미터를 컨트롤러 매서드의 매개변수와 연결할 때 작동하는 녀석이고, 2가지 기능을 한다.

 

첫 번째는 형 변환이다. String으로 들어온 값들을 컨트롤러 매서드의 매개변수에 맞게 형 변환 시켜준다. 이때의 결과와 에러를 BindingResult란 클래스의 객체에 저장한다. 

 

두 번째는 데이터 검증이다. 이는 데이터가 유효한 값인지 검사하는 것이다. 위에서 예를 들어 month는 1~12까지만 가능하다. 이때 month =34가 들어온다면 이는 유효하지 않은 값이다. 

데이터 검증 결과와 발생한 에러도 BindingResult의 객체에 저장한다. 

 

(2) 코드 리뷰 

	
    //BindingResult의 객체는 예외처리 매서드의 매개변수로도 쓰일 수 있다. 
    @ExceptionHandler(Exception.class)
	public String Catcher (Exception ex, BindingResult result) {
		System.out.println("result="+result);
        
        //BindingResult 안의 변수 중 하나인 error 객체를 활용하여
        // 어떤 에러고 어디서 발생했는지 확인한다.
		FieldError error = result.getFieldError();
		
		System.out.println("code="+error.getCode());
		System.out.println("field="+error.getField());
		System.out.println("msg="+error.getDefaultMessage());
		
		ex.printStackTrace();
		return "yoilError";
	}
	
    //원래 BindingResult는 컨트롤러 매서드에서 처리한다.
    // 여기서는 URL을 직접 치고 들어와서 생기는 에러는 
    // 컨트롤러까지 안가고 예외처리 부분에서 catch 해서 처리한다.
    // 위치는 항상 검사 당하는 매개변수들보다 뒤이다.
    
	@RequestMapping("/getYoilMVC6")
	public String main(MyDate date, BindingResult result) {
System.out.println("result= " + result);
		
		if(!isVaild(date)) {
			return "yoilError";
		}

		return "yoil";
	}

요청한 값, 일부러 day에 잘못된 값인 "aaa"를 넣었다.
실행결과 에러 메세지는 aaa를 int로 변환하는 과정에서 에러가 났다고 말해주고 있다.

 

3. 스스로 해보기

yoilMVC5, yoilMVC6 없애고 yoilMVC4로 새로 만들어봄.