진짜 개발자
본문 바로가기

FrameWork/Spring MVC

SpringMVC - 핸들러 메소드 - 2 (@ModelAttribute, @BindingResult, @Valid(매개변수 매핑, 에러, 유효성 검사 다루기))

728x90
요청 파라미터 매핑, 에러, 유효성 검사 다루기(@ModelAttribute, @BindingResult, @Valid)

요청 파라미터 매핑, 에러, 유효성 검사 다루기(@ModelAttribute, @BindingResult, @Valid)

 

 

1. 요청 데이터 매핑 (@ModelAttribute)

@ModelAttribute@RequestParam처럼 요청에 존재하는 데이터를 매개변수로 매핑할수 있도록하는 어노테이션입니다. @ModelAttribute는 기본 타입만이 아닌, 객체로도 자동으로 맵핑을 시켜줍니다.

 

하지만 @ModelAttribute어노테이션의 경우에는 데이터가 반드시 RequestParameter여야만 매핑해주는 것이 아니라 UriPath , Session의 데이터들을 자동으로 매핑해준다는 장점이 있습니다. 예제를 통해 알아보도록 하겠습니다.

 

 

1.1 PathUri 맵핑하기

첫번째로 @ModelAttribute를 이용하여 Uri Path에 존재하는 데이터를 Event 객체로 맵핑해보겠습니다.

 

우리가 맵핑할 Event 객체입니다.

 

우리가 통과해야할 테스트 코드를 먼저 작성해보도록 하겠습니다. 가장 low한 단계인 url에 매핑되는 핸들러가 존재한다면 통과하는 테스트코드부터 작성합니다.

 

/model이라는 uri에 요청시 응답하는 handler가 있는지 테스트 코드를 작성합니다. 이 코드는 지금은 당연히 실패를 할 것 입니다.

 

위의 코드를 통과하기 위해서, /model에 맵핑되는 핸들러를 작성합니다. 테스트에 성공합니다.

 

다음으로 통과해야할 테스트 코드입니다. 우선 uri에 추가적으로 path를 통해 name를 받는 핸들러가 존재하는지의 테스트 코드를 작성합니다. 당연히 테스트에 실패합니다.

 

핸들러를 위와같이 수정하여 name라는 이름으로 url로 부터 데이터를 가져옵니다.

 

이번에는 테스트코드를 다음과 같이 수정하여, 핸들러의 응답으로 json데이터가 오는데, 그 데이터중 name이라는 키값의 value는 jjy 데이터가 응답되도록 합니다.

 

핸들러를 위와 같이 수정합니다. @ResponseBody를 통해 응답 본문에 응답 데이터를 담도록하고, @ModelAttribute를 이용하여 요청을 Event객체로 맵핑합니다. 마지막으로 event를 return 합니다.

 

 

1.2 Parameter로 전달받는 데이터 맵핑하기

이번에는 사용자가 Parameter에 데이터를 담아 요청하는 경우, @ModelAttribute를 이용하여 객체로 맵핑하는 방법을 알아보겠습니다.

 

먼저 여러 데이터를 맵핑하기 위해 Event 클래스를 위와같이 수정했습니다.

 

이번에는 그냥 한번에 통과해야할 테스트 코드를 작성해보겠습니다. parameter로 데이터를 전달하기 위해서는 위와 같이 작성할 수 있습니다. (?name=jjy&age=10)

 

또는 이렇게도 테스트 코드를 작성할 수도 있습니다.

 

핸들러를 다음과 같이 작성한 후 테스트를 합니다.

 

 

 

2. 에러 다루기(@BindingResult)

서버 개발자는 사용자가 항상 우리가 원하는 형식의 데이터를 보낸다고 생각하면 안됩니다. 예를 들면 우리는 앞서 만든 Event 객체의 age에는 정수형 값이 전달되어 어떠한 처리를 할것입니다. 하지만 사용자가 age에 int형 값이 아닌 String형태의 값을 보냈다면 어떨까요?

 

위와 같이 테스트를 하면 당연히 binding에 실패하며 400error가 나타날 것입니다.

 

에러다루기(BindingResult)


위 상황의 문제점은 그냥 error만 나타나고 핸들러 메소드가 끝난다는 점입니다. 이 때 직접 에러를 핸들링 하고 싶다면, Handler 메소드에 BindingResult 매개변수를 추가해주면 됩니다.

*주의

꼭 @ModelAttribute의 바로 오른쪽 매개변수이어야 합니다.

 

위의 코드와 같이 그냥 BindingResult 매개변수만 추가해주면 끝입니다. 테스트 결과를 보겠습니다.

 

상태코드 200을 반환하며 테스트에 성공하는 것을 볼 수 있습니다. 결과값에 age가 바인딩 되지 못해 null을 나타내는것을 볼 수 있습니다.

 

BindingResult를 이용해 에러들을 출력해보겠습니다. 위와 같이 age 변수에 string을 바인딩 하려다보니 NumberFormatException이 나타나는것을 볼 수 있습니다. 위의 코드를 이용해 사용자가 입력한 값이 허용되지 않는 경우의 처리를 할 수 있습니다.

 

 

 

3. 값 유효성 검사 및 처리(@Valid, @Validated)

앞선 상황(Integer에 String을 바인딩) 같이 특별한 에러를 발생시키는 경우가 아닌, 특정 값을 사용하지 못하고 싶은 경우에는 유효성 검사를 통해서 사용자의 입력값을 제어할 수 있습니다. 바로 @Valid , @Validated어노테이션을 이용하는 것 인데요. 예제를 통해 알아보도록 하겠습니다.

 

 

3.1 @Valid 어노테이션으로 Null 값 검증하기

사용자가 값을 아에 입력하지 않는 경우에는 서버에서 어떤 식으로 처리가 될까요? 정답은 null로 맵핑한다 입니다. 테스트 코드를 통해 알아보도록 하겠습니다.

 

위의 코드는 간단히 사용자의 요청에 담긴 값들을 Event 객체로 맵핑한뒤 Http Body에 그 데이터를 담아 반환하는 핸들러입니다.

 

사용자가 아무런 입력을 하지 않은 경우입니다. 200 코드가 나타나며 테스트에 성공합니다. 하지만 Body 담겨온 Json의 각 데이터들이 모두 null입니다.

 

우선 Dto를 수정합니다. 꼭 사용자로부터 입력받아야할 데이터에 해당하는 필드에 @NotNull어노테이션을 추가합니다.

 

핸들러의 매개변수 앞에 @Valid라는 어노테이션을 추가로 등록합니다. 또 테스트시 어떤 에러가 나타나는지 콘솔에 출력하기 위해서, BindingResult를 매개변수에 추가하고, bindingResult를 이용해 Error의 내용을 출력하는 코드를 작성합니다.

 

name, age필드가 null임을 알려주는 error가 콘솔에 나타납니다. 현재는 BindingResult를 매개변수로 받고 있기 때문에 일단 테스트가 통과한 것입니다.

 

 

 

3.2 @Validated를 이용해 유효성 검사 그룹을 지정

@Validated를 이용하면 그룹을 지정하여 유효성 검사를 수행할 수 있습니다. 예제를 통해 알아보겠습니다.

 

우선 interface 로 Group들을 생성합니다. Name과 Age 각각에 적용할 Group을 생성했습니다. 그 후 유효성 검사 어노테이션의 옵션중 하나인 groups를 이용하여 앞서 생성한 interface중 포함될 그룹의 interface.class를 지정합니다.

 

Handler 메소드에서는 @Valid@Validated로 변경합니다. @Validated의 속성으로는 Event.GroupInterface.class를 입력합니다. 위의 핸들러는 사용자가 보내는 요청 데이터에 Age값이 Null인지 확인을 하게 됩니다.

 

Name과 Age를 모두 Null로하여 테스트를 진행합니다. 결과로 Age값이 Null이라는 에러만이 나타납니다.