[Info]Tags categorized posts and contents patterns..

[AJAX] Ajax Code E xamples.. [Book] About the book.. [CSS] CSS Code E xamples.. [DB] Sql Code E xamples.. [DEV] All development stor...

레이블이 MVC인 게시물을 표시합니다. 모든 게시물 표시
레이블이 MVC인 게시물을 표시합니다. 모든 게시물 표시

2016년 8월 18일 목요일

[JAVA] 19장 포틀릿(Portlet) MVC 프레임워크 #1..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

이 문서는 개인적인 목적이나 배포하기 위해서 복사할 수 있다. 출력물이든 디지털 문서든 각 복사본에 어떤 비용도 청구할 수 없고 모든 복사본에는 이 카피라이트 문구가 있어야 한다.

19. 포틀릿(Portlet) MVC 프레임워크
19.1 소개

JSR-168 자바 포틀릿 명세
포틀릿 개발과 관련한 일반적인 정보는 Sun이 작성한 "JSR 168 소개"와 JSR-168 명세를 보기 바란다.

전통적인 (서블릿 기반의) 웹 개발 지원에 추가적으로 스프링은 JSR-168 포틀릿 개발도 지원한다. 포틀릿 MVC 프레임워크는 웹 MVC 프레임워크의 복사판과도 같고 동일한 뷰 추상화와 통합 기술을 사용한다. 그러므로 이번 장을 읽기 전에 Chapter 16, 웹 MVC 프레임워크장과 Chapter 17, 뷰 기술 장을 읽어봐라.

Note
스프링 MVC의 개념이 스프링 포틀릿 MVC에서도 동일하지만 JSR-168 포틀릿의 독특한 흐름(workflow)으로 생겨난 주요한 차이점이 있다는 점을 명심해라.

서블릿의 흐름과 다른 포틀릿의 흐름은 포틀릿으로의 요청이 액션(action) 단계와 렌더링(render) 단계의 두 단계로 구분될 수 있다는 것이다. 액션 단계는 데이터베이스의 변경같은 '백엔드'의 변경사항이나 액션이 일어났을 때 딱 한번만 실행된다. 그 다음 렌더링 단계가 화면을 갱신할 때마다 사용자에게 보여줄 내용을 만든다. 여기서 중요한 점은 하나의 전체 요청에서 액션 단계는 딱 한번만 실행되지만 렌더링 단계는 여러번 실행될 수 있다는 것이다. 이는 시스템의 상태를 변경하는 활동과 사용자에게 보여줄 내용을 생성하는 활동을 깔끔하게 구분해준다. (구분해야 한다)

스프링 웹 플로우(Spring Web Flow)
스프링 웹 플로우 (SWF, Spring Web Flow)는 웹 어플리케이션 페이지 흐름을 관리하는 최상의 솔루션이다.

SWF는 서블릿 환경과 포틀릿 환경에서 모두에서 스프링 MVC, 스트럿츠, JSF같은 프레임워크와 통합한다. 순사하게 요청 모델과는 반대로 대화식 모델의 이점을 갖는 비즈니스 처리과정을 가진다면 SWF가 해결책이 될 것이다.

SWF는 여러 상황에서 재사용할 수 있는 내장된 모쥴처럼 논리적인 페이지 흐름을 갖을 수 있도록 하고 비즈니스 처리과정을 유도하는 제어된 네비게이션드로 사용자를 도와주는 웹 어플리케이션 모듈을 구성하는데 최적이다.

SWF에 대한 자세한 내용은 Spring Web Flow 웹사이트를 참고해라.

포틀릿 요청의 이중 단계는 JSR-168 명세의 진짜 강점 중 하나이다. 예를 들어 사용자가 명시적으로 검색을 재실행하지 않아도 동적인 검색 결과의 화면을 언제나 갱신할 수 있다. 대부분의 다른 포틀릿 MVC 프레임워크는 개발자한테 이 두 단계를 완전히 감추고 가능한한 일반적인 서블릿 개발처럼 보이도록 한다. 스프링 개발팀은 이 접근이 포틀릿을 사용하는 주요한 이점을 없앤다고 생각한다. 그래서 스프링 포틀릿 MVC 프레임워크에서 두 단계의 구분은 유지된다. 이 접근방법의 주요한 영향으로 MVC 클래스의 서블릿 버전이 요청을 다루는 하나의 메서드를 가질 때 MVC 클래스의 포틋릿 버전은 요청을 다루는 두 메서드를 가질 것이다.(하나는 액션단계용이고 하나는 렌더링단계용이다.) 예를 들어 AbstractController의 서블릿 버전은 handleRequestInternal(..) 메서드를 가지지만 AbstractController의 포틀릿 버전은 handleActionRequestInternal(..)와 handleRenderRequestInternal(..) 메서드를 가진다.

포틀릿 프레임워크는 웹 프레임워크에서 DispatcherServlet가 하듯이 요청을 핸들러로 디스패치하는 DispatcherPortlet을 기반으로 설계되었고 설정가능한 핸들러 매핑과 뷰 처리를 제공한다. 파입업로드도 같은 방법으로 지원한다.

포틀릿 MVC에서는 로케일 처리와 테마 처리를 지원하지 않는다. 이 영역은 포탈(portal)/포틀릿 컨테이너의 범위이므로 스프링 수준에서 다루기는 적합하지 않다. 하지만 로케일에 기반한 스프링의 모든 메카니즘은(메시지의 국제화 등) DispatcherPortlet가 DispatcherServlet과 같은 방법으로 현재 로케일을 노출하므로 기능이 제대로 동작할 것이다.

19.1.1 컨트롤러 - MVC에서 C
기본 핸들러는 여전히 아주 간단한 Controller 인터페이스이고 두 메서드를 제공한다.

  • void handleActionRequest(request,response)
  • ModelAndView handleRenderRequest(request,response)
포틀릿 프레임워크는 AbstractController, SimpleFormController처럼 거의 동일한 컨트롤러 구현체 계층을 가진다. 데이터 바인딩, 커맨드 객체의 사용, 모델 처리, 뷰 처리는 서블릿 프레임워크와 완전히 동일하다.

19.1.2 뷰 - MVC에서 V
ViewRendererServlet라는 특수한 브릿지 서블릿으로 서블릿 프레임워크의 모든 뷰 렌더링 기능을 직접 사용한다. 이 서블릿을 사용해서 포틀릿 요청을 서블릿 요청으로 변환하고 일반적인 서블릿 인프라 전체를 사용해서 뷰를 렌더링할 수 있다. 이는 JSP, Velocity 등 기존에 존재하는 렌더러 모두를 포틀릿에서도 계속해서 사용할 수 있음을 의미한다.

19.1.3 웹의 범위를 갖는(Web-scoped) 빈
스프링 포틀릿 MVC는 생명주기가 현재 HTTP 요청이나 HTTP Session(일반적인 세션이나 전역 세션 모두)의 범위를 갖는 빈을 지원한다. 이는 스프링 포틀릿 MVC 자체의 특정 기능이라기 보다는 스프링 포틀릿 MVC가 사용하는 WebApplicationContext 컨테이너의 기능이다. 이러한 빈의 범위는 Section 4.5.4, “리퀘스트, 세션, 글로벌 세션 범위”에서 설명한다.

19.2 DispatcherPortlet
포틀릿 MVC는 요청을 컨트롤러로 디스패치하고 포틀릿 어플리케이션을 개발하는 기반 기능을 제공하는 포틀릿에 기반해서 설계된 요청주도 웹 MVC 프레임워크이다. 하지만 스프링의 DispatcherPortlet는 이 이상을 수행한다. DispatcherPortlet은 스프링 ApplicationContext와 완전히 통합되었고 스프링이 가진 모든 기능을 사용할 수 있게 한다.

일반적인 포틀릿처럼 DispatcherPortlet는 웹 어플리케이션의 portlet.xml 파일에 선언한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Xml

<portlet>
  <portlet-name>sample</portlet-name>
  <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
  <supports>
    <mime-type>text/html</mime-type>
    <portlet-mode>view</portlet-mode>
  </supports>
  <portlet-info>
    <title>Sample Portlet</title>
  </portlet-info>
</portlet>

이제 DispatcherPortlet를 설정해야 한다.

포틀릿 MVC 프레임워크에서 각 DispatcherPortlet는 루트 WebApplicationContext에 이미 정의된 모든 빈을 상속받은 자신만의 WebApplicationContext를 가진다. 이 상속된 빈은 포틀릿에 한정된 범위에서 오버라이드할 수 있고 새로운 범위로 한정된 빈은 해당 포틀릿 인스턴스의 로컬로 정의할 수 있다.

DispatcherPortlet 초기화시 포틀릿 MVC 프레임워크는 웹 어플리케이션의 WEB-INF 디렉토리에서 [portlet-name]-portlet.xml라는 파일을 검색해서 정의된 빈을 생성한다.(전역 범위에 같은 이름으로 정의된 모든 빈 정의는 오버라이드한다.)

DispatcherPortlet가 사용하는 설정 위치는 포틀릿 초기화 파라미터로 수정할 수 있다.(자세한 내용은 뒤에 나온다.)

스프링 DispatcherPortlet는 요청을 처리하고 적절한 뷰를 렌더링하는 데 사용하는 몇몇 특수한 빈을 가진다. 이러한 빈은 스프링 프레임워크에 포함되어 있고 설정할 다른 빈과 마찬가지로 WebApplicationContext에서 설정할 수 있다. 이 빈들은 뒤에서 각각 자세히 설명한다. 지금은 DispatcherPortlet을 계속 설명하기 위해서 이러한 빈이 존재한다는 것만 알아두면 된다. 대부분의 빈은 설정을 신경쓰지 않아도 되도록 기본값을 제공한다.

Table 19.1. WebApplicationContext의 특수한 빈

표현식설명
handler mapping(s)(Section 19.5, “핸들러 매핑”) 전처리자, 후처리자, 컨트롤러의 목록으로 이들이 특정 크리테리아와 일치한다면 실행될 것이다.(예를 들어 찾아낸 포틀릿은 모드는 컨트롤러로 지정된다.)
controller(s)(Section 19.4, “컨트롤러”) MVC의 일부로 실제 기능을 제공하는 빈(또는 최소한 기능에 접근하는)
view resolver(Section 19.6, “뷰와 뷰 처리”) 뷰 이름을 뷰 정의로 처리할 수 있다
multipart resolver(Section 19.7, “멀티파트(파일 업로드) 지원”) HTML 폼의 파일 업로드를 처리하는 기능을 제공한다
handler exception resolver(Section 19.8, “예외 처리”) 예외를 뷰에 매핑하거나 복잡한 예외 처리코드를 구현하는 기능을 제공한다











DispatcherPortlet이 해당 DispatcherPortlet 들어오는 요청을 받고 사용할 설정이 되면 요청을 처리하기 시작한다. 아래 목록은 DispatcherPortlet의 요청을 처리하는 전체 과정을 설명한다.

  1. 요청을 처리할 때(뷰 렌더링, 데이터 준비 등) 처리중인 요청이 사용할 로케일을 처리하도록 PortletRequest.getLocale()가 반환하는 로케일을 요청에 바인딩한다.
  2. 멀티파트 리졸버를 지정했고 요청이 ActionRequest라면 해당 요청은 멀티파트인지 검사하고 멀티파트라면 다른 요소가 추가적인 처리를 하도록 MultipartActionRequest로 감싼다. (멀티파트 처리에 대한 자세한 내용은 Section 19.7, “멀티파트(파일 업로드) 지원”를 봐라.)
  3. 적합한 핸들러를 검색한다. 핸들러를 발견하면 모델을 준비하기 위해 핸들러(전처리자, 후처리자, 컨트롤러)에 연결된 실행 체인을 실행할 것이다.
  4. 모델이 반환되면 WebApplicationContext로 설정된 뷰 리졸버를 사용해서 뷰를 렌더링한다. 모델이 반환되지 않는다면(예를 들어 전처리자나 후처리자가 보안때문에 요청을 가로채는 등의 이유로) 요청이 이미 완료되었으므로 뷰를 렌더링하지 않는다.

요청을 처리하는 중 던저진 예외는 WebApplicationContext에 선언된 핸들러 예외 리졸버(handler exception resolver)가 처리한다. 이러한 예외 리졸버를 사용해서 예외가 던저진 경우에 대한 커스텀 동작을 정의할 수 있다.

portlet.xml 파일이나 포틀릿 init 파라미터에 컨텍스트 파라미터를 추가해서 스프링의 DispatcherPortlet를 커스터마이징할 수 있다. 할 수 있는 일은 다음 목록에 나와있다.

Table 19.2. DispatcherPortlet 초기화 파라미터

파라미터설명
contextClassWebApplicationContext를 구현한 클래스로 해당 포틀릿이 사용하는 컨텍스트를 인스턴스화하는데 사용할 것이다. 이 파라미터를 지정하지 않으면 XmlPortletApplicationContext를 사용할 것이다.
contextConfigLocation컨텍스트를 찾을 수 있는 위치를 나타내려고 컨텍스트 인스턴스 (contextClass가 지정한)에 전달하는 문자열이다. 이 문자열은 여러 컨텍스트(여러 컨텍스트 위치가 있는 경우 두번 정의된 빈은 마지막에 정의된 빈을 우선시한다)를 지원하기 위해 여러개의 문자열로 나눌 수 있다.(구분자로 콤마를 사용한다)
namespaceWebApplicationContext의 네임스페이스다. 기본값은 [portlet-name]-portlet이다.
viewRendererUrlDispatcherPortlet가 ViewRendererServlet에 접근할 수 있는 URL이다.(Section 19.3, “ViewRendererServlet” 참고)











19.3 ViewRendererServlet
포틀릿 MVC에서 렌더링 과정은 웹 MVC보다 약간 더 복잡하다. 스프링 웹 MVC의 모든 뷰 기술을 재사용하려면 PortletRequest / PortletResponse를 HttpServletRequest / HttpServletResponse로 변환하고 View의 render 메서드를 호출해야 한다. 이렇게 하기 위해서 DispatcherPortlet이 이 목적때문에 존재하는 전용 서블릿 ViewRendererServlet을 사용한다.

DispatcherPortlet 렌더링이 동작하려면 웹 어플리케이션의 web.xml 파일에 다음과 같이 ViewRendererServlet 인스턴스를 선언해야 한다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Xml

<servlet>
  <servlet-name>ViewRendererServlet</servlet-name>
  <servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>ViewRendererServlet</servlet-name>
  <url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>

실제 렌더링을 수행하기 위해 DispatcherPortlet는 다음의 과정을 진행한다.
  1. DispatcherServlet이 사용하는 같은 WEB_APPLICATION_CONTEXT_ATTRIBUTE 키에 속성으로 WebApplicationContext를 요청에 바인딩한다.
  2. ViewRendererServlet에서 사용할 수 있도록 Model과 View 객체를 요청에 바인딩한다.
  3. PortletRequestDispatcher를 생성하고 ViewRendererServlet에 매핑된 /WEB- INF/servlet/view URL을 사용해서 include를 수행한다.
그 다음 ViewRendererServlet이 View의 render 메서드를 적절한 인자로 호출할 수 있다.

ViewRendererServlet의 실제 URL은 DispatcherPortlet의 viewRendererUrl 설정 파라미터를 사용해서 변경할 수 있다.

19.4 컨트롤러
포틀릿 MVC의 컨트롤러는 웹 MVC 컨트롤러와 아주 유사하고 서로간에 코드를 포딩하는 것은 아주 간단하다.

포틀릿 MVC 컨트롤러 아키텍처의 기반은 다음 목록에 나온 org.springframework.web.portlet.mvc.Controller 인터페이스이다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Java

public interface Controller {

  /**
   * 요청의 렌더링을 처리하고 DispatcherPortlet이 렌더링할
   * ModelAndView 객체를 반환한다.
   */
  ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response)
    throws Exception;

  /**
   * 액션 요청을 처리한다. 아무것도 반환하지 않는다.
   */
  void handleActionRequest(ActionRequest request, ActionResponse response)
    throws Exception;
}

여기서 보듯이 포틀릿 Controller 인터페이스는 포틀릿 요청의 두 단계(액션 요청과 렌더링 요청)을 다루는 두 개의 메서드를 필요로 한다. 액션 단계는 액션 요청을 처리할 수 있어야 하고 렌더링 단계는 렌더링 요청을 처리하고 적절한 모델과 뷰를 반환할 수 있어야 한다. Controller 인터페이스가 상당히 추상화되어 있으므로 스프링 포틀릿 MVC는 필요로 할 많은 기능을 담고 있는 다수의 컨트롤러를 제공한다. 이러한 대부분의 컨트롤러는 스프링 웹 MVC의 컨트롤러와 아주 유사하다. Controller 인터페이스는 모든 컨트롤러가 필요로하는 많은 공통 기능(액션 요청, 렌더링 요청 처리, 모델과 뷰 반환)을 정의하고 있다.

19.4.1 AbstractController와 PortletContentGenerator
물론 Controller 인터페이스만으로는 충분치 않다. 기본 인프라스트럭처인 AbstractController를 상속받은 스프링 포틀릿 MVC의 모든 Controller를 제공하려면 클래스가 스프링의 ApplicationContext에 접근할 수 있고 캐싱을 제어할 수 있어야 한다.

Table 19.3. AbstractController가 제공하는 기능

파라미터설명
requireSession해당 Controller가 동작하는 세션을 필요로 하는지를 나타낸다. 이러한 컨트롤러가 요청을 받았을 때 세션이 제공되지 않으면 SessionRequiredException를 사용해서 사용자에게 알려준다.
synchronizeSession사용자 세션 동기화를 해당 컨트롤러가 제어하기를 원한다면 이 파라미터를 사용해라. 더 자세히 보자면 컨트롤러를 확장하는 것은 변수를 지정한 경우 사용자 세션의 동기화를 할 handleRenderRequestInternal(..)와 handleActionRequestInternal(..) 메서드를 오버라이드 할 것이다.
renderWhenMinimized포틀릿이 최소화된 상태(minimized state)일 때 컨트롤러가 뷰를 실제로 렌더링하게 하려면 이 파라미터를 true로 설정해라. 기본적으로 이는 false이므로 최소화된 상태의 포틀릿은 아무 내용도 보여주지 않을 것이다.
cacheSeconds포틀릿에 정의된 캐시 기본 만료(expiration)를 컨트롤러가 오버라이드하려면 이 파라미터를 양수로 지정해라. 기본적으로 이 파라미터는 기본 캐싱을 바꾸지 않는 의미로 -1로 설정되어 있다. 이 파라미터를 0으로 설정하면 결과를 절대 캐싱하지 않는다.













requireSession와 cacheSeconds 프로퍼티는 PortletContentGenerator (AbstractController의 수퍼클래스)에 선언하지만 자세함을 위해서 여기에 포함시켰다.

AbstractController를 컨트롤러의 기반클래스로 사용하는 경우(이미 해당 작업을 하는 다른 클래스가 많이 있으므로 추천하는 방법은 아니다.) handleActionRequestInternal(ActionRequest, ActionResponse) 메서드나 handleRenderRequestInternal(RenderRequest, RenderResponse) 메서드(혹은 둘다)를 오버라이드하고 로직을 구현하고 ModelAndView 객체를 반환 (handleRenderRequestInternal인 경우에)해야 한다.

handleActionRequestInternal(..)와 handleRenderRequestInternal(..)의 기본 구현체는 PortletException를 던진다. 이는 JSR-168 명세서 API의 GenericPortlet 동작과 일관성이 있다. 그러므로 컨트롤러가 다루기를 원하는 메서드만 오버라이드 해야한다.

클래스와 웹 어플리케이션 컨텍스트의 선언으로 구성된 간단한 예제가 다음에 나와 있다.


 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
Java

package samples;

import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.springframework.web.portlet.mvc.AbstractController;
import org.springframework.web.portlet.ModelAndView;

public class SampleController extends AbstractController {

    public ModelAndView handleRenderRequestInternal(RenderRequest request, RenderResponse response) {
        ModelAndView mav = new ModelAndView("foo");
        mav.addObject("message", "Hello World!");
        return mav;
    }
}


Xml

<bean id="sampleController" class="samples.SampleController">
  <property name="cacheSeconds" value="120"/>
</bean>

이 아주 간단한 컨트롤러가 동작하는데 위의 클래스와 웹 어플리케이션의 선언이 핸들러 매핑 구성(Section 19.5, “핸들러 매핑” 참고)외에 해야하는 전부이다.

19.4.2 그 외 간단한 컨트롤러
AbstractController를 확장할 수 있더라도 스프링 포틀릿 MVC는 간단한 MVC 어플리케이션에서 공통적으로 사용하는 기능을 제공하는 다수의 구현체를 제공하고 있다.

ParameterizableViewController는 웹 어플리케이션 컨텍스트에서 반환할 뷰 이름을 지정할 수 있다는 점(뷰 이름을 하드코딩할 필요가 없다)을 제외하면 기본적으로 위의 예제와 같다.

PortletModeNameViewController는 뷰 이름으로 포틀릿의 현재 모드를 사용한다. 그래서 포틀릿이 View 모드라면(예시: PortletMode.VIEW) 뷰 이름으로 "view"를 사용한다.

19.4.3 커맨드 컨트롤러
스프링 포틀릿 MVC는 스프링 웹 MVC와 정확히 같은 계층의 커맨트 컨트롤러를 가진다. 커맨드 컨트롤러는 데이터 객체와 상호작용하고 PortletRequest의 파라미터를 지정한 데이터 객체로 동적으로 바인딩하는 방법을 제공한다. 데이터 객체는 프레임워크에 특화된 인터페이스를 구현하지 않아야 하므로 원한다면 퍼시스턴트 객체를 직접 조작할 수 있다. 커맨드 컨트롤러로 무엇을 할 수 있는지 살펴보기 위해서 어떤 커맨드 컨트롤러를 사용할 수 있는지 보자.

  • AbstractCommandController - 자신만의 커맨트 컨트롤러를 생성하는데 사용할 수 있는 커맨드 컨트롤러로 요청 파라미터를 지정한 데이터 객체에 바인딩할 수 있다. 이 클래스는 폼(form) 기능을 제공하지 않지만 유효성검사 기능은 제공하고 컨트롤러에서 요청의 파라미터로 채워진 커맨드 객체로 무엇을 할지 지정하게 한다.
  • AbstractFormController - 폼 제출을 지원하는 추상 컨트롤러다. 이 컨트롤러를 사용해서 폼을 만들 수 있고 컨트롤러에서 획득한 커맨드 객체를 사용해서 폼을 유지할 수 있다. 사용자가 폼을 채운 후에 AbstractFormController가 필드를 바인딩하고 유효성을 검사하고 객체를 적절한 액션을 취하는 컨트롤러에 다시 전달한다. 유효하지 않은 폼 제출 (중복제출), 유효성 검사, 일반적인 폼 워크플로우의 기능을 지원한다. 폼 표현과 성공에 어떤 뷰를 사용할지 결정하는 메서드를 구현한다. 폼은 필요하지만 어플리케이션 컨텍스트에서 사용자를 보여줄 뷰를 지정하기 원치 않는다면 이 컨트롤러를 사용해라.
  • SimpleFormController - AbstractFormController의 구현체로 대응되는 커맨드 객체로 폼을 생성할 때 추가적인 지원을 제공한다. SimpleFormController는 커맨드 객체, 폼에 대한 뷰이름, 폼 제출이 성공적으로 이뤄졌을 때 사용자를 보여주는 페이지의 뷰이름을 지정하게 한다.
  • AbstractWizardFormController ? 여러 화면에 걸쳐서 커맨드 객체의 내용을 수정하는 마법사방식의 인터페이스를 제공하는 AbstractFormController의 구현체이다. 종료, 취소, 페이지 변경등 여러 사용자 동작을 지원하고 이 모든 기능을 화면의 요청 파라미터로 쉽게 지정한다.

이러한 커맨드 컨트롤러는 아주 강력하지만 효율적으로 사용하려면 어떻게 동작하는지 자세히 이해해야 한다. 이 컨트롤러의 전체 계층에 대해서는 Javadoc을 자세히 보고 사용하기 전에 예제 구현체를 살펴봐라.

19.4.4 PortletWrappingController
새로운 컨트롤러를 개발하는 대신 기존에 존재하는 포틀릿을 사용해서 DispatcherPortlet에서 요청을 컨트롤러로 매핑할 수 있다. PortletWrappingController를 사용해서 Controller로 이미 존재하는 Portlet을 다음과 같이 인스턴스화 할 수 있다.


1
2
3
4
5
6
7
8
9
Xml

<bean id="myPortlet" class="org.springframework.web.portlet.mvc.PortletWrappingController">
  <property name="portletClass" value="sample.MyPortlet"/>
  <property name="portletName" value="my-portlet"/>
  <property name="initParameters">
    <value>config=/WEB-INF/my-portlet-config.xml</value>
  </property>
</bean>

이러한 포틀릿으로 들어오는 요청 이전과 이후의 처리를 위해 인터셉터를 사용할 수 있으므로 이는 아주 가치가 있다. JSR-168가 필터 메카니즘을 전혀 지원하지 않으므로 이 방법이 아주 편리하다. 예를 들어 이를 MyFaces JSF 포틀릿을 하이버네이트 OpenSessionInViewInterceptor로 감싸는데 사용할 수 있다.

19.5 핸들러 매핑
핸들러 매핑을 사용해서 들어오는 포틀릿 요청을 적당한 핸들러로 매핑할 수 있다. 사용할 수 있는 몇몇 핸들러 매핑이 존재하지만(예를 들면 PortletModeHandlerMapping) 먼저 HandlerMapping의 일반적인 개념을 살펴보자.

Note: 의도적으로 “Controller” 대신에 “Handler”라는 용어를 여기서 사용하고 있다. DispatcherPortlet은 스프링 포틀릿 MVC 자체의 컨트롤러와는 다른 방법으로 요청을 처리하는데 사용하도록 설계되었다. 핸들러는 포틀릿 요청을 다룰 수 있는 객체이다. 컨트롤러는 핸들러의 하나의 예시이면서 기본값이다. 다른 프레임워크와 함께 DispatcherPortlet를 사용하려면 HandlerAdapter의 대응되는 구현체가 필요한 전부이다.

기본 HandlerMapping이 제공하는 기능은 들어오는 요청과 일치하는 핸들러를 반드시 가지고 요청에 적용되는 핸들러 인터셉터의 목록을 가질 수도 있는 HandlerExecutionChain의 전달이다. 요청이 들어올 때 DispatcherPortlet는 요청을 검사하고 적절한 HandlerExecutionChain에 접근하도록 핸들러 매핑에 전달한다. 그 다음 DispatcherPortlet은 체인에 있는(존재한다면) 핸들러와 인터셉터를 실행할 것이다. 이러한 개념은 스프링 웹 MVC와 완전히 같다.

선택적으로 인터셉터(실제 실행되는 핸들러 이전과 이후에 실행된다)를 가질 수 있고 설정가능한 핸들러 매핑의 개념은 굉장히 강력하다. 지원하는 다수의 기능은 커스텀 HandlerMapping의 일부가 될 수 있다. 들어오는 요청의 포틀릿 모드에 기반하지 않고 요청과 연관된 세션의 특정 상태에 기반해서 핸들러를 선택하는 커스텀 핸들러 매핑을 생각해 보자.

스프링 웹 MVC에서 핸들러 매핑은 일반적으로 URL에 기반한다. 포틀릿에서는 URL같은 것이 없기 때문에 매핑을 제어하는 다른 메카니즘을 사용해야 한다. 가장 일반적인 두가지는 포틀릿 모드와 요청 파라미터이지만 포틀릿 요청에서 사용할 수 있는 것들은 커스텀 핸들러 매핑에서도 사용할 수 있다.

이번 섹션의 남은 부분은 스프링 포틀릿 MVC에서 가장 일반적으로 사용하는 핸들러 매핑을 설명한다. 이 핸들러 매핑은 모두 AbstractHandlerMapping을 확장하고 다음 프로퍼티를 공유한다.

  • interceptors: 사용할 인터셉터 목록. HandlerInterceptor는 Section 19.5.4, “HandlerInterceptor 추가”에서 설명했다.
  • defaultHandler: 해당 핸들러 매핑에서 일치하는 핸들러가 없는 경우 사용할 기본 핸들러.
  • order: order 프로퍼티 값에 기반해서 (org.springframework.core.Ordered 인터페이스 참고) 스프링은 컨텍스트에서 사용할 수 있는 모든 핸들러 매핑을 정렬하고 처음 일치하는 핸들러를 적용할 것이다.
  • lazyInitHandlers: 싱글톤 핸들러의 지연 초기화를 허용한다.(프로토타입 핸들러는 항상 지연 초기화를 한다.) 기본값은 false다. 이 프로퍼티는 세가지 구현체에서 직접 구현되었다.

19.5.1 PortletModeHandlerMapping
들어오는 요청을 포틀릿의 현재 모드에 기반해서(예시. ‘view’, ‘edit’, ‘help’) 매핑하는 간단한 핸들러 매핑이다. 예를 들면 다음과 같다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Xml

<bean class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
  <property name="portletModeMap">
    <map>
      <entry key="view" value-ref="viewHandler"/>
      <entry key="edit" value-ref="editHandler"/>
      <entry key="help" value-ref="helpHandler"/>
    </map>
  </property>
</bean>

19.5.2 ParameterHandlerMapping
포틀릿 모드를 변경하지 않고 여러 컨트롤러를 탐색해야 한다면 매핑을 제어하는 키로 요청 파라미터를 사용하는 것이 가장 간단한 방법이다.

ParameterHandlerMapping은 특정 요청 파라미터의 값을 매핑을 제어하는데 사용한다. 파라미터의 기본 이름은 'action'이지만 'parameterName' 프로퍼티를 사용해서 변경할 수 있다.

이 매핑에 대한 빈(bean) 설정은 다음과 같을 것이다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Xml

<bean class="org.springframework.web.portlet.handler.ParameterHandlerMapping”>
  <property name="parameterMap">
    <map>
      <entry key="add" value-ref="addItemHandler"/>
      <entry key="edit" value-ref="editItemHandler"/>
      <entry key="delete" value-ref="deleteItemHandler"/>
    </map>
  </property>
</bean>

19.5.3 PortletModeParameterHandlerMapping
가장 강력한 내장 핸들러 매핑인 PortletModeParameterHandlerMapping는 각 포틀릿 모드내에서 다른 탐색을 허용하는 앞의 두 핸들러 매핑의 기능을 합쳐놓았다.

마찬가지로 기본 파라미터 이름은 "action"이지만 parameterName 프로퍼티를 사용해서 변경할 수 있다.

기본적으로 두가지 다른 포틀릿 모드에서 같은 파라미터값을 사용하지 않는다. 이는 포탈(portal)이 직접 포틀릿 모드를 변경하면 요청이 더이상 매핑에서 유효하지 않도록 하기 위함이다. 이 동작은 allowDupParameters 프로퍼티를 true로 설정해서 변경할 수 있다. 하지만 변경하는 것을 권장하지 않는다.

이 매핑에 대한 빈(bean) 설정은 다음과 같을 것이다.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
Xml

<bean class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
  <property name="portletModeParameterMap">
    <map>
      <entry key="view"> <!-- 'view' 포틀릿 모드 -->
        <map>
          <entry key="add" value-ref="addItemHandler"/>
          <entry key="edit" value-ref="editItemHandler"/>
          <entry key="delete" value-ref="deleteItemHandler"/>
        </map>
      </entry>
      <entry key="edit"> <!-- 'edit' 포틀릿 모드 -->
        <map>
          <entry key="prefs" value-ref="prefsHandler"/>
          <entry key="resetPrefs" value-ref="resetPrefsHandler"/>
        </map>
      </entry>
    </map>
  </property>
</bean>

이 매핑은 PortletModeHandlerMapping보다 먼저 체이닝될 수 있어서 각 모드나 전체적인 기본값을 제공할 수 있다.

19.5.4 HandlerInterceptor 추가
스프링의 핸들러 매핑 메카니즘은 특정 요청에 어떤 기능(예를 들어 중요한 검사 등)을 적용하기를 원할 때 아주 유용할 수 있는 핸들러 인터셉터의 개념을 가진다. 스프링 포틀릿 MVC는 웹 MVC와 같은 방법으로 이러한 개념을 구현한다.

핸들러 매핑에 있는 인터셉터는 org.springframework.web.portlet 패키지의 HandlerInterceptor를 구현해야 한다. 서블릿 버전처럼 이 인터셉터는 세가지 메서드를 정의한다. 한 메서드는 실제 핸들러가 실행되기 전에 호출될 것이고(preHandle) 한 메서드는 핸들러가 실행된 후에 호출할 것이고(postHandle) 한 메서드는 요청이 완료된 후에 호출할 것이다. (afterCompletion) 이 세가지 메서드는 모든 종류의 전처리와 후처리를 하는 유연성을 충분히 제공해야 한다.

preHandle 메서드는 불리언 값을 반환한다. 실행체인의 처리를 멈추거나 계속 진행하는데 이 메서드를 사용할 수 있다. 이 메서드가 true를 반환하면 핸들러 실행 체인을 계속 진행될 것이고 false를 반환하면 DispatcherPortlet는 인터셉터가 직접 요청을 처리한다고(예를 들어 적절한 뷰를 렌더링하는 등) 가정하고 실행 체인의 다른 인터셉터와 실제 핸들러를 계속해서 실행하지 않는다.

RenderRequest에서만 postHandle메서드를 호출한다. preHandle와 afterCompletion 메서드는 ActionRequest와 RenderRequest에서 모두 실행된다. 딱 한가지 종류의 요청에서 이러한 메서드의 로직을 실행해야 한다면 처리하기 전에 요청의 종류를 확인해봐야 한다.

19.5.5 HandlerInterceptorAdapter
서블릿 패키지처럼 포틀릿 패키지는 HandlerInterceptor의 구현체인 HandlerInterceptorAdapter를 가진다. 이 클래스를 상속받아서 필요한 하나나 두개의 메서드를 구현할 수 있도록 이 클래스는 구현부는 없는(empty)는 모든 메서드를 가진다.

19.5.6 ParameterMappingInterceptor
포틀릿 패키지도 ParameterMappingInterceptor라는 인터셉터를 가지므로 직접 ParameterHandlerMapping과 PortletModeParameterHandlerMapping을 함께 사용할 수 있다. 이 인터셉터는 파라미터를 ActionRequest에서 이어진 RenderRequest으로 진행하는 매핑을 제어하는데 사용할 수 있게 한다. 이는 ActionRequest처럼 RenderRequest가 같은 핸들러로 매핑된도록 보장하는 것을 도와줄 것이다. 이는 인터셉터의 preHandle 메서드에서 이뤄지므로 RenderRequest가 매핑될 위치를 변경하기 위해 핸들러에서 파라미터 값을 수정할 수 있다.

이 인터셉터가 ActionResponse에서 setRenderParameter를 호출하고 있음을 유의해라. 즉, 이 인터셉터를 사용할 때는 핸들러에서 sendRedirect를 호출할 수 없다. 외부로 리다이렉트해야 한다면 수동으로 파라미터를 매핑하거나 이를 처리하는 다른 인터셉터를 작성해야 할 것이다.


2016년 4월 1일 금요일

[DEV]MVVM 패턴에 대해서..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

MVVM 패턴은 Model-View-ViewModel패턴의 약자입니다. Knockout.js이 MVVM패턴에 기반을 두고 있기 때문에 본격적으로 만져보기 전에 MVVM을 어느정도 이해할 필요가 있을 것 같아서 이리저리 좀 살펴보았습니다. MVVM은 Microsoft의 John Gossman이 WPF와 Siverlight의 아키텍쳐중 하나로 2005년 자신의 블로그에 공개를 했으며 현재도 WPF와 Siverligh쪽에서 많이 쓰이고 있는 패턴입니다. (Knockout.js도 조만간 살펴보것이긴 하지만 FRENDS 블로그에 공식문서가 번역되어 있습니다.)

MVVM 이란...
앞에서도 얘기했듯이 MVVM은 Model-View-ViewModel을 이야기하고 MVC가 모델, 뷰, 컨트롤러로 이루어져있다면 MVVM은 모델, 뷰, 뷰모델로 이루어진 패턴입니다. 일반적으로 MVC가 가장 많이 쓰이고 잘 알려져 있기 때문에 MVC랑 비교해 볼 필요가 있을듯 합니다. MVVM도 MVC의 변종으로 볼 수 있고 다른 변종으로는 MVP(Model-View-Presenter)도 있습니다.(사실 대부분의 MVVM문서가 WPF와 Silverlight를 기반으로 설명하고 있었기 때문에 패턴만 따로 개념을 잡기가 만만치 않았습니다.)

MVC - 아키텍처의 최상위에 뷰가 있고 그아래 컨트롤러가 있고 그 아래 모델이 있습니다. 때문에 뷰는 컨트롤러만 알고 있고 컨트롤러는 모델을 알고 있습니다. 모델이 변경되었을 때 뷰는 컨트롤러를 통해서 통보를 받습니다.

MVP - MVC에서 컨트롤러가 Presenter로 교체된 형태이고 프리젠터는 뷰와 같은 레벨에 있습니다. 프리젠터는 뷰와 보델의 이벤트를 모두 받으면서 둘 사이의 상호작용을 조정합니다.

MVVM - MVC에서 컨트롤러가 뷰모델로 교체된 형테이고 뷰모델은 UI레이어 아래에 위치합니다. 뷰모델은 뷰가 필요로 하든 데이터와 커맨드 객체를 노출해 주기 때문에 뷰가 필요로하는 데이터와 액션은 담고 있는 컨테이너 객체로 볼 수도 있습니다.



MVVM은 왜 다른가?
MVVM이 MVC와 다른 점은 뷰모델은 뷰를 지원하고 뷰가 필요한 데이터와 커맨드를 제공하기 위해서 만들어졌다는 것입니다. 이름그대로 뷰모델은 뷰를 위한 모델이며 뷰모델을 뷰에 바인딩할 때 가장 강력합니다. 여러가지 뷰를 제공하는 일반적인 객체가 아닌 각 뷰에 맞춰서 만들어진 것입니다. 때문에 뷰는 뷰모델에 대해서만 알고 있으면 되고 그외의 아키텍처에 대해서는 신경쓰지 않아도 됩니다.

그래서 MVC와 가장 다른 점은 커맨드와 데이터바인딩이라고 할 수 있습니다. 이 2가지 요소로 인하여 뷰와 컨트롤러(MVVM에서는 뷰모델)의 관계를 끊을 수 있습니다. 커맨드를 사용함으로써 비헤이비어를 뷰모델에서 정의한 특정한 뷰액션과 연결할 수 있습니다. 데이터바인딩 특정한 뷰 속성과 뷰모델의 속성을 연결할 수 있도록 하고 뷰모델에서 속성이 변경되었을 때 뷰에 반영이 됩니다.

사실 패턴이기 때문에 설명만으로는 감을 잡기가 어려운 부분이 있었습니다.(다른 패턴들도 비슷하다 생각합니다.) knockout.js를 쓰다보면 좀더 감이 올듯 합니다.

참고문서:
5 Minute Overview of MVVM in Silverlight
What is the difference between MVC and MVVM?
Overview of the ModelView ? ViewModel (MVVM) pattern and data-binding
MVVM and jQuery: Designing Maintainable, Fast and Versatile Sites


My Comment..
딱히 내가 쓰는 언어이거나 패턴은 아니지만 가져왔다.. 이유는 MVVM 이라는 패턴 자체도 처음 들어봤지만 비단 이것만 새로 듣는건 아니긴 하지만 그래도!!! 이런 새로운게 있구나 싶어서 약간의 스터디성?? 이라고 해야될까.. 그런 개념에서 가져왔다.. 근데 읽어보니 햄 말처럼 설명만 있고, 내가 직접 사용하는게 아니어서 그런지 감을 잡기가 좀 애매하다.. 

2016년 2월 22일 월요일

[Book] 최범균의 JSP 2.0 프로그래밍 - 기초부터 중급까지..

출처 : Outsider's Dev Story https://blog.outsider.ne.kr/

최범균의 JSP 2.0 프로그래밍 - 10점
최범균 지음/가메출판사

이 책은 작년에 입사하기 전에 Java교육을 받으면서 JSP교재로 사용했던 책이다. 개인적으로 최범균님의 책이 기술적인 내용도 탄탄하면서 설명을 기초부터 잘 해놓아서 초보자들이 입문용으로 사용하기 괜찮다고 생각하고 있다. 한번 보기는 했지만 JSP에 개념도 없을때 교육의 빠른 진도에 눈돌아가면서 봤던거고 거기에 진도일정을 맞추다보니 건너뛴 내용들도 좀 있고 해서 좀더 고급 Java개발을 하기 전에 정확히는 MVC Model2로 넘어가기 전에 기초를 좀 다지고 가자는 차원에서 다시 봤다.

한번 본 내용이고 상당부분은 익히(?) 알고 있는 내용들인지라 좀 빠르게 봤다. 원래 책은 아는만큼 보인다고 요기 이런것들이 있었나? 하는 것들도 있었고 JSP를 어느정도 하고 난뒤에 보니까 확실히 기초부터 탄탄히 얘기하고 있다는 생각이 든다. 물론 처음 이책을 볼때는 뭔소린지.. 어떻게 하라는 건지 엄청 해매기는 했지만 그리 어렵지 않게 간단한 JSP부터 구동방식과 각 객체들에 대해서 자세히 설명하고 좀더 쉽고 좋게 웹개발을 하기 위한 필요한 모든 JSP 기술에 대해서 제목대로 초중급용에 적당한 난이도의 정보와 예제를 잘 제공하고 있다.

뒷부분에서는 갈수록 지양하고 있는 JSP 스크립틀릿 방식을 벗어난 표현언어나 JSTL을 이용해서 어떻게 사용할 수 있는지와 요즘 대세가 되어버린 MVC 모델2에 대해서 설명함으로써 자연스럽게 중급기술에 다가갈 수 있도록 유도하고 있다. 더불어 틈틈히 유행기술이나 생각없이 남들 다하는대로만 하지 않고 창의적인 생각을 할 수 있도록 노력하고 있다.

나로써는 제대로 개념이 안잡히거나 애매했던 부분에 대해서 한번 좀 깔끔히 정리된 느낌이고 JSTL을 어떻게 사용하는 건지 알게 된것과 서블릿의 필터라는 유용한 기능을 알게 된것만으로도 충분히 읽은 보람이 있다. 물론 안좋은걸 해바야 좋은걸 체감할 수 있는거지만 JSTL을 본 순간 난 왜 여태 JSP 스크립틀릿 노가다를 해왔던 것인지 한탄할 수 밖에 없었다... ㅠ..ㅠ 다음 프로젝트때 적용해 보면 확실히 감잡을 수 있겠다... ㅎㄷㄷㄷㄷㄷ

My Comment..
흠.. 이 책은 나도 추억에 사로잡히게 만드는 책인 듯..
햄과 같이 학원에서 배운.. 현존하는 동기들이라고 하는 형, 친구, 동생들을..
알게 해준 곳이기도 하공.. 무튼 머 그곳에서 배운것인데..

그 때는 개포자였기 때문에.. 걍 그런가보다 하고.. 따라서 치기 바뻤다..
[물론, 따라 치는데도 못 칠정도로 기본의 기자도 모른 시절이긴 했다..]
내가 형처럼 전반적으로 다 보기 위해서 책을 꺼내들었던 건 아니고, 잠시 참고할것이..
있어서 책을 다시금 본적이 있었는데.. 확실히 그 때는.. 눈에 들어오더라..

진짜 아는만큼 보이는 듯.. 아는게 힘.. But.. 모른다고 부끄러워하지 말고..
모르는 것을 찾아보려는.. 알려고 하는.. 노력을 해야 될 듯하다..