스프링 MVC 적용하면서 대강 정리한 "노트"입니다. 팁이라고하기엔 좀 모자란듯...^^ (오류가 있을수도 있으니 너무 맹신하지는 마세요..-.-);
-. 한 웹app에 여러개의 *-servlet.xml 사용하기 - web.xml에서 원하는 만큼 DispatcherServlet을 선언하고 그 서블릿이름에 대응하는 [servlet-name]-servlet.xml을 작성한다. - 이렇게 하면 한개의 웹APP 내부에서 웹자원을 몇개의 그룹으로 나누어 별도로 설정하는 것이 가능해진다.
-. 웹자원에 AOP 적용하기 - 핸들러 매핑에 인터셉터를 걸면 된다.
<bean id="signonInterceptor" class="xxx.SignonInterceptor"> <property name="signonView"> <value>redirect:/login_form.html</value> </property> </bean>
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="interceptors"> <list> <ref bean="signonInterceptor"/> </list> </property> <property name="mappings"> .... ....
</property> </bean>
- 그럼 Controller를 통하지 않고 호출되는 JSP에 AOP 적용하려면? => 우선은 web.xml에서 jsp 자원들이 DispatcherServlet을 타도록 만든다음 UrlFilenameViewController를 이용한다. - 위의 SimpleUrlHandlerMapping 의 mappings 속성을 아래처럼 설정한다.(패턴은 적절히 수정)
<property name="mappings"> <props> <prop key="*">jspViewController</prop> </props> </property> .... ....
<bean id="jspViewController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" />
- 그럼 특정 자원에 대해서만 AOP를 적용하지 않으려면?(ex: 로그인폼과 로그인콘트롤러에는 인증인터셉터를 걸면 안된다...) => AOP적용하면 안되는 자원에 대한 핸들러 매핑을 추가로 설정하고 우선 순위를 부여한다. (아래에서 publicHandlerMapping에는 인증인터셉터가 없다)
<bean id="publicHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="order"><value>1</value></property> <property name="mappings"> <props> <prop key="/signon/LoginForm.do">jspViewController</prop> <prop key="/signon/Login.do">signonController</prop> </props> </property> </bean>
<bean id="protectedHandlerMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> <property name="order"><value>2</value></property> <property name="interceptors"> <list> <ref bean="signonInterceptor"/> </list> </property> <property name="mappings"> <props> <prop key="*">jspViewController</prop> </props> </property> </bean>
- 위와 같은 설정에서 /signon/Login.do 요청이 들어오면 url패턴은 publicHandlerMapping과 protectedHandlerMapping에 모두 해당된다 할지라도 order 순위에 의해 publicHandlerMapping이 그 요청을 처리하게 된다.
-. ModelAndView에 여러 개의 객체를 저장하기 - Map을 만들어 거기에 적절한 이름으로 저장하면 된다.
return new ModelAndView(this.successView, map);
- JSP에서는 Map에 저장된 키로 request 스코프에서 꺼내어 쓰면된다.
-. 콘트롤러 실행후 특정 url로 redirect하는 방법 - ModelAndView에 넘길 url 앞에 redirect: 접두문자열을 붙여주면 된다.
<bean name="xxxController" class="xxx.XxxController"> <property name="successView"><value>redirect:/admin/XXXForm.do</value></property> </bean>
-. 에러 종류별로 에러페이지 다르게 지정하기- exception resolver 빈을 설정한다. exception resolver는 콘트롤러에서 발생한 에러를 잡아낸다.
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <prop key="org.springframework.dao.DataIntegrityViolationException">DupError.jsp</prop> <prop key="org.springframework.dao.DataAccessException">DBError.jsp</prop> <prop key="java.lang.Exception">Error.jsp</prop> </props> </property> </bean>
- exceptionMappings의 각 키의 속성값은 exception 타입별 jsp 페이지 경로이며 이 또한 view resolver에 의해 해석된다(만약 뷰resolver에 suffix를 .jsp로 주었다면 위의 설정에서 Error.jsp는 그냥 Error로 설정해야할 것이다). - 의문 : 위 설정이 콘트롤러에서 포워딩되는 JSP에도 적용이 되는가? => 안된다!!! - jsp에서 발생한 에러는 jsp 페이지에 선언한 error 페이지로 가거나 jsp에서 error 페이지 선언을 안했으면 web.xml에 정의한 error 페이지로 간다.
- SimpleFormController 를 이용할 경우엔 onSubmit 메소드안에서 try { if (siteForm.isNewSite()) { this.siteService.insertSite(siteForm.getSite()); } else { this.siteService.updateSite(siteForm.getSite()); } } catch (DataIntegrityViolationException ex) { errors.rejectValue( "site.siteip", "SITE_IP_ALREADY_EXISTS", "Site IP already exists: choose a different Site ip."); return showForm(request, response, errors); } 이런식으로 에러처리가 가능하다.
-. 프로세스 처리 과정 - Handler mappings가 url을 분석하여 Controllers 선택 => Controllers는 로직수행후 지정된 View를 생성 => ViewResolvers는 뷰를 해석 => 해석된 뷰로 포워딩
-. 핸들러 매핑 - 여러개의 핸들러 매핑이 있을 때 순서를 부여할 수 있다. - 패턴 사용가능하다... 특정 패턴의 url을 하나의 콘트롤러에 매핑할 수 있다. - url 별로 빈네임을 설정하기 위해서 : SimpleUrlHandlerMapping 이용 - url을 바로 빈네임으로 사용하기 위해서 : BeanNameUrlHandlerMapping 이용(아래 소스)
<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<bean name="/editaccount.form" class="org.springframework.web.servlet.mvc.SimpleFormController"> <property name="formView"><value>account</value></property> <property name="successView"><value>account-created</value></property> <property name="commandName"><value>Account</value></property> <property name="commandClass"><value>samples.Account</value></property> </bean>
- setAlwaysUseFullPath 속성에 대한 API 설명 읽어볼것.
=> setAlwaysUseFullPath 속성이 false(default)일 때 web.xml에서 /abc/* 로 매핑한 상태에서 url요청이 /abc/test.do 로 들어왔다면 빈네임은 /test.do 로 해야한다는 것이다.
-. 무한루핑 피하기
[web.xml] "test"라는 이름의 Dispatcher 서블릿에 대한 url 패턴매핑 : /test/jsp/*
[test-servlet.xml] : test Dispatcher 서블릿에 대한 컨텍스트 파일
<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="prefix"><value>/test/jsp/</value></property> <property name="suffix"><value>.jsp</value></property> </bean>
=> 이 상황에서 viewResolver에 의한 url 조합이 web.xml의 서블릿 매핑 패턴과 일치하면 무한루프에 빠진다. 주의할 것.
-. UrlFilenameViewController - UrlFilenameViewController은 url에서 파일명 부분만 빼내어 뷰 url을 만든다. 즉 url이 /foo/bar/test.do 로 들어오건 /bar/foo/test.do로 들어오건 상관없이 뷰 url은 test가 된다. - 따라서 UrlFilenameViewController를 쓸 경우에는 경로는 다르지만 이름은 동일한 매핑은 없는지 주의해야 한다.
-. JSP에서 스프링 빈 참조하기- servlet context로부터 스프링 WebApplicationContext를 얻은 후 거기서 빈을 액세스한다.
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext); SomeBean = (SomeBean)wac.getBean("beanName");
- 그러나 이 방법은 너무 힘들어보인다. 그래서 혹시나해서 스프링 태그라이브러리를 찾아보았다.
- 스프링 태그라이브러리에는 빈을 직접 리턴해주는 태그가 안보인다...왜 없을까?
=> 아시는분? JSTL 때문일까? bind 태그를 잠깐 봤는데... 잘이해안됨.
=> 내가 바라는 건 콘트롤러 통하지 않는 JSP에서 특정 빈의 속성값을 쉽게 액세스하고 싶은거다.
-. web.xml에 대한 푸념 - web.xml에서 서블릿 url 패턴매핑에 *.foo 나 /foo/*는 되는데 이 둘의 조합은 왜 안될까? => 패턴의 우선 순위 등이 문제일까? 심플하게 갈려고?
- 에러페이지 설정하는데 Exception 클래스 타입별로 다른 에러페이지를 지정했는데, 예외 클래스간의 관계가 부모자식인게 있으면 최상위 부모 클래스에 대한 에러페이지가 보여진다. => 왜 상식과 어긋나게 행동하게 했는지...
-. web.xml에서의 url 패턴매핑에 대한 의견 - 개인적으로 /foo/* 식의 패턴매핑은 바람직하지 않다고 생각한다. 왜냐면 static 자원(html,image 등)도 서블릿을 타게 만들기 때문이다. 물론 컨테이너 앞단에 웹서버를 둔다면 어차피 static 자원은 웹서버가 처리하기 때문에 상관은 없겠지만... 하여간 *.foo 식의 패턴을 사용하는게 좋다고 생각한다.
-. 정적 자원에 대한 의문 - 서블릿/jsp 에서 static 자원으로 포워딩했을 때 컨테이너에서 도대체 무슨일이 벌어지는 걸까? 쓰레드 제어권이 static 자원으로 넘어갈리는 없을테고... redirect같은 특수한 응답헤더가 존재하는가? |
|