※ HandlerMethodArgumentResolver에 관한 자세한 내용은 http://to-dy.tistory.com/57?category=700248 에서 확인할 수 있다.
1. CommandMap 클래스 생성
<annotation-driven/>을 선언하게 되면 HandlerMethodArgumentResolver는 컨트롤러의 파라미터가 Map 형식이면 안된다. 우리가 HandlerMethodArgumentResolver를 사용하여 기능을 만들었다고 해도 컨트롤러에서 Map형식으로 보내오면, 우리가 설정한 클래스가 아닌, Spring에서 기본적으로 설정된 ArgumentResolver를 거쳐가기 때문이다. 물론 <annotation-driven/>을 선언하지않으면 Map을 그대로 써도 된다. 하지만, Spring을 개발하면서 <annotation-driven/>을 선언하는 경우들이 생기기 때문에 우리는 Map 대신에 CommandMap 클래스를 만들어서 사용한다.
CommandMap.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 | package tody.prjdoo.common.common; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set; public class CommandMap { Map<String,Object> map = new HashMap<String,Object>(); public Object get(String key){ return map.get(key); } public void put(String key, Object value){ map.put(key, value); } public Object remove(String key){ return map.remove(key); } public boolean containsKey(String key){ return map.containsKey(key); } public boolean containsValue(Object value){ return map.containsValue(value); } public void clear(){ map.clear(); } public Set<Entry<String, Object>> entrySet(){ return map.entrySet(); } public Set<String> keySet(){ return map.keySet(); } public boolean isEmpty(){ return map.isEmpty(); } public void putAll(Map<? extends String, ?extends Object> m){ map.putAll(m); } public Map<String,Object> getMap(){ return map; } } | cs |
CommandMap 클래스는 내부적으로 Map을 하나 생성하고, 그 맵에 모든 데이터를 담는 역할을 한다. 중요한 것은 절대. Map을 상속받아서는 안된다. 많은 메서드들 때문에 어려워 보일텐데, 거의 대부분은 Map의 기본기능을 다시 호출하는 작업들이다. 여러곳에서 CommandMap을 Map과 똑같이 사용할 수 있게 만들어 두었다.
2. HandlerMethodArgumentResolver 작성
CustomMapArgumentResovler.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 | package tody.prjdoo.common.resolver; import java.util.Enumeration; import javax.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import tody.prjdoo.common.common.CommandMap; public class CustomMapArgumentResovler implements HandlerMethodArgumentResolver { @Override public Object resolveArgument(MethodParameter arg0, ModelAndViewContainer arg1, NativeWebRequest arg2, WebDataBinderFactory arg3) throws Exception { // TODO Auto-generated method stub CommandMap commandMap = new CommandMap(); HttpServletRequest request = (HttpServletRequest) arg2.getNativeRequest(); Enumeration<?> enumeration = request.getParameterNames(); String key = null; String[] values = null; while(enumeration.hasMoreElements()){ key = (String) enumeration.nextElement(); values = request.getParameterValues(key); if(values != null){ commandMap.put(key, (values.length > 1) ? values:values[0] ); } } return commandMap; } @Override public boolean supportsParameter(MethodParameter arg0) { // TODO Auto-generated method stub return CommandMap.class.isAssignableFrom(arg0.getParameterType()); } } | cs |
- 21행 : CommandMap 객체 생성한다.
- 28~30행 : request에 담겨있는 값을 iterator를 이용해서 하나씩 가져오는 로직이다.
- 32행 : request에 담겨있는 모든 key와 value를 commandMap에 저장한다.
- 35행 : 모든 파라미터가 담겨있는 commandMap을 반환한다.
3. CustomMapArgumentResovler 등록
action-servlet.xml에 아래의 CustomMapArgumentResovler를 등록하자.
<argument-resolvers> 태그를 이용해서 CustomMapArgumentResolver의 빈(bean)을 수동으로 등록한다.
1 2 3 4 5 | <annotation-driven> <argument-resolvers> <beans:bean class="tody.prjdoo.common.resolver.CustomMapArgumentResolver"></beans:bean> </argument-resolvers> </annotation-driven> | cs |
action-servlet.xml
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 | <?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: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 --> <!-- Enables the Spring MVC @Controller programming model --> <annotation-driven> <argument-resolvers> <beans:bean class="tody.prjdoo.common.resolver.CustomMapArgumentResolver"></beans:bean> </argument-resolvers> </annotation-driven> <!-- 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="order" value="1"/> </beans:bean> <context:component-scan base-package="tody" /> </beans:beans> | cs |
- 13~17행 : CustomMapArgumentResolver를 등록한 부분이다.
4. Controller를 이용해 테스트
정상적으로 동작하는 지 테스트 해보자.
TestController.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 | package tody.prjdoo.common.controller; import java.util.Iterator; import java.util.Map.Entry; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import tody.prjdoo.common.common.CommandMap; @Controller public class TestController { Logger log = Logger.getLogger(this.getClass()); @RequestMapping(value="/testMapArgumentResolver") public ModelAndView testMapArgumentResolver(CommandMap commandMap) throws Exception{ ModelAndView mv = new ModelAndView(""); if(commandMap.isEmpty() == false){ Iterator<Entry<String,Object>> iterator = commandMap.getMap().entrySet().iterator(); Entry<String,Object> entry = null; while(iterator.hasNext()){ entry = iterator.next(); log.debug("key : "+entry.getKey()+", value : "+entry.getValue()); } } return mv; } } | cs |
- 18행 :
- 19행 : CommandMap으로 파라미터를 받는다.
- 23행 : commandMap에 있는 모든 파라미터를 iterator를 이용하여 출력한다.
5. 실행
서버를 실행시켜 보자. 그럼 아래와 같이 설정한 페이지로 갈 것이다.
이제 주소창에 key=value식의 파라미터를 전송해보자.
ex) /testMapArgumentResolver?name=lovelytody
-> key = name , value = lovelytody
404 Not Found 떴다고 당황하지 말자. 우린 jsp를 만들지 않았기때문에 이게 뜨는 게 정상이다.
우리는 이거보다 console창을 주목해야 한다.
4번째 줄에 key : name, value : loveltody 를 볼 수 있다. 우리가 get방식으로 보낸 name이라는 key와 lovelytody라는 value이다.
이렇게 뜨면, CustomMapArgumentResolver가 정상적으로 등록된 것이다.
두 개의 키와 두 개의 값을 전송해보자.
ex) /testMapArgumentResovler?aaa=hello&bbb=world
console창을 확인하자.
정상적으로 들어오는 것을 확인할 수 있다.
CustomMapArgumentResolver 클래스를 만듦으로써, 클라이언트들이 전송한 데이터들이 request에 담겨서 서버로 전송되고, 서버에서는 그 데이터를 DB에 입력하거나 수정할 수 있게 된다. 주로 게시판에 많이 사용될 것 같다. 나는 학원에서도, 그리고 주로 VO를 써왔는데 이런 형식은 처음이라 신기했다. 뭐가 더 쉬운거냐, 에 대해서는 둘 다 비슷하다. 고 답할 수 있고, 뭐가 더 좋냐, 에 대해서는 사용하는 곳에 따라 달라질 것 같다. 라고 답할 수 있다. 헤헤 사실 나도 잘 모르겠다!
참고한 블로그 :
http://addio3305.tistory.com
'공부 > Spring' 카테고리의 다른 글
Spring 개발 - 게시판 만들기 #글작성 (5) | 2018.10.13 |
---|---|
Spring 개발 - 게시판 만들기 #목록 (15) | 2018.09.17 |
스프링(Spring) 개발 - 로그인, 로그아웃 하기 (61) | 2018.05.11 |
스프링(Spring) 개발 - Hibernate를 이용한 회원가입하기 (7) | 2018.05.10 |
스프링(Spring) 개발 - Validator를 이용한 회원가입 만들기 (50) | 2018.05.09 |