반응형
틀이 무엇이며 틀을 기반으로 한 MVC 웹 어플리케이션을 개발할 수 있도록 간단한 예제를 살펴본다.
틀 프레임워크란?
틀 프레임워크(TLE Framework)는 스트러츠(Struts)나 코쿤(Cocoon)과 같이 MVC(Model-View-Controller) 패턴을 구현한 프레임워크로서, MVC 패턴에 따라 웹 어플리케이션을 구현할 수 있도록 지원해준다. 다른 프레임워크와 달리 틀 프레임워크는 웹 어플리케이션을 구축하는 데 필요한 요소들을 컴포넌트로 분리시켰다. 웹 어플리케이션을 구축하려면 클라이언트의 요청이 무엇인지 파악하고, 요청에 알맞은 로직을 수행하고, 로직 수행 결과를 보여주는 세 가지 요소가 필요한데, 이 세가지 요소를 별도의 컴포넌트로 분리시켜놓았다.
틀 프레임워크의 구성
아래 그림은 틀의 구성 요소들을 보여주고 있는데, 이 그림에서 앞서 말한 요청 분석, 요청 처리, 결과 생성의 세가지 요소를 볼 수 있으며, 또한 웹 어플리케이션을 사용하는 사용자의 인증과 권한 처리를 위한 요소가 있는 것도 확인할 수 있다.
위 그림에서 각 구성 요소의 기능은 아래와 같다.
위 표에서 알 수 있듯이, '요청분석기', '요청처리기', 'UI생성기', 그리고 '권한/인증관리자'는 틀 프레임워크의 핵심 컴포넌트인데, 틀은 이 네가지 컴포넌트를 위한 인터페이스를 제공하고 있다. 또한, 기본적으로 이 네가지 컴포넌트의 인터페이스를 구현한 기본 구현체를 제공하고 있다. 아래 그림은 인터페이스와 기본 구현체 사이의 관계를 보여주고 있다.
Component 인터페이스는 틀 프레임워크가 관리하게 될 컴포넌트의 인터페이스이며, ComponentBase 추상 클래스는 이 Component 인터페이스를 구현한 클래스로서 컴포넌트가 구현해야 할 기본 기능을 제공한다.
요청분석기의 기본 구현체는 DefaultRequestAnalyzer 이고, 요청처리기의 기본 구현체는 DefaultRequestProcessor 이고, UI생성기의 기본 구현체는 DefaultUICreator이다. 권한/인증 관리자의 경우는 기본 구현체가 두개가 존재한다. 이 두 컴포넌트 구현체는 아래와 같다.
틀 프레임워크의 요청 처리 순서
틀 프레임워크는 네 개의 컴포넌트(요청분석기, 요청처리기, UI생성기, 권한/인증관리자)를 사용해서 클라이언트의 요청을 처리한다. 요청이 들어왔을 때 틀 프레임워크는 아래의 그림처럼 네 개의 컴포넌트를 순서대로 사용한다. 이 실행 순서를 머리속에 넣고 있으면 보다 쉽게 틀 프레임워크를 사용할 수 있을 것이다.
위 그림을 보면 틀 프레임워크가 Model-View-Controller 패턴에 기반하고 있음을 알 수 있다. 즉, 틀 컨트롤러는 클라이언트의 요청과 모델 사이의 가교 역할을 하는 Controller 역할을 하고, 요청 분석기-권한/인증 관리자-요청처리기는 클라이언트의 요청에 따라 알맞은 로직을 수행하는 Model 역할을 하며, UI생성기는 Model로부터 데이터(Result)를 받아 클라이언트에 보여줄 화면을 생성하는 View 역할을 한다.
명령어 ID
틀 프레임워크를 사용할 때 반드시 이해해야 할 요소가 '명령어 ID(command ID)'이다. 명령어 ID는 클라이언트가 어떤 기능을 요청했는 지 분간할 때 사용되는 식별자로서 각 컴포넌트는 이 명령어 ID를 사용해서 알맞은 기능을 수행하게 된다.
앞서 틀 프레임워크의 요청 처리 순서에서 살펴봤듯이 클라이언트의 요청을 최초로 전달받는 컴포넌트는 요청 분석기이다. 요청 분석기는 클라이언트의 요청을 전달받으면, 클라이언트의 요청을 분석해서 알맞은 명령어 ID를 생성해낸다. 요청 분석기가 생성한 명령어 ID는 이후에 요청 처리기, UI 생성기, 권한/인증 관리자 컴포넌트에서 클라이언트 요청을 식별하는 데 사용된다.
예를 들어, 요청 처리기는 명령어 ID를 사용해서 클라이언트가 요청한 기능을 수행하고, 권한/인증 관리자는 명령어 ID를 통해서 현재 사용자가 요청한 기능을 실행할 권한이 있는 지 검사하게 된다. 또한 UI 생성기는 명령어 ID를 사용해서 클라이언트가 요청한 기능에 알맞은 뷰를 생성하게 된다.
틀 프레임워크를 이용한 MVC 프로그래밍 맛보기
앞서 봤듯이 틀 프레임워크는 MVC 패턴에 기반하여 웹 어플리케이션을 개발할 수 있도록 도와주는데, 본 장에서는 간단하게 틀을 이용해서 MVC 프로그래밍을 하는 방법을 살펴볼 것이다. 더불어, 틀 프레임워크가 제공하는 기본 컴포넌트인 DefaultRequestAnalyzer, DefaultRequestProcessor, DefaultUICreator와 틀 프레임워크 자체의 설정 방법 및 사용방법을 살펴보도록 하겠다.
틀 프레임워크 다운로드 및 설치
틀 프레임워크의 현 버전은 2.0.5.6로서 틀 프레임워크의 프로젝트 사이트인 http://kldp.net/projects/tle에서 다운로드 받을 수 있다. 이 사이트에서 TLEFramework-2.0.5.6.zip 파일을 다운로드 받은 후 압축을 풀면 아래와 같은 폴더가 생성된다.
각 폴더에는 다음과 같은 파일들이 존재한다.
틀 프레임워크를 사용하여 웹 어플리케이션을 개발하기 위해서는 먼저 틀 프레임워크를 설치해야 한다. 틀 프레임워크의 설치 순서는 다음과 같다.
프레임워크 기본 설정하기
틀 프레임워크를 실행하는데 필요한 jar 파일들을 웹 어플리케이션의 WEB-INF/lib 폴더에 복사했다면, 이제 남은 일은 틀 프레임워크를 실행하기 위해 필요한 정보들을 설정하는 것이다. 틀 프레임워크의 설정은 크게 틀 프레임워크 자체에 대한 설정과 각각의 컴포넌트에 대한 설정 이렇게 두 가지 부분으로 나뉜다.
먼저 틀 프레임워크 자체에 대한 설정 부분부터 살펴보도록 하겠다.
web.xml 파일에 틀 프레임워크 관련 설정하기
틀 프레임워크를 사용하려면 먼저 어떤 파일을 틀 프레임워크의 설정 파일로 사용할지의 여부를 web.xml 파일을 통해서 지정해주어야 한다. 아래는 web.xml 파일에 틀과 관련된 설정 내용을 추가한 예를 보여주고 있다.
TLEWebapplicationListener 클래스는 웹 어플리케이션이 시작될 때 틀 프레임워크의 초기화작업을 진행하는 리스너로서 콘텍스트 파라미터인 tle.configFile의 값을 틀 설정 파일로 사용한다. 위 예제에서 tle.configFile 콘텍스트 파라미터의 값은 다음과 같다.
여기서 {context.path}는 웹 어플리케이션이 위치한 폴더의 경로로 대체된다. 예를 들어, 웹 어플리케이션 폴더가 c: omcatwebappsROOT 인 경우 위의 값은 실제로 c: omcatwebappsROOTWEB-INFconfigTLEFrameworkConfig.xml이 된다.
tle.framework.RequestReceiver는 클라이언트의 요청을 받아서 틀 컨트롤러에 전달하는 역할을 하는데(이미 앞에서 RequestReceiver에 대해 간단하게 설명한 바 있다), 위 코드에서는 *.do로 들어오는 요청을 받아서 틀 컨트롤러에 전달하도록 설정하였다.
틀 프레임워크 설정 파일 작성하기
web.xml 파일을 설정한 뒤에는 틀 프레임워크의 설정 파일을 작성해주면 된다. 틀 프레임워크의 설정 파일에는 다음의 두 가지 정보가 저장된다.
설정 파일의 예를 들면 아래와 같다.
먼저, 틀 프레임워크에 장착될 컴포넌트는 <component-list> 태그에서 명시한다. component-list 태그는 component 태그를 갖는데, component 태그의 각 태그는 다음과 같은 의미를 갖는다.
각 컴포넌트는 필요한 설정 정보를 초기화 파라미터를 사용해서 입력받을 수 있다.
컴포넌트의 설정을 완료했으면, 그 다음으로 설정할 내용은 네 개의 핵심 컴포넌트를 지정하는 것이다. 위 설정 파일에서 핵심 컴포넌트를 지정하는 부분은 다음과 같다.
위 코드에서 각 태그는 다음과 같은 의미를 갖는다.
네 개의 태그에서 사용할 아이디값은 앞서 컴포넌트를 정의할 때 component-id 태그로 지정한 값을 지정해주면 된다.
기본 컴포넌트의 주요 기능 및 설정
DefaultRequestAnalyzer의 주요기능
DefaultRequestAnalyzer 컴포넌트는 요청 분석기로서 클라이언트의 요청을 분석하게 된다. DefaultRequestAnalyzer 컴포넌트가 제공하는 주요 기능은 다음과 같다.
이때, 요청 URI인 /tle/admin/front.view 중에서 콘텍스트 경로인 /tle 을 제외한 나머지 부분인 /admin/front.view 가 명령어 ID로 사용된다.
파일 업로드시에는 인코딩을 multipart/form-data 타입으로 하게 되는데, DefaultRequestAnalyzer 컴포넌트는 이 인코딩 타입으로 전송된 데이터를 처리하기 위해서 Jakarta Commons FileUpload API를 사용한다. 틀 프레임워크의 배포판에는 Jakarta Commons FileUpload API와 관련된 jar 파일인 commons-fileupload-1.0.jar 파일이 포함되어 있으므로, commons-fileupload-1.0.jar 파일을 웹 어플리케이션의 WEB-INF/lib 폴더에 복사해야 DefaultRequestAnalyzer 컴포넌트가 올바르게 동작한다.
DefaultRequestAnalyzer 설정하기
앞서 틀 프레임워크 설정 파일에서 DefaultRequestAnalyzer 컴포넌트를 설정한 부분을 살펴보도록 하자.
위 코드에서 볼 수 있듯이, DefaultRequestAnalyzer 컴포넌트는 configFile 초기화 파라미터를 사용해서 사용할 설정 파일의 경로를 입력받는다. 파라미터 값에 있는 {context.path}는 틀 프레임워크 설정 파일에서와 마찬가지로 콘텍스트의 경로를 의미한다.
DefaultRequestAnalyzer가 사용하는 설정 파일은 자바의 프로퍼티 파일로서 아래와 같이 작성된다.
위 프로퍼티 파일에서 각 프로퍼티는 아래와 같다.
DefaultRequestProcessor 주요 기능
DefaultRequestProcessor 컴포넌트는 클라이언트가 요청을 처리하는 요청 처리기이다. DefaultRequestProcessor는 요청 분석기가 생성한 명령어 ID를 사용해서 알맞은 기능을 수행한다. DefaultRequestProcessor 컴포넌트는 아래 그림과 같이 명령어 ID에 알맞은 핸들러(handler)를 사용해서 클라이언트의 요청을 처리한다.
DefaultRequestProcessor 컴포넌트는 명령어 ID를 전달받으면, 명령어 ID에 해당하는 핸들러를 실행한다. 핸들러는 클라이언트가 요청한 기능을 수행한 뒤 결과를 담아서 요청 처리기에 전달하며, 요청 처리기는 다시 그 결과를 틀 컨트롤러에 전달하게 된다.
(일반적으로 하나의 핸들러가 하나의 명령어 ID를 처리하게 되는데(물론, 하나의 핸들러가 다수의 명령어 ID를 처리하기도 한다), 여기서 DefaultRequestProcessor 컴포넌트가 커맨드 패턴을 사용하고 있다는 것을 알 수 있다. 커맨드 패턴에 대한 글은 자바캔의 '커맨드(Command) 패턴과 그 구현' 글을 참고하기 바란다.)
DefaultRequestProcessor 설정하기
DefaultRequestProcessor 컴포넌트를 사용하기 위해서는 먼저 틀 프레임워크 설정 파일에 다음과 같이 DefaultRequestProcessor 컴포넌트를 사용한다고 명시해주어야 한다.
DefaultRequestAnalyzer 컴포넌트와 마찬가지로 DefaultRequestProcessor 컴포넌트도 configFile 초기화 파라미터를 사용해서 설정 파일의 경로를 전달받는다. DefaultRequestProcessor 컴포넌트의 설정 파일은 다음의 두 가지 정보를 담는다.
클라이언트의 요청을 실제로 처리하게 되는 핸들러의 목록을 지정하는 태그는 handler-list/handler로서 handler 태그는 다음과 같은 속성을 갖고 있습니다.
핸들러는 크게 클래스와 JSP의 두가지 방식으로 구현할 수 있는데 각각의 구현방법에 대해서는 뒤에서 설명하도록 하겠다.
핸들러 목록을 지정했다면 다음으로 할 일은 클라이언트의 요청을 나타내는 명령어 ID와 핸들러를 매핑시키는 작업이다. 이는 command-handler-mapping/mapping 태그를 통해서 지정할 수 있으며, mapping 태그의 각 속성은 아래와 같은 의미를 갖는다.
예를 들어, 아래의 코드는 /hello/greeting.do 라는 요청이 들어오면 greeting2 라는 핸들러를 실행한다는 의미를 갖는다.
만약 아이디가 greeting2인 핸들러가 /handler/hello_handler.jsp 라면, /hello/greeting.do 요청이 들어오면 /handler/hello_handler.jsp가 실행된다.
DefaultRequestProcessor 핸들러 구현하기
DefaultRequestProcessor 컴포넌트의 핸들러는 클라이언트가 요청한 로직을 수행한 뒤 그 결과를 UI 생성기에 전달해주는 기능을 담당한다. 즉, MVC 패턴에 컨트롤러의 역할을 담당하는 것이 바로 핸들러인 것이다. 핸들러는 모델을 사용해서 클라이언트가 요구한 로직을 수행하게 되며, 그 결과를 뷰에 전달하게 된다.
DefaultRequestProcessor 컴포넌트는 두 가지 방식으로 핸들러를 구현할 수 있도록 해 준다. 한가지는 스트러츠의 Action과 같이 자바 클래스로 핸들러를 구현하는 방식이며, 다른 한가지는 JSP로 핸들러를 구현하는 방식이다. 개발자에 따라 원하는 방식으로 핸들러를 구현하면 되는데, 필자의 경우는 JSP 방식을 선호한다.
두 가지 방식으로 핸들러를 구현하는 방법에 대해서 살펴보고, 어떤 방식이 본인이 사용하기에 편리할지는 직접 판단해보기 바란다.
DefaultRequestProcessor의 클래스 핸들러 작성하기
먼저, 클래스를 사용해서 핸들러를 작성해보도록 하겠다. 핸들러 클래스를 작성하기 위해서는 아래의 규칙을 따라야 한다.
코드: /WEB-INF/src/test/tle/command/GreetingCommandHandler.java
package test.tle.command;
import tle.framework.component.impl.rp.DefaultResult;
import tle.framework.component.impl.rp.handler.CommandHandler;
import tle.framework.component.spi.ra.RequestInfo;
/**
* Result의 "greeting" 데이터에 "안녕하세요"라는 데이터를 추가한다.
*
* @author 최범균
*/
public class GreetingCommandHandler implements CommandHandler {
public DefaultResult process(RequestInfo requestInfo, DefaultResult result) {
result.setData("greeting", "안녕하세요."); // 데이터 저장
result.setCode("success"); // 요청 처리 결과 코드 입력
return result;
}
}
위 코드에서 GreetingCommandHandler는 클라이언트의 요청을 전달받으면, "greeting" 데이터의 값을 "안녕하세요."로 지정한 뒤 결과 코드값을 "success"로 지정한다. 이 코드값은 클라이언트의 요청을 처리한 결과를 의미하며, 알맞은 값을 입력할 수 있다. 예를 들어, 다음과 같이 상황에 따라 서로 다른 코드값을 지정할 수 있다.
핸들러는 상황에 알맞은 코드값을 지정하며, Result를 전달받게 될 UI 생성기 컴포넌트는 이 결과 코드값을 사용하여 알맞은 화면을 생성하게 된다.
DefaultRequestProcessor의 JSP 핸들러 작성하기
앞서 클래스를 사용해서 핸들러를 작성하는 방법을 살펴봤는데, 두번째로 JSP를 이용한 핸들러 클래스의 작성 방법에 대해서 살펴보도록 하자.
JSP 핸들러는 아래와 같은 코드를 사용해서 작성한다.
JSP를 사용하여 작성한 핸들러의 코드를 보면 클래스를 사용하여 작성한 핸들러 코드와 크게 다르지 않다는 것을 알 수 있다. JSP 핸들러와 클래스 핸들러 사이의 차이점이라면 다음의 두가지 정도 뿐이다.
TLEFramework 2.0.5.5 버전까지는 JSP 핸들러에 실제 response에 해당하는 객체를 전달해서 JSP 핸들러가 지정한 컨텐츠 타입이나 출력한 내용 때문에 실제 UICreator가 내용을 생성할 때 글자가 깨지는 경우가 발생했다. 그래서 TLEFramework 2.0.5.5 및 그 이하 버전에서는, 이런 문제가 발생하는 콘테이너의 경우 JSP 핸들러의 마지막에 out.clearBuffer() 메소드를 실행시켜서 문제를 해결할 수 있었다.
TLEFramework 2.0.5.6 버전부터는 JSP 핸들러에 실제 response에 해당하는 객체가 아닌 HttpServletResponseWrapper 객체를 새롭게 생성해서 전달한 뒤 JSP 핸들러가 생성한 내용이나 지정한 컨텐츠 타입을 무시하기 때문에, 더 이상 이런 문제가 발생하지 않는다.
DefaultUICreator의 주요기능
앞서 살펴본 내용은 클라이언트가 요청한 기능을 처리하는 방법에 대한 것이었다. 핸들러는 클라이언트의 요청을 처리한 뒤 결과로 보여줄 내용을 Result에 저장한다. 틀 컨트롤러는 요청 처리기 컴포넌트가 생성한 Result를 UI 생성기에 전달해서 알맞은 UI를 생성하도록 한다. 틀 프레임워크가 기본적으로 제공하는 DefaultUICreator 컴포넌트는 JSP를 사용하여 뷰를 생성하며 다음과 같은 기능을 제공한다.
JSP 템플릿 기능을 레이아웃 코드를 중복해서 작성하지 않고 템플릿으로 만들어 UI를 조립하듯이 사용할 수 있도록 해준다.
뷰셋, 템플릿, 뷰의 상속 등 DefaultUICreator 컴포넌트가 제공하는 기능에 대해서는 본 시리즈를 진행하면서 살펴보게 될 것이다.
DefaultUICreator 설정하기
DefaultUICreator 컴포넌트를 사용하기 위해서는 먼저 틀 프레임워크 설정 파일에 다음과 같이 DefaultUICreator 컴포넌트를 사용한다고 명시해주어야 한다.
DefaultRequestProcessor 컴포넌트와 마찬가지로 DefaultUICreator 컴포넌트도 configFile 초기화 파라미터를 사용해서 UI와 관련된 설정 정보가 담긴 파일의 경로를 입력받는다.
DefaultUICreator 컴포넌트의 설정 파일은 다음과 같은 정보를 담는다.
위 코드에서 핵심 태그는 view-set 태그와 command-view-mapping 태그이다. 먼저 view-set 태그에는 view 태그가 존재하는데 뷰 태그는 아래표와 같은 내용을 설정한다.
command-view-mapping 태그는 요청 처리기의 결과를 어떤 뷰를 통해서 보여줄지를 지정하기 위해 사용된다. command-view-mapping 태그는 자식 태그로 command 태그를 갖고 있으며, command 태그는 다음과 같은 코드 형태를 띈다.
위 코드는 다음을 의미한다.
명령어 ID가 /greeting.do이고, 요청 처리기가 이 명령어를 처리한 결과 코드값이 "success"이면, "greeting"뷰인 /view/greeting.jsp를 이용해서 결과를 보여준다는 것을 의미한다.
뷰셋 설정에 따라 보여지는 화면이 달라질 수 있는데, 이에 대해서는 틀 프레임워크의 시리즈를 진행하면서 살펴보기로 하자.
DefaultUICreator 뷰 JSP 작성하기
간단하게 뷰 기능을 수행하는 JSP를 작성해보자. 본 글에서 사용하는 예제는 아래와 같이 3개의 뷰를 정의하고 있다.
"error" 뷰는 view-set 태그의 error-view 속성을 통해서 에러 뷰로 지정됐는데, 에러 뷰에 대해서는 나중에 살펴보기로 하고, 본 글에서는 나머지 두 개의 뷰의 코드를 살펴보도록 하겠다. 먼저 "greeting" 뷰에 해당하는 /view/greeting.jsp 코드를 보도록 하자.
위 코드는 DefaultUICreator 컴포넌트와 관련된 뷰 JSP 페이지의 가장 기본적인 코드 형태를 보여주고 있다. 먼저 뷰 JSP는 결과를 참조하기 위해 request의 "result" 속성으로부터 Result 객체를 구한다. 그런 뒤, Result.getData() 메소드를 사용해서 요청 처리기가 생성한 데이터를 읽어와 그 데이터를 알맞게 사용해서 화면을 생성하게 된다.
예제 실행하기
지금까지 살펴본 예제의 전체 코드는 하단의 다운로드 링크를 통해 구할 수 있다. 예제 압축 파일인 tletest.war 파일을 [톰캣]/webapps와 같이 웹 어플리케이을 자동으로 인식할 수 있는 폴더에 복사하면 자동으로 배포된다. 자동 배포가 아니더라도 war 파일의 압축을 푼뒤 어플리케이션 서버의 설정을 알맞게 지정해줘도 된다.
tletest.war를 알맞게 배포한 뒤 어플리케이션 서버를 실행해보자. 톰캣의 /webapps 폴더에 배포했다면 아래의 URL을 사용해서 결과를 확인할 수 있을 것이다.
두 URL을 실행하면 아래와 같이 결과가 출력될 것이다.
예제 실행 흐름
http://localhost:8080/tletest/greeting.do 요청이 왔을 때 틀 프레임워크의 순차적인 처리 순서는 아래 그림과 같다.
위 실행 순서를 모르더라도 틀을 사용하는데에 지장은 없다. 하지만, 위의 실행 순서는 틀 프레임워크나 스트러츠 등 MVC 패턴에 기반한 프레임워크들은 대부분 위와 비슷한 순서로 작업을 진행하므로, 위 순서를 이해해두면 프레임워크를 사용하는데 많은 도움이 될 것이다.
다음 글에서는
본 글에서는 간단하게 틀 프레임워크의 기본적인 사용방법을 살펴보았다. 어렴풋이 틀의 설정 방법이나 사용방법, 그리고 틀이 어떤 식으로 돌아가는지에 대한 내용을 이해했으리라 본다. 다음 글에서는 클라이언트의 요청을 처리할때 사용되는 DefaultRequestProcessor의 핸들러의 개발 방법에 대해서 자세하게 살펴보도록 하겠다.
관련링크:
틀 프레임워크란?
틀 프레임워크(TLE Framework)는 스트러츠(Struts)나 코쿤(Cocoon)과 같이 MVC(Model-View-Controller) 패턴을 구현한 프레임워크로서, MVC 패턴에 따라 웹 어플리케이션을 구현할 수 있도록 지원해준다. 다른 프레임워크와 달리 틀 프레임워크는 웹 어플리케이션을 구축하는 데 필요한 요소들을 컴포넌트로 분리시켰다. 웹 어플리케이션을 구축하려면 클라이언트의 요청이 무엇인지 파악하고, 요청에 알맞은 로직을 수행하고, 로직 수행 결과를 보여주는 세 가지 요소가 필요한데, 이 세가지 요소를 별도의 컴포넌트로 분리시켜놓았다.
틀 프레임워크의 구성
아래 그림은 틀의 구성 요소들을 보여주고 있는데, 이 그림에서 앞서 말한 요청 분석, 요청 처리, 결과 생성의 세가지 요소를 볼 수 있으며, 또한 웹 어플리케이션을 사용하는 사용자의 인증과 권한 처리를 위한 요소가 있는 것도 확인할 수 있다.
위 그림에서 각 구성 요소의 기능은 아래와 같다.
틀 컴포넌트의 구성요소 | |
구성요소 | 설명 |
RequestReceiver | 서블릿으로서 클라이언트의 요청을 전달받아 틀 프레임워크의 컨트롤러에 전달한다. |
틀 컨트롤러 TLEController |
RequestReceiver로부터 전달받은 클라이언트 요청을 각 컴포넌트를 사용하여 알맞게 처리한다. 요청분석->요청처리->결과출력의 일련의 과정을 각 컴포넌트를 사용하여 알맞게 제어하는 역할을 수행한다. |
요청분석기 RequestAnalyzer |
클라이언트 요청을 분석하여 그 정보를 RequestInfo 인스턴스에 담는다. RequestInfo에는 파라미터 값, 쿠키값, 업로드한 파일, 헤더 등 요청과 관련된 정보가 저장된다. 또한, 요청 처리기는 클라이언트의 요청으로부터 알맞은 명령어ID를 생성해서 RequestInfo에 담는다. |
요청처리기 RequestProcessor |
요청분석기를 통해 생성된 RequestInfo를 사용해서 클라이언트가 요청한 기능을 수행한다. RequestInfo에 저장된 명령어ID를 사용해서 어떤 기능을 요청했는 지 파악한다. 요청 처리기는 로직을 수행한 결과를 Result에 저장한다. |
UI생성기 UICreator |
요청처리기가 생성한 Result를 사용하여 알맞은 결과를 출력한다. |
권한/인증 관리자 AAManager |
클라이언트가 요청한 기능을 사용할 수 있는지의 여부를 확인한다. 요청분석기가 생성한 RequestInfo 객체를 사용해서 클라이언트가 요청한 기능을 파악하며, 틀 컨트롤러에 기능을 사용할 수 있는지의 여부를 알려주게 된다. |
컴포넌트 관리자 ComponentManager |
네 개의 주요 컴포넌트를 비롯한 틀 프레임워크와 관련된 컴포넌트를 관리한다. |
위 표에서 알 수 있듯이, '요청분석기', '요청처리기', 'UI생성기', 그리고 '권한/인증관리자'는 틀 프레임워크의 핵심 컴포넌트인데, 틀은 이 네가지 컴포넌트를 위한 인터페이스를 제공하고 있다. 또한, 기본적으로 이 네가지 컴포넌트의 인터페이스를 구현한 기본 구현체를 제공하고 있다. 아래 그림은 인터페이스와 기본 구현체 사이의 관계를 보여주고 있다.
Component 인터페이스는 틀 프레임워크가 관리하게 될 컴포넌트의 인터페이스이며, ComponentBase 추상 클래스는 이 Component 인터페이스를 구현한 클래스로서 컴포넌트가 구현해야 할 기본 기능을 제공한다.
요청분석기의 기본 구현체는 DefaultRequestAnalyzer 이고, 요청처리기의 기본 구현체는 DefaultRequestProcessor 이고, UI생성기의 기본 구현체는 DefaultUICreator이다. 권한/인증 관리자의 경우는 기본 구현체가 두개가 존재한다. 이 두 컴포넌트 구현체는 아래와 같다.
- NullAAManager - 권한/인증 검사를 하지 않는다. 개발자가 직접 권한/인증 부분 로직을 작성해야 한다.
- DefaultAAManager - 권한/인증 검사를 위한 인터페이스를 제공한다. ServiceManager, RoleManager, SessionManager 등의 인터페이스를 사용해서 권한/인증 기능을 수행하며, 개발자는 이들 인터페이스를 구현한 클래스를 제공해야 한다.
틀 프레임워크의 요청 처리 순서
틀 프레임워크는 네 개의 컴포넌트(요청분석기, 요청처리기, UI생성기, 권한/인증관리자)를 사용해서 클라이언트의 요청을 처리한다. 요청이 들어왔을 때 틀 프레임워크는 아래의 그림처럼 네 개의 컴포넌트를 순서대로 사용한다. 이 실행 순서를 머리속에 넣고 있으면 보다 쉽게 틀 프레임워크를 사용할 수 있을 것이다.
위 그림을 보면 틀 프레임워크가 Model-View-Controller 패턴에 기반하고 있음을 알 수 있다. 즉, 틀 컨트롤러는 클라이언트의 요청과 모델 사이의 가교 역할을 하는 Controller 역할을 하고, 요청 분석기-권한/인증 관리자-요청처리기는 클라이언트의 요청에 따라 알맞은 로직을 수행하는 Model 역할을 하며, UI생성기는 Model로부터 데이터(Result)를 받아 클라이언트에 보여줄 화면을 생성하는 View 역할을 한다.
명령어 ID
틀 프레임워크를 사용할 때 반드시 이해해야 할 요소가 '명령어 ID(command ID)'이다. 명령어 ID는 클라이언트가 어떤 기능을 요청했는 지 분간할 때 사용되는 식별자로서 각 컴포넌트는 이 명령어 ID를 사용해서 알맞은 기능을 수행하게 된다.
앞서 틀 프레임워크의 요청 처리 순서에서 살펴봤듯이 클라이언트의 요청을 최초로 전달받는 컴포넌트는 요청 분석기이다. 요청 분석기는 클라이언트의 요청을 전달받으면, 클라이언트의 요청을 분석해서 알맞은 명령어 ID를 생성해낸다. 요청 분석기가 생성한 명령어 ID는 이후에 요청 처리기, UI 생성기, 권한/인증 관리자 컴포넌트에서 클라이언트 요청을 식별하는 데 사용된다.
예를 들어, 요청 처리기는 명령어 ID를 사용해서 클라이언트가 요청한 기능을 수행하고, 권한/인증 관리자는 명령어 ID를 통해서 현재 사용자가 요청한 기능을 실행할 권한이 있는 지 검사하게 된다. 또한 UI 생성기는 명령어 ID를 사용해서 클라이언트가 요청한 기능에 알맞은 뷰를 생성하게 된다.
틀 프레임워크를 이용한 MVC 프로그래밍 맛보기
앞서 봤듯이 틀 프레임워크는 MVC 패턴에 기반하여 웹 어플리케이션을 개발할 수 있도록 도와주는데, 본 장에서는 간단하게 틀을 이용해서 MVC 프로그래밍을 하는 방법을 살펴볼 것이다. 더불어, 틀 프레임워크가 제공하는 기본 컴포넌트인 DefaultRequestAnalyzer, DefaultRequestProcessor, DefaultUICreator와 틀 프레임워크 자체의 설정 방법 및 사용방법을 살펴보도록 하겠다.
틀 프레임워크 다운로드 및 설치
틀 프레임워크의 현 버전은 2.0.5.6로서 틀 프레임워크의 프로젝트 사이트인 http://kldp.net/projects/tle에서 다운로드 받을 수 있다. 이 사이트에서 TLEFramework-2.0.5.6.zip 파일을 다운로드 받은 후 압축을 풀면 아래와 같은 폴더가 생성된다.
각 폴더에는 다음과 같은 파일들이 존재한다.
- / : 틀 프레임워크 jar 파일(TLEFramework-2.0.5.6.jar), readme.txt, chage.txt, src.zip
- /config : 틀 프레임워크 및 기본 제공 컴포넌트의 설정 파일 예제
- /doc/api : 틀 프레임워크 API 문서
- /lib : 틀 프레임워크를 실행하는 데 필요한 jar 파일 포함
- /license : 틀 프레임워크 및 관련 jar 파일의 라이센스 문서
- /example : 예제 war 파일 (tle.war)
틀 프레임워크를 사용하여 웹 어플리케이션을 개발하기 위해서는 먼저 틀 프레임워크를 설치해야 한다. 틀 프레임워크의 설치 순서는 다음과 같다.
- 웹 어플리케이션 폴더를 생성한다.
- WEB-INF/lib 폴더에 다음의 jar 파일들을 복사한다.:
- commons-digester.jar commons-logging.jar commons-collections-3.1.jar
- commons-beanutils.jar commons-fileupload-1.0.jar
- jakarta-regexp-1.3.jar
- mx4j.jar mx4j-remote.jar mx4j-tools.jar
- TLEFramework-2.0.5.6.jar
- TLEFramework 설정 파일을 작성한다.
- 각 컴포넌트의 설정 파일을 알맞게 작성한다.
- web.xml 파일에 틀 프레임워크 관련 설정을 추가한다.
프레임워크 기본 설정하기
틀 프레임워크를 실행하는데 필요한 jar 파일들을 웹 어플리케이션의 WEB-INF/lib 폴더에 복사했다면, 이제 남은 일은 틀 프레임워크를 실행하기 위해 필요한 정보들을 설정하는 것이다. 틀 프레임워크의 설정은 크게 틀 프레임워크 자체에 대한 설정과 각각의 컴포넌트에 대한 설정 이렇게 두 가지 부분으로 나뉜다.
먼저 틀 프레임워크 자체에 대한 설정 부분부터 살펴보도록 하겠다.
web.xml 파일에 틀 프레임워크 관련 설정하기
틀 프레임워크를 사용하려면 먼저 어떤 파일을 틀 프레임워크의 설정 파일로 사용할지의 여부를 web.xml 파일을 통해서 지정해주어야 한다. 아래는 web.xml 파일에 틀과 관련된 설정 내용을 추가한 예를 보여주고 있다.
코드: /WEB-INF/web.xml
<?xml version="1.0" encoding="euc-kr" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<context-param>
<param-name>tle.configFile</param-name>
<param-value>{context.path}WEB-INFconfigTLEFrameworkConfig.xml</param-value>
</context-param>
<listener>
<listener-class>tle.framework.TLEWebapplicationListener</listener-class> </listener>
<servlet>
<servlet-name>TLERequestReceiver</servlet-name>
<servlet-class>tle.framework.RequestReceiver</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TLERequestReceiver</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
<?xml version="1.0" encoding="euc-kr" ?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<context-param>
<param-name>tle.configFile</param-name>
<param-value>{context.path}WEB-INFconfigTLEFrameworkConfig.xml</param-value>
</context-param>
<listener>
<listener-class>tle.framework.TLEWebapplicationListener</listener-class> </listener>
<servlet>
<servlet-name>TLERequestReceiver</servlet-name>
<servlet-class>tle.framework.RequestReceiver</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TLERequestReceiver</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
TLEWebapplicationListener 클래스는 웹 어플리케이션이 시작될 때 틀 프레임워크의 초기화작업을 진행하는 리스너로서 콘텍스트 파라미터인 tle.configFile의 값을 틀 설정 파일로 사용한다. 위 예제에서 tle.configFile 콘텍스트 파라미터의 값은 다음과 같다.
{context.path}WEB-INFconfigTLEFrameworkConfig.xml
여기서 {context.path}는 웹 어플리케이션이 위치한 폴더의 경로로 대체된다. 예를 들어, 웹 어플리케이션 폴더가 c: omcatwebappsROOT 인 경우 위의 값은 실제로 c: omcatwebappsROOTWEB-INFconfigTLEFrameworkConfig.xml이 된다.
tle.framework.RequestReceiver는 클라이언트의 요청을 받아서 틀 컨트롤러에 전달하는 역할을 하는데(이미 앞에서 RequestReceiver에 대해 간단하게 설명한 바 있다), 위 코드에서는 *.do로 들어오는 요청을 받아서 틀 컨트롤러에 전달하도록 설정하였다.
틀 프레임워크 설정 파일 작성하기
web.xml 파일을 설정한 뒤에는 틀 프레임워크의 설정 파일을 작성해주면 된다. 틀 프레임워크의 설정 파일에는 다음의 두 가지 정보가 저장된다.
- 컴포넌트 목록 및 기본 설정
- RequestAnalyzer, RequestProcessor, UICreator, AAManager로 사용할 컴포넌트 지정
설정 파일의 예를 들면 아래와 같다.
코드: WEB-INFconfigTLEFrameworkConfig.xml
<?xml version="1.0" encoding="euc-kr" ?>
<tle>
<component-list>
<!-- DefaultRequestAnalyzer에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.ra.DefaultRequestAnalyzer</component-class>
<component-id>DefaultRequestAnalyzer</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestAnalyzer.properties</param-value>
</init-param>
</component>
<!-- DefaultRequestProcessor에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.rp.DefaultRequestProcessor</component-class>
<component-id>DefaultRequestProcessor</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestProcessor.xml</param-value>
</init-param>
</component>
<!-- DefaultAAManager에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.aamanager.NullAAManager</component-class>
<component-id>NullAAManager</component-id>
</component>
<!-- DefaultUICreator에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.uicreator.DefaultUICreator</component-class>
<component-id>DefaultUICreator</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultUICreator.xml</param-value>
</init-param>
</component>
</component-list>
<framework>
<core-component>
<request-analyzer-id>DefaultRequestAnalyzer</request-analyzer-id>
<request-processor-id>DefaultRequestProcessor</request-processor-id>
<ui-creator-id>DefaultUICreator</ui-creator-id>
<aa-manager-id>NullAAManager</aa-manager-id>
</core-component>
</framework>
</tle>
<?xml version="1.0" encoding="euc-kr" ?>
<tle>
<component-list>
<!-- DefaultRequestAnalyzer에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.ra.DefaultRequestAnalyzer</component-class>
<component-id>DefaultRequestAnalyzer</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestAnalyzer.properties</param-value>
</init-param>
</component>
<!-- DefaultRequestProcessor에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.rp.DefaultRequestProcessor</component-class>
<component-id>DefaultRequestProcessor</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestProcessor.xml</param-value>
</init-param>
</component>
<!-- DefaultAAManager에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.aamanager.NullAAManager</component-class>
<component-id>NullAAManager</component-id>
</component>
<!-- DefaultUICreator에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.uicreator.DefaultUICreator</component-class>
<component-id>DefaultUICreator</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultUICreator.xml</param-value>
</init-param>
</component>
</component-list>
<framework>
<core-component>
<request-analyzer-id>DefaultRequestAnalyzer</request-analyzer-id>
<request-processor-id>DefaultRequestProcessor</request-processor-id>
<ui-creator-id>DefaultUICreator</ui-creator-id>
<aa-manager-id>NullAAManager</aa-manager-id>
</core-component>
</framework>
</tle>
먼저, 틀 프레임워크에 장착될 컴포넌트는 <component-list> 태그에서 명시한다. component-list 태그는 component 태그를 갖는데, component 태그의 각 태그는 다음과 같은 의미를 갖는다.
틀 프레임워크 설정 파일의 컴포넌트 설정 관련 태그 | |
태그 | 설명 |
component-class | 컴포넌트 구현 클래스의 완전한 이름 |
component-id | 컴포넌트를 식별할 때 사용되는 아이디. 유일한 값이어야 한다. |
init-param/param-name | 컴포넌트를 초기화할 때 전달되는 파라미터의 이름 |
init-param/param-value | 컴포넌트를 초기화할 때 전달되는 파라미터의 값 |
각 컴포넌트는 필요한 설정 정보를 초기화 파라미터를 사용해서 입력받을 수 있다.
컴포넌트의 설정을 완료했으면, 그 다음으로 설정할 내용은 네 개의 핵심 컴포넌트를 지정하는 것이다. 위 설정 파일에서 핵심 컴포넌트를 지정하는 부분은 다음과 같다.
<framework>
<core-component>
<request-analyzer-id>DefaultRequestAnalyzer</request-analyzer-id>
<request-processor-id>DefaultRequestProcessor</request-processor-id>
<ui-creator-id>DefaultUICreator</ui-creator-id>
<aa-manager-id>NullAAManager</aa-manager-id>
</core-component>
</framework>
<core-component>
<request-analyzer-id>DefaultRequestAnalyzer</request-analyzer-id>
<request-processor-id>DefaultRequestProcessor</request-processor-id>
<ui-creator-id>DefaultUICreator</ui-creator-id>
<aa-manager-id>NullAAManager</aa-manager-id>
</core-component>
</framework>
위 코드에서 각 태그는 다음과 같은 의미를 갖는다.
핵심 컴포넌트 지정 관련 태그 | |
태그 | 설명 |
request-analyzer-id | RequestAnalyzer로 사용할 컴포넌트의 아이디를 지정한다. |
request-processor-id | RequestProcessor로 사용할 컴포넌트의 아이디를 지정한다. |
ui-creator-id | UICreator로 사용할 컴포넌트의 아이디를 지정한다. |
aa-manager-id | AAManager로 사용할 컴포넌트의 아이디를 지정한다. |
네 개의 태그에서 사용할 아이디값은 앞서 컴포넌트를 정의할 때 component-id 태그로 지정한 값을 지정해주면 된다.
기본 컴포넌트의 주요 기능 및 설정
DefaultRequestAnalyzer의 주요기능
DefaultRequestAnalyzer 컴포넌트는 요청 분석기로서 클라이언트의 요청을 분석하게 된다. DefaultRequestAnalyzer 컴포넌트가 제공하는 주요 기능은 다음과 같다.
- 클라이언트의 요청 URI 중에서 콘텍스트 경로를 제외한 나머지 부분을 명령어 ID로 추출한다.
- Jakarta Commons FileUpload API를 사용해서 파일 업로드를 처리한다.
- 업로드하려는 파일의 크기를 제한할 수 있다.
http://host:port/tle/admin/front.view
이때, 요청 URI인 /tle/admin/front.view 중에서 콘텍스트 경로인 /tle 을 제외한 나머지 부분인 /admin/front.view 가 명령어 ID로 사용된다.
파일 업로드시에는 인코딩을 multipart/form-data 타입으로 하게 되는데, DefaultRequestAnalyzer 컴포넌트는 이 인코딩 타입으로 전송된 데이터를 처리하기 위해서 Jakarta Commons FileUpload API를 사용한다. 틀 프레임워크의 배포판에는 Jakarta Commons FileUpload API와 관련된 jar 파일인 commons-fileupload-1.0.jar 파일이 포함되어 있으므로, commons-fileupload-1.0.jar 파일을 웹 어플리케이션의 WEB-INF/lib 폴더에 복사해야 DefaultRequestAnalyzer 컴포넌트가 올바르게 동작한다.
DefaultRequestAnalyzer 설정하기
앞서 틀 프레임워크 설정 파일에서 DefaultRequestAnalyzer 컴포넌트를 설정한 부분을 살펴보도록 하자.
<!-- DefaultRequestAnalyzer에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.ra.DefaultRequestAnalyzer</component-class>
<component-id>DefaultRequestAnalyzer</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestAnalyzer.properties</param-value>
</init-param>
</component>
<component>
<component-class>tle.framework.component.impl.ra.DefaultRequestAnalyzer</component-class>
<component-id>DefaultRequestAnalyzer</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestAnalyzer.properties</param-value>
</init-param>
</component>
위 코드에서 볼 수 있듯이, DefaultRequestAnalyzer 컴포넌트는 configFile 초기화 파라미터를 사용해서 사용할 설정 파일의 경로를 입력받는다. 파라미터 값에 있는 {context.path}는 틀 프레임워크 설정 파일에서와 마찬가지로 콘텍스트의 경로를 의미한다.
DefaultRequestAnalyzer가 사용하는 설정 파일은 자바의 프로퍼티 파일로서 아래와 같이 작성된다.
코드: /WEB-INF/config/DefaultRequestAnalyzer.properties
tempDir=C:\WINDOWS\Temp
max=10M
encoding=EUC-KR
tempDir=C:\WINDOWS\Temp
max=10M
encoding=EUC-KR
위 프로퍼티 파일에서 각 프로퍼티는 아래와 같다.
DefaultRequestAnalyzer 설정 파일의 프로퍼티 | |
프로퍼티 | 설명 |
tempDir | 업로드한 파일이 임시로 저장될 디렉토리. 임시로 파일이 저장되는 공간이며 생성된 임시 파일은 자동으로 삭제된다. |
max | 최대 업로드 사이즈: 기본적으로 바이트 단위이며, 뒤에 k, m, g를 붙이면 각각 KB, MB, GB를 기본 크기로 사용한다 |
encoding | 요청 파라미터를 읽어올 때 사용할 캐럭터 인코딩 |
DefaultRequestProcessor 주요 기능
DefaultRequestProcessor 컴포넌트는 클라이언트가 요청을 처리하는 요청 처리기이다. DefaultRequestProcessor는 요청 분석기가 생성한 명령어 ID를 사용해서 알맞은 기능을 수행한다. DefaultRequestProcessor 컴포넌트는 아래 그림과 같이 명령어 ID에 알맞은 핸들러(handler)를 사용해서 클라이언트의 요청을 처리한다.
DefaultRequestProcessor 컴포넌트는 명령어 ID를 전달받으면, 명령어 ID에 해당하는 핸들러를 실행한다. 핸들러는 클라이언트가 요청한 기능을 수행한 뒤 결과를 담아서 요청 처리기에 전달하며, 요청 처리기는 다시 그 결과를 틀 컨트롤러에 전달하게 된다.
(일반적으로 하나의 핸들러가 하나의 명령어 ID를 처리하게 되는데(물론, 하나의 핸들러가 다수의 명령어 ID를 처리하기도 한다), 여기서 DefaultRequestProcessor 컴포넌트가 커맨드 패턴을 사용하고 있다는 것을 알 수 있다. 커맨드 패턴에 대한 글은 자바캔의 '커맨드(Command) 패턴과 그 구현' 글을 참고하기 바란다.)
DefaultRequestProcessor 설정하기
DefaultRequestProcessor 컴포넌트를 사용하기 위해서는 먼저 틀 프레임워크 설정 파일에 다음과 같이 DefaultRequestProcessor 컴포넌트를 사용한다고 명시해주어야 한다.
<!-- DefaultRequestProcessor에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.rp.DefaultRequestProcessor</component-class>
<component-id>DefaultRequestProcessor</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestProcessor.xml</param-value>
</init-param>
</component>
<component>
<component-class>tle.framework.component.impl.rp.DefaultRequestProcessor</component-class>
<component-id>DefaultRequestProcessor</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultRequestProcessor.xml</param-value>
</init-param>
</component>
DefaultRequestAnalyzer 컴포넌트와 마찬가지로 DefaultRequestProcessor 컴포넌트도 configFile 초기화 파라미터를 사용해서 설정 파일의 경로를 전달받는다. DefaultRequestProcessor 컴포넌트의 설정 파일은 다음의 두 가지 정보를 담는다.
- 핸들러 목록
- 명령어 ID와 핸들러 매핑
코드: /WEB-INF/config/DefaultRequestProcessor.xml
<?xml version="1.0" encoding="euc-kr" ?>
<tle>
<default-request-processor usingIndependentClassPath="false">
<!-- 핸들러 목록을 지정-->
<handler-list>
<handler id="greeting" class="test.tle.command.GreetingCommandHandler" />
<handler id="greeting2" jsp="/handler/hello_handler.jsp" />
</handler-list>
<!-- 명령어 ID와 핸들러 매핑 -->
<command-handler-mapping defaultHandler="NOHANDLER" useDefaultOnInvalidMapping="false">
<mapping commandID="/greeting.do" handlerID="greeting" />
<mapping commandID="/hello/greeting.do" handlerID="greeting2" />
</command-handler-mapping>
</default-request-processor>
</tle>
<?xml version="1.0" encoding="euc-kr" ?>
<tle>
<default-request-processor usingIndependentClassPath="false">
<!-- 핸들러 목록을 지정-->
<handler-list>
<handler id="greeting" class="test.tle.command.GreetingCommandHandler" />
<handler id="greeting2" jsp="/handler/hello_handler.jsp" />
</handler-list>
<!-- 명령어 ID와 핸들러 매핑 -->
<command-handler-mapping defaultHandler="NOHANDLER" useDefaultOnInvalidMapping="false">
<mapping commandID="/greeting.do" handlerID="greeting" />
<mapping commandID="/hello/greeting.do" handlerID="greeting2" />
</command-handler-mapping>
</default-request-processor>
</tle>
클라이언트의 요청을 실제로 처리하게 되는 핸들러의 목록을 지정하는 태그는 handler-list/handler로서 handler 태그는 다음과 같은 속성을 갖고 있습니다.
DefaultRequestProcessor 컴포넌트 설정 파일: handler 태그의 속성 | ||
속성 | 설명 | |
id | 핸들러를 식별하기 위한 아이디. 각각의 핸들러는 고유의 식별자를 가져야 한다. | |
class | 클래스로 구현된 핸들러의 완전한 클래스 이름 | 둘 중에 한가지를 지정해주어야 한다. |
jsp | JSP로 구현된 핸들러의 콘텍스트 내에서의 경로 |
핸들러는 크게 클래스와 JSP의 두가지 방식으로 구현할 수 있는데 각각의 구현방법에 대해서는 뒤에서 설명하도록 하겠다.
핸들러 목록을 지정했다면 다음으로 할 일은 클라이언트의 요청을 나타내는 명령어 ID와 핸들러를 매핑시키는 작업이다. 이는 command-handler-mapping/mapping 태그를 통해서 지정할 수 있으며, mapping 태그의 각 속성은 아래와 같은 의미를 갖는다.
DefaultRequestProcessor 컴포넌트 설정 파일: mapping 태그의 속성 | |
속성 | 설명 |
commandID | 명령어 ID |
handlerID | 지정한 명령어 ID에 해당하는 요청을 처리할 핸들러의 아이디 |
예를 들어, 아래의 코드는 /hello/greeting.do 라는 요청이 들어오면 greeting2 라는 핸들러를 실행한다는 의미를 갖는다.
<mapping commandID="/hello/greeting.do" handlerID="greeting2" />
만약 아이디가 greeting2인 핸들러가 /handler/hello_handler.jsp 라면, /hello/greeting.do 요청이 들어오면 /handler/hello_handler.jsp가 실행된다.
DefaultRequestProcessor 핸들러 구현하기
DefaultRequestProcessor 컴포넌트의 핸들러는 클라이언트가 요청한 로직을 수행한 뒤 그 결과를 UI 생성기에 전달해주는 기능을 담당한다. 즉, MVC 패턴에 컨트롤러의 역할을 담당하는 것이 바로 핸들러인 것이다. 핸들러는 모델을 사용해서 클라이언트가 요구한 로직을 수행하게 되며, 그 결과를 뷰에 전달하게 된다.
DefaultRequestProcessor 컴포넌트는 두 가지 방식으로 핸들러를 구현할 수 있도록 해 준다. 한가지는 스트러츠의 Action과 같이 자바 클래스로 핸들러를 구현하는 방식이며, 다른 한가지는 JSP로 핸들러를 구현하는 방식이다. 개발자에 따라 원하는 방식으로 핸들러를 구현하면 되는데, 필자의 경우는 JSP 방식을 선호한다.
두 가지 방식으로 핸들러를 구현하는 방법에 대해서 살펴보고, 어떤 방식이 본인이 사용하기에 편리할지는 직접 판단해보기 바란다.
DefaultRequestProcessor의 클래스 핸들러 작성하기
먼저, 클래스를 사용해서 핸들러를 작성해보도록 하겠다. 핸들러 클래스를 작성하기 위해서는 아래의 규칙을 따라야 한다.
- tle.framework.component.impl.rp.handler.CommandHandler 인터페이스를 implements 한다.
- public DefaultResult process(RequestInfo requestInfo, DefaultResult result) 메소드를 구현한다.
- process() 메소드에서
- requestInfo 객체로부터 파라미터, 쿠키 등 로직을 처리하는 데 필요한 값을 읽어온다.
- 알맞은 로직을 수행한다.
- result.setData() 메소드를 사용해서 뷰에서 사용할 값을 저장한다.
- result.setCode() 메소드를 사용해서 결과 코드를 입력한다.
코드: /WEB-INF/src/test/tle/command/GreetingCommandHandler.java
package test.tle.command;
import tle.framework.component.impl.rp.DefaultResult;
import tle.framework.component.impl.rp.handler.CommandHandler;
import tle.framework.component.spi.ra.RequestInfo;
/**
* Result의 "greeting" 데이터에 "안녕하세요"라는 데이터를 추가한다.
*
* @author 최범균
*/
public class GreetingCommandHandler implements CommandHandler {
public DefaultResult process(RequestInfo requestInfo, DefaultResult result) {
result.setData("greeting", "안녕하세요."); // 데이터 저장
result.setCode("success"); // 요청 처리 결과 코드 입력
return result;
}
}
위 코드에서 GreetingCommandHandler는 클라이언트의 요청을 전달받으면, "greeting" 데이터의 값을 "안녕하세요."로 지정한 뒤 결과 코드값을 "success"로 지정한다. 이 코드값은 클라이언트의 요청을 처리한 결과를 의미하며, 알맞은 값을 입력할 수 있다. 예를 들어, 다음과 같이 상황에 따라 서로 다른 코드값을 지정할 수 있다.
public DefaultResult process(RequestInfo requestInfo, DefaultResult result) {
// 로그인 기능을 수행
boolean isSuccess = sessionManager.openSession(
requestInfo.getParameter("id"),
requestInfo.getParameter("password"));
if (isSuccess) {
...
result.setCode("loginSuccess");
} else {
...
result.setCode("loginFail");
}
return result;
}
// 로그인 기능을 수행
boolean isSuccess = sessionManager.openSession(
requestInfo.getParameter("id"),
requestInfo.getParameter("password"));
if (isSuccess) {
...
result.setCode("loginSuccess");
} else {
...
result.setCode("loginFail");
}
return result;
}
핸들러는 상황에 알맞은 코드값을 지정하며, Result를 전달받게 될 UI 생성기 컴포넌트는 이 결과 코드값을 사용하여 알맞은 화면을 생성하게 된다.
DefaultRequestProcessor의 JSP 핸들러 작성하기
앞서 클래스를 사용해서 핸들러를 작성하는 방법을 살펴봤는데, 두번째로 JSP를 이용한 핸들러 클래스의 작성 방법에 대해서 살펴보도록 하자.
JSP 핸들러는 아래와 같은 코드를 사용해서 작성한다.
코드: handlerhello_handler.jsp
<%@ page pageEncoding = "euc-kr" %>
<%@ page import = "tle.framework.component.impl.rp.DefaultResult" %>
<%@ page import = "tle.framework.component.spi.ra.RequestInfo" %>
<%
// 핸들러 클래스의 process() 메소드가 두 개의 파라미터로 전달받는 객체를
// request의 attribute로부터 가져온다.
DefaultResult result = (DefaultResult)request.getAttribute("result");
RequestInfo ri = (RequestInfo)request.getAttribute("requestInfo");
// 알맞은 로직을 수행한 뒤 결과 값을 저장한다.
result.setData("greeting", "헬로우");
// 처리 결과 코드를 기록한다.
result.setCode("success");
// TLEFramework 2.0.5.5 및 이하 버전에서는 JSP 엔진에 따라
// out.clearBuffer()를 실행하지 않으면 캐릭터셋 처리가
// 올바르게 처리되지 않을 수도 있다.
//out.clearBuffer();
%>
<%@ page pageEncoding = "euc-kr" %>
<%@ page import = "tle.framework.component.impl.rp.DefaultResult" %>
<%@ page import = "tle.framework.component.spi.ra.RequestInfo" %>
<%
// 핸들러 클래스의 process() 메소드가 두 개의 파라미터로 전달받는 객체를
// request의 attribute로부터 가져온다.
DefaultResult result = (DefaultResult)request.getAttribute("result");
RequestInfo ri = (RequestInfo)request.getAttribute("requestInfo");
// 알맞은 로직을 수행한 뒤 결과 값을 저장한다.
result.setData("greeting", "헬로우");
// 처리 결과 코드를 기록한다.
result.setCode("success");
// TLEFramework 2.0.5.5 및 이하 버전에서는 JSP 엔진에 따라
// out.clearBuffer()를 실행하지 않으면 캐릭터셋 처리가
// 올바르게 처리되지 않을 수도 있다.
//out.clearBuffer();
%>
JSP를 사용하여 작성한 핸들러의 코드를 보면 클래스를 사용하여 작성한 핸들러 코드와 크게 다르지 않다는 것을 알 수 있다. JSP 핸들러와 클래스 핸들러 사이의 차이점이라면 다음의 두가지 정도 뿐이다.
- 클래스 핸들러는 RequestInfo와 DefaultResult를 메소드 인자로 받지만, JSP 핸들러는 request 기본 객체의 attribute로부터 가져온다.
- 클래스 핸들러는 result를 리턴하는 반면에 JSP 핸들러는 리턴하지 result를 않는다.
<%@ page pageEncoding = "euc-kr" %>
<%@ page import = "tle.framework.component.impl.rp.DefaultResult" %>
<%@ page import = "tle.framework.component.spi.ra.RequestInfo" %>
<%
DefaultResult result = (DefaultResult)request.getAttribute("result");
RequestInfo ri = (RequestInfo)request.getAttribute("requestInfo");
...
result.setCode("success");
// TLEFramework 2.0.5.5 및 이하 버전에선 실행시켜주는 것이 좋음
out.clearBuffer();
%>
<%@ page import = "tle.framework.component.impl.rp.DefaultResult" %>
<%@ page import = "tle.framework.component.spi.ra.RequestInfo" %>
<%
DefaultResult result = (DefaultResult)request.getAttribute("result");
RequestInfo ri = (RequestInfo)request.getAttribute("requestInfo");
...
result.setCode("success");
// TLEFramework 2.0.5.5 및 이하 버전에선 실행시켜주는 것이 좋음
out.clearBuffer();
%>
TLEFramework 2.0.5.5 버전까지는 JSP 핸들러에 실제 response에 해당하는 객체를 전달해서 JSP 핸들러가 지정한 컨텐츠 타입이나 출력한 내용 때문에 실제 UICreator가 내용을 생성할 때 글자가 깨지는 경우가 발생했다. 그래서 TLEFramework 2.0.5.5 및 그 이하 버전에서는, 이런 문제가 발생하는 콘테이너의 경우 JSP 핸들러의 마지막에 out.clearBuffer() 메소드를 실행시켜서 문제를 해결할 수 있었다.
TLEFramework 2.0.5.6 버전부터는 JSP 핸들러에 실제 response에 해당하는 객체가 아닌 HttpServletResponseWrapper 객체를 새롭게 생성해서 전달한 뒤 JSP 핸들러가 생성한 내용이나 지정한 컨텐츠 타입을 무시하기 때문에, 더 이상 이런 문제가 발생하지 않는다.
DefaultUICreator의 주요기능
앞서 살펴본 내용은 클라이언트가 요청한 기능을 처리하는 방법에 대한 것이었다. 핸들러는 클라이언트의 요청을 처리한 뒤 결과로 보여줄 내용을 Result에 저장한다. 틀 컨트롤러는 요청 처리기 컴포넌트가 생성한 Result를 UI 생성기에 전달해서 알맞은 UI를 생성하도록 한다. 틀 프레임워크가 기본적으로 제공하는 DefaultUICreator 컴포넌트는 JSP를 사용하여 뷰를 생성하며 다음과 같은 기능을 제공한다.
- 뷰셋(View Set)을 제공한다.
- 클라이언트의 타입에 따라 뷰셋을 지정할 수 있다.
- JSP 템플릿 기능을 제공한다.
- 뷰의 상속 개념을 제공한다.
- 특정 명령어ID의 결과코드 값에 따라서 각각 알맞은 뷰를 보여줄 수 있다.
JSP 템플릿 기능을 레이아웃 코드를 중복해서 작성하지 않고 템플릿으로 만들어 UI를 조립하듯이 사용할 수 있도록 해준다.
뷰셋, 템플릿, 뷰의 상속 등 DefaultUICreator 컴포넌트가 제공하는 기능에 대해서는 본 시리즈를 진행하면서 살펴보게 될 것이다.
DefaultUICreator 설정하기
DefaultUICreator 컴포넌트를 사용하기 위해서는 먼저 틀 프레임워크 설정 파일에 다음과 같이 DefaultUICreator 컴포넌트를 사용한다고 명시해주어야 한다.
<!-- DefaultUICreator에 대한 설정 -->
<component>
<component-class>tle.framework.component.impl.uicreator.DefaultUICreator</component-class>
<component-id>DefaultUICreator</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultUICreator.xml</param-value>
</init-param>
</component>
<component>
<component-class>tle.framework.component.impl.uicreator.DefaultUICreator</component-class>
<component-id>DefaultUICreator</component-id>
<init-param>
<param-name>configFile</param-name>
<param-value>{context.path}WEB-INFconfigDefaultUICreator.xml</param-value>
</init-param>
</component>
DefaultRequestProcessor 컴포넌트와 마찬가지로 DefaultUICreator 컴포넌트도 configFile 초기화 파라미터를 사용해서 UI와 관련된 설정 정보가 담긴 파일의 경로를 입력받는다.
DefaultUICreator 컴포넌트의 설정 파일은 다음과 같은 정보를 담는다.
- 뷰셋(ViewSet>
- 글로벌 뷰
- 명령어ID와 결과값에 따른 뷰 매핑
- 클라이언트 타입
코드: WEB-INFconfigDefaultUICreator.xml
<?xml version="1.0" encoding="euc-kr" ?>
<tle>
<default-ui-creator>
<view-set id="web"
error-view="error" >
<view id="greeting" page="/view/greeting.jsp" />
<view id="greeting2" page="/view/greeting2.jsp" />
<view id="error" page="/view/error.jsp" />
</view-set>
<command-view-mapping>
<command id="/greeting.do">
<result code="success" view="greeting" />
</command>
<command id="/hello/greeting.do" view="greeting2" />
</command-view-mapping>
<client-view-set default-view-set="web">
<client type="MSIE" view-set="web" />
<client type="NETSCAPE" view-set="web" />
</client-view-set>
</default-ui-creator>
</tle>
<?xml version="1.0" encoding="euc-kr" ?>
<tle>
<default-ui-creator>
<view-set id="web"
error-view="error" >
<view id="greeting" page="/view/greeting.jsp" />
<view id="greeting2" page="/view/greeting2.jsp" />
<view id="error" page="/view/error.jsp" />
</view-set>
<command-view-mapping>
<command id="/greeting.do">
<result code="success" view="greeting" />
</command>
<command id="/hello/greeting.do" view="greeting2" />
</command-view-mapping>
<client-view-set default-view-set="web">
<client type="MSIE" view-set="web" />
<client type="NETSCAPE" view-set="web" />
</client-view-set>
</default-ui-creator>
</tle>
위 코드에서 핵심 태그는 view-set 태그와 command-view-mapping 태그이다. 먼저 view-set 태그에는 view 태그가 존재하는데 뷰 태그는 아래표와 같은 내용을 설정한다.
view 태그 : 핸들러가 처리한 결과를 보여줄 때 사용될 JSP 정보를 입력한다. | |
속성 | 내용 |
id | 뷰셋에 있는 뷰의 고유 식별자값을 뷰 페이지의 식별자 |
page | 뷰를 생성할 때 사용될 JSP |
command-view-mapping 태그는 요청 처리기의 결과를 어떤 뷰를 통해서 보여줄지를 지정하기 위해 사용된다. command-view-mapping 태그는 자식 태그로 command 태그를 갖고 있으며, command 태그는 다음과 같은 코드 형태를 띈다.
<command id="/greeting.do">
<result code="success" view="greeting" />
<result code="fail" view="greetingFail" />
</command>
<result code="success" view="greeting" />
<result code="fail" view="greetingFail" />
</command>
위 코드는 다음을 의미한다.
- 클라이언트가 요청한 명령어 ID가 "/greeting.do"인 경우,
요청 처리기가 생성한 결과 코드가- success 이면, greeting 뷰를 통해서 결과를 보여주고
- fail 이면, greetingFail 뷰를 통해서 결과를 보여준다.
<view-set id="web"
error-view="error" >
<view id="greeting" page="/view/greeting.jsp" />
</view-set>
<command-view-mapping>
<command id="/greeting.do">
<result code="success" view="greeting" />
</command>
</command-view-mapping>
error-view="error" >
<view id="greeting" page="/view/greeting.jsp" />
</view-set>
<command-view-mapping>
<command id="/greeting.do">
<result code="success" view="greeting" />
</command>
</command-view-mapping>
명령어 ID가 /greeting.do이고, 요청 처리기가 이 명령어를 처리한 결과 코드값이 "success"이면, "greeting"뷰인 /view/greeting.jsp를 이용해서 결과를 보여준다는 것을 의미한다.
뷰셋 설정에 따라 보여지는 화면이 달라질 수 있는데, 이에 대해서는 틀 프레임워크의 시리즈를 진행하면서 살펴보기로 하자.
DefaultUICreator 뷰 JSP 작성하기
간단하게 뷰 기능을 수행하는 JSP를 작성해보자. 본 글에서 사용하는 예제는 아래와 같이 3개의 뷰를 정의하고 있다.
<view-set id="web"
error-view="error" >
<view id="greeting" page="/view/greeting.jsp" />
<view id="greeting2" page="/view/greeting2.jsp" />
<view id="error" page="/view/error.jsp" />
</view-set>
error-view="error" >
<view id="greeting" page="/view/greeting.jsp" />
<view id="greeting2" page="/view/greeting2.jsp" />
<view id="error" page="/view/error.jsp" />
</view-set>
"error" 뷰는 view-set 태그의 error-view 속성을 통해서 에러 뷰로 지정됐는데, 에러 뷰에 대해서는 나중에 살펴보기로 하고, 본 글에서는 나머지 두 개의 뷰의 코드를 살펴보도록 하겠다. 먼저 "greeting" 뷰에 해당하는 /view/greeting.jsp 코드를 보도록 하자.
코드: /view/greeting.jsp
<%@ page contentType = "text/html; charset=euc-kr" %>
<%@ page import = "tle.framework.component.spi.rp.Result" %>
<%
Result result = (Result)request.getAttribute("result");
String greeting = (String)result.getData("greeting");
%>
<html>
<head><title>greeting</title></head>
<body>
<%= greeting %>
</body>
</html>
<%@ page contentType = "text/html; charset=euc-kr" %>
<%@ page import = "tle.framework.component.spi.rp.Result" %>
<%
Result result = (Result)request.getAttribute("result");
String greeting = (String)result.getData("greeting");
%>
<html>
<head><title>greeting</title></head>
<body>
<%= greeting %>
</body>
</html>
위 코드는 DefaultUICreator 컴포넌트와 관련된 뷰 JSP 페이지의 가장 기본적인 코드 형태를 보여주고 있다. 먼저 뷰 JSP는 결과를 참조하기 위해 request의 "result" 속성으로부터 Result 객체를 구한다. 그런 뒤, Result.getData() 메소드를 사용해서 요청 처리기가 생성한 데이터를 읽어와 그 데이터를 알맞게 사용해서 화면을 생성하게 된다.
예제 실행하기
지금까지 살펴본 예제의 전체 코드는 하단의 다운로드 링크를 통해 구할 수 있다. 예제 압축 파일인 tletest.war 파일을 [톰캣]/webapps와 같이 웹 어플리케이을 자동으로 인식할 수 있는 폴더에 복사하면 자동으로 배포된다. 자동 배포가 아니더라도 war 파일의 압축을 푼뒤 어플리케이션 서버의 설정을 알맞게 지정해줘도 된다.
tletest.war를 알맞게 배포한 뒤 어플리케이션 서버를 실행해보자. 톰캣의 /webapps 폴더에 배포했다면 아래의 URL을 사용해서 결과를 확인할 수 있을 것이다.
http://localhost:8080/tletest/greeting.do
http://localhost:8080/tletest/hello/greeting.do
http://localhost:8080/tletest/hello/greeting.do
두 URL을 실행하면 아래와 같이 결과가 출력될 것이다.
예제 실행 흐름
http://localhost:8080/tletest/greeting.do 요청이 왔을 때 틀 프레임워크의 순차적인 처리 순서는 아래 그림과 같다.
위 실행 순서를 모르더라도 틀을 사용하는데에 지장은 없다. 하지만, 위의 실행 순서는 틀 프레임워크나 스트러츠 등 MVC 패턴에 기반한 프레임워크들은 대부분 위와 비슷한 순서로 작업을 진행하므로, 위 순서를 이해해두면 프레임워크를 사용하는데 많은 도움이 될 것이다.
다음 글에서는
본 글에서는 간단하게 틀 프레임워크의 기본적인 사용방법을 살펴보았다. 어렴풋이 틀의 설정 방법이나 사용방법, 그리고 틀이 어떤 식으로 돌아가는지에 대한 내용을 이해했으리라 본다. 다음 글에서는 클라이언트의 요청을 처리할때 사용되는 DefaultRequestProcessor의 핸들러의 개발 방법에 대해서 자세하게 살펴보도록 하겠다.
관련링크: