2010년 12월 29일 수요일
2010년 12월 20일 월요일
2010년 12월 19일 일요일
[펌] url rewrite filter 사용 방법
Fancy URL 이란?
/board.html?action=list&page=2 이런 일반적인 URL 형식 대신
/board/list/2 이런 형식의 확장자가 없고 쿼리 스트링이 없는 간단한 URL을 말한다.
(fancy url의 정확한 뜻은 모르지만 나는 이렇게 이해하고 있다)
태터툴즈의 /tt/tags/태터툴즈 (태터툴즈란 태그가 있는 글목록을 출력하는 url) 이런걸 생각하면 된다.
가장 간단한 방법은 Apache의 mod_rewrite 모듈을 사용하면 된다.
하지만 이 경우는 아파치가 없거나 있어도 mod_rewrite 모듈 사용이 불가능한 걸 가정했다.
확장자가 없는 매핑이라도 특별한 패턴이 있으면 간단하게 할 수 있다. 예를 들면
/board/list
/board/view
/board/write
이런 매핑을 처리하려면 다음처럼 하면 된다.
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/board/*</url-pattern>
</servlet-mapping>
하지만
/list
/view
/write
이런걸 매핑하려면?
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/list</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/view</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/write</url-pattern>
</servlet-mapping>
특별한 패턴이 없는 url이기 때문에 전부다 매핑해 줘야 한다.
이건 세개뿐이니까 그렇지 매번 늘어날 때마다 매핑해 줄수도 없다.
특히 어떤 url로 요청이 들어올지 미리 알 수 없는 경우는 난감하다.
예를 들면 /(username) 했을 경우 해당 사용자의 정보를 보여준다든지 하는 것
아무튼 글의 요점은 확장자가 없는 모든 요청을 dispatcher가 받도록 하고 싶다는 거다.
내가 알기로는... 현재의 servlet-mapping은... 아래와 같은 식이나
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/*</url-pattern> <!-- 모든 요청을 처리하되 -->
<url-pattern-exclude>*.*</url-pattern-exclude> <!-- 확장자 '.' 이 들어가면 안돼 -->
</servlet-mapping>
또는 아래와 같은 형식으로는...
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>^/([^.]*)$</url-pattern> <!-- . 이 포함되지 않은 패턴 -->
</servlet-mapping>
이렇게는 지원이 안되기 때문에;;; 조금 편법을 써야 했다.
일단 매핑은 아래처럼...
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
이렇게 해당 /app/로 시작하는 모든 요청에 대해 처리하도록 한다.
근데 이것도 맘에 안 드는게 확장자 없이 매핑하는건 되지만
url을 항상 /app/로 요청해야 한다.
그다지 의미있는 url은 아닌데 항상 따라다니는 것도 보기 안좋고...
appfuse에서 발견한... urlrewrite filter를 사용한다.
(appfuse에서는 이런 목적으로 쓰진 않았지만...)
https://urlrewrite.dev.java.net/ 에서 urlrewrite filter 3.0 을 받아 lib 폴더에 넣는다.
/WEB-INF/urlrewrite.xml 을 작성
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
<!-- https://urlrewrite.dev.java.net/manual/3.0 -->
<urlrewrite>
<rule>
<note>
확장자가 없는 요청은 /app/* 요청이다.
ex) /user/register -> /app/user/register
</note>
<from>^/([^.]*)$</from>
<to type="forward">/app/$1</to>
</rule>
</urlrewrite>
web.xml 에 url rewrite filter 등록
<filter>
<filter-name>urlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
<init-param>
<param-name>logLevel</param-name>
<param-value>commons</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>urlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Spring Context XML 파일에서...
<bean id="userMappings" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<map>
<entry key="/user/register" value-ref="userRegisterController"/>
<entry key="/user/login" value-ref="userLoginController"/>
</map>
</property>
앞에 /app를 붙여 주지 않아도 된다... 물론 브라우저에서 요청시에도 /app를 안 붙여도 되고...
/board.html?action=list&page=2 이런 일반적인 URL 형식 대신
/board/list/2 이런 형식의 확장자가 없고 쿼리 스트링이 없는 간단한 URL을 말한다.
(fancy url의 정확한 뜻은 모르지만 나는 이렇게 이해하고 있다)
태터툴즈의 /tt/tags/태터툴즈 (태터툴즈란 태그가 있는 글목록을 출력하는 url) 이런걸 생각하면 된다.
가장 간단한 방법은 Apache의 mod_rewrite 모듈을 사용하면 된다.
하지만 이 경우는 아파치가 없거나 있어도 mod_rewrite 모듈 사용이 불가능한 걸 가정했다.
확장자가 없는 매핑이라도 특별한 패턴이 있으면 간단하게 할 수 있다. 예를 들면
/board/list
/board/view
/board/write
이런 매핑을 처리하려면 다음처럼 하면 된다.
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/board/*</url-pattern>
</servlet-mapping>
하지만
/list
/view
/write
이런걸 매핑하려면?
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/list</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/view</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/write</url-pattern>
</servlet-mapping>
특별한 패턴이 없는 url이기 때문에 전부다 매핑해 줘야 한다.
이건 세개뿐이니까 그렇지 매번 늘어날 때마다 매핑해 줄수도 없다.
특히 어떤 url로 요청이 들어올지 미리 알 수 없는 경우는 난감하다.
예를 들면 /(username) 했을 경우 해당 사용자의 정보를 보여준다든지 하는 것
아무튼 글의 요점은 확장자가 없는 모든 요청을 dispatcher가 받도록 하고 싶다는 거다.
내가 알기로는... 현재의 servlet-mapping은... 아래와 같은 식이나
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/*</url-pattern> <!-- 모든 요청을 처리하되 -->
<url-pattern-exclude>*.*</url-pattern-exclude> <!-- 확장자 '.' 이 들어가면 안돼 -->
</servlet-mapping>
또는 아래와 같은 형식으로는...
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>^/([^.]*)$</url-pattern> <!-- . 이 포함되지 않은 패턴 -->
</servlet-mapping>
이렇게는 지원이 안되기 때문에;;; 조금 편법을 써야 했다.
일단 매핑은 아래처럼...
<servlet-mapping>
<servlet-name>lucy</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
이렇게 해당 /app/로 시작하는 모든 요청에 대해 처리하도록 한다.
근데 이것도 맘에 안 드는게 확장자 없이 매핑하는건 되지만
url을 항상 /app/로 요청해야 한다.
그다지 의미있는 url은 아닌데 항상 따라다니는 것도 보기 안좋고...
appfuse에서 발견한... urlrewrite filter를 사용한다.
(appfuse에서는 이런 목적으로 쓰진 않았지만...)
https://urlrewrite.dev.java.net/ 에서 urlrewrite filter 3.0 을 받아 lib 폴더에 넣는다.
/WEB-INF/urlrewrite.xml 을 작성
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN" "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
<!-- https://urlrewrite.dev.java.net/manual/3.0 -->
<urlrewrite>
<rule>
<note>
확장자가 없는 요청은 /app/* 요청이다.
ex) /user/register -> /app/user/register
</note>
<from>^/([^.]*)$</from>
<to type="forward">/app/$1</to>
</rule>
</urlrewrite>
web.xml 에 url rewrite filter 등록
<filter>
<filter-name>urlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
<init-param>
<param-name>logLevel</param-name>
<param-value>commons</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>urlRewriteFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Spring Context XML 파일에서...
<bean id="userMappings" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<map>
<entry key="/user/register" value-ref="userRegisterController"/>
<entry key="/user/login" value-ref="userLoginController"/>
</map>
</property>
앞에 /app를 붙여 주지 않아도 된다... 물론 브라우저에서 요청시에도 /app를 안 붙여도 되고...
2010년 12월 16일 목요일
OSGi 란
1. OSGi 란 무엇인가?
- Open Services Gateway initiative
- Dynamic Module System for Java
요약하면, OSGi 는 한 개의 번들 또는 여러 개의 번들로 이루어진 애플리케이션 자체를 언제든지 동적으로 프레임워크상에 설치, 실행, 업데이트, 중단, 제거하는 것을 가능하게 하는 매우 유연한 라이프 사이클 모델을 지원하는 프레임워크이다.
2. OGSi 의 특징
바이트코드와 가상머신 기술을 이용하여 코드 호환성을 보장하는 자바 플랫폼 위에서, 각 애플리케이션들이 번들이라 불리는 작고 재사용 가능한 컴포넌드로부터 조립될 수 있도록 도와준다. 번들은 OSGi에서 얘기하는 각각의 컴포넌트 똔느 애플리케이션을 가리키는 단위를 의미한다. OSGi는 JVM 위에서 돌아가는 하나의 프레임워크이며, 사용자가 개발한 프로그램들은 번들 형태로 선언되어 OSGi 내부에서 실행된다.
3. OSGi 아키텍쳐
3-1. OSGi 프레임워크
* Secutiry Layer : 자바의 보안 구조에 기반하고 있으며, 패키지나 서비스에 대한 권한을 관리하거나, Digitally Signed JAR 파일에 대한 지우너을 해주는 레이어이다. 꼭 사용하지 않아도 되는 선택가능한 레이어이다.
* Service Layer : 서비스 레지스트리 를 통해 서비스를 등록하고 찾을 수 있도록 지원하는 레이어이다.
* Life Cycle Layer : 번들이 어떻게 동적으로 설치되고 관리될 수 있는지를 정의하는 레이어이다. 번들 내에서 어떻게 외부의 OSGi Context에 접근할 수 있는지를 정의함.
* Module Layer : OSGi의 근간이 되는 번들
* Execution Environment : 번들이 수행될 수 있는 환경(J2SE등)
- Open Services Gateway initiative
- Dynamic Module System for Java
요약하면, OSGi 는 한 개의 번들 또는 여러 개의 번들로 이루어진 애플리케이션 자체를 언제든지 동적으로 프레임워크상에 설치, 실행, 업데이트, 중단, 제거하는 것을 가능하게 하는 매우 유연한 라이프 사이클 모델을 지원하는 프레임워크이다.
2. OGSi 의 특징
바이트코드와 가상머신 기술을 이용하여 코드 호환성을 보장하는 자바 플랫폼 위에서, 각 애플리케이션들이 번들이라 불리는 작고 재사용 가능한 컴포넌드로부터 조립될 수 있도록 도와준다. 번들은 OSGi에서 얘기하는 각각의 컴포넌트 똔느 애플리케이션을 가리키는 단위를 의미한다. OSGi는 JVM 위에서 돌아가는 하나의 프레임워크이며, 사용자가 개발한 프로그램들은 번들 형태로 선언되어 OSGi 내부에서 실행된다.
3. OSGi 아키텍쳐
3-1. OSGi 프레임워크
* Secutiry Layer : 자바의 보안 구조에 기반하고 있으며, 패키지나 서비스에 대한 권한을 관리하거나, Digitally Signed JAR 파일에 대한 지우너을 해주는 레이어이다. 꼭 사용하지 않아도 되는 선택가능한 레이어이다.
* Service Layer : 서비스 레지스트리 를 통해 서비스를 등록하고 찾을 수 있도록 지원하는 레이어이다.
* Life Cycle Layer : 번들이 어떻게 동적으로 설치되고 관리될 수 있는지를 정의하는 레이어이다. 번들 내에서 어떻게 외부의 OSGi Context에 접근할 수 있는지를 정의함.
* Module Layer : OSGi의 근간이 되는 번들
* Execution Environment : 번들이 수행될 수 있는 환경(J2SE등)
2010년 12월 15일 수요일
10 things in MySQL that won’t work as expected
http://architects.dzone.com/news/10-things-mysql-won’t-work
IExternalizable 인터페이스를 이용한 AMF 커스텀 직렬화 활용 [출처] IExternalizable 인터페이스를 이용한 AMF 커스텀 직렬화 활용|작성자 네모
LCDS, BlazeDS, ZendAMF등을 이용해 AMF(ActionScript Message Format)으로 데이터를 주고받는 형태는 이미 많은 예제들과 문서들이 있다. AMF는 데이터 통신을 위한 일종의 약속된 규약이고 이 데이터를 주고 받고 하는 과정에서 서로의 언어에 맞게 직렬화(Serialization)하는 것은 각 언어에서 지원해주는 라이브러리를 사용하면 된다. ActionScript 3.0은 기본 API에서 지원해준다. 자바의 경우는 BlazeDS나 LCDS를 사용하면 된다. PHP의 경우에는 ZendAMF를 사용하면 된다. 이들을 이용하면 가령, 자바와 java.lang.Interger는 ActionScript의 int나 uint로... java.lang.Double은 ActionScript의 Number형과 직렬화된다. 이는 일종의 기본적으로 지원되는 직렬화이다.
다음은 BlazeDS에서 기본적으로 지원되는 직렬화이다.
Serializing between ActionScript and Java
하지만 이런 기본 직렬화과정을 사용하지 않고 직렬화 자체를 커스터마이징(customizing)할 수 있다. 가령 클라이언트(예,Flash 애플리케이션)에서는 아이디, 이름, 속성, 가격의 정보가 중요하지만 서버(예,자바)에서는 재고(inventory)가 중요한 경우가 있다. 이런 경우에는 Flex와 Java쪽의 직렬화하는 클래스를 다음과 같이 디자인 할 수 있겠다.
위 코드는 ActionScript의 flash.utils.IExternalizable 인터페이스와 Java의 java.io.Externalizable 인터페이스를 이용해 기본직렬화를 무시하고 이들 인터페이스에 정의된 readExternal와 writeExternal 메소드를 호출하여 직렬화 자체를 커스터마이징 할 수 있다는 것을 의미한다. Externalizable 인터페이스를 구현한 클래스에 정의된 메소드가 기본 직렬화보다 우선순위가 높다는 것을 기억하면 되겠다.
Flex의 ArrayCollection을 다시 한번 보기 바란다. 이 클래스는 flash.utils.IExternalizable를 구현했다.
mx.collections.ArrayCollection
꼭 이런 경우만은 아니다. 쓸데없는 데이터의 크기를 줄이기 위한 방법도 해당한다. 예를들어 ActionScript 코드를 보면 다음과 같다.
데이터 통신을 위해 쓸데없이 Boolean객체를 주고 받을 필요없다. 위 코드처럼 직렬화를 커스터마이징한다면 송수신 데이터 자체의 크기도 줄일 수 있다. 이는 매우 유용하다.
참고글
flash.utils.IExternalizable
커스텀 직렬화의 사용
ActionScript 3.0 데이터 유형 및 직렬화(serialization)
Serializing between ActionScript and Java
AMF 3 스팩
AS3 BitmapData AMF solution using IExternalizable
Flex and PHP: remoting with Zend AMF
글쓴이 : 지돌스타(http://blog.jidolstar.com/644)
다음은 BlazeDS에서 기본적으로 지원되는 직렬화이다.
Serializing between ActionScript and Java
하지만 이런 기본 직렬화과정을 사용하지 않고 직렬화 자체를 커스터마이징(customizing)할 수 있다. 가령 클라이언트(예,Flash 애플리케이션)에서는 아이디, 이름, 속성, 가격의 정보가 중요하지만 서버(예,자바)에서는 재고(inventory)가 중요한 경우가 있다. 이런 경우에는 Flex와 Java쪽의 직렬화하는 클래스를 다음과 같이 디자인 할 수 있겠다.
01.
// Product.as
02.
package
samples.externalizable {
03.
04.
import
flash.utils.IExternalizable;
05.
import
flash.utils.IDataInput;
06.
import
flash.utils.IDataOutput;
07.
08.
[RemoteClass(alias=
"samples.externalizable.Product"
)]
09.
public
class
Product
implements
IExternalizable {
10.
public
function
Product(name:
String
=
null
) {
11.
this
.name = name;
12.
}
13.
14.
public
var
id:
int
;
15.
public
var
name:
String
;
16.
public
var
properties:
Object
;
17.
public
var
price:
Number
;
18.
19.
public
function
readExternal(input:IDataInput):
void
{
20.
name = input.readObject()
as
String
;
21.
properties = input.readObject();
22.
price = input.readFloat();
23.
}
24.
25.
public
function
writeExternal(output:IDataOutput):
void
{
26.
output.writeObject(name);
27.
output.writeObject(properties);
28.
output.writeFloat(price);
29.
}
30.
}
31.
}
01.
// Product.java
02.
package
samples.externalizable;
03.
04.
import
java.io.Externalizable;
05.
import
java.io.IOException;
06.
import
java.io.ObjectInput;
07.
import
java.io.ObjectOutput;
08.
import
java.util.Map;
09.
10.
/**
11.
* This Externalizable class requires that clients sending and
12.
* receiving instances of this type adhere to the data format
13.
* required for serialization.
14.
*/
15.
public
class
Product
implements
Externalizable {
16.
private
String inventoryId;
17.
public
String name;
18.
public
Map properties;
19.
public
float
price;
20.
21.
public
Product()
22.
{
23.
}
24.
25.
/**
26.
* Local identity used to track third party inventory. This property is
27.
* not sent to the client because it is server-specific.
28.
* The identity must start with an 'X'.
29.
*/
30.
public
String getInventoryId() {
31.
return
inventoryId;
32.
}
33.
34.
public
void
setInventoryId(String inventoryId) {
35.
if
(inventoryId !=
null
&& inventoryId.startsWith(
"X"
))
36.
{
37.
this
.inventoryId = inventoryId;
38.
}
39.
else
40.
{
41.
throw
new
IllegalArgumentException("3rd party product
42.
inventory identities must start with
'X'
");
43.
}
44.
}
45.
46.
/**
47.
* Deserializes the client state of an instance of ThirdPartyProxy
48.
* by reading in String for the name, a Map of properties
49.
* for the description, and
50.
* a floating point integer (single precision) for the price.
51.
*/
52.
public
void
readExternal(ObjectInput in)
throws
IOException,
53.
ClassNotFoundException {
54.
// Read in the server properties from the client representation.
55.
name = (String)in.readObject();
56.
properties = (Map)in.readObject();
57.
price = in.readFloat();
58.
setInventoryId(lookupInventoryId(name, price));
59.
}
60.
/**
61.
* Serializes the server state of an instance of ThirdPartyProxy
62.
* by sending a String for the name, a Map of properties
63.
* String for the description, and a floating point
64.
* integer (single precision) for the price. Notice that the inventory
65.
* identifier is not sent to external clients.
66.
*/
67.
public
void
writeExternal(ObjectOutput out)
throws
IOException {
68.
// Write out the client properties from the server representation
69.
out.writeObject(name);
70.
out.writeObject(properties);
71.
out.writeFloat(price);
72.
}
73.
74.
private
static
String lookupInventoryId(String name,
float
price) {
75.
String inventoryId =
"X"
+ name + Math.rint(price);
76.
return
inventoryId;
77.
}
78.
}
위 코드는 ActionScript의 flash.utils.IExternalizable 인터페이스와 Java의 java.io.Externalizable 인터페이스를 이용해 기본직렬화를 무시하고 이들 인터페이스에 정의된 readExternal와 writeExternal 메소드를 호출하여 직렬화 자체를 커스터마이징 할 수 있다는 것을 의미한다. Externalizable 인터페이스를 구현한 클래스에 정의된 메소드가 기본 직렬화보다 우선순위가 높다는 것을 기억하면 되겠다.
Flex의 ArrayCollection을 다시 한번 보기 바란다. 이 클래스는 flash.utils.IExternalizable를 구현했다.
mx.collections.ArrayCollection
꼭 이런 경우만은 아니다. 쓸데없는 데이터의 크기를 줄이기 위한 방법도 해당한다. 예를들어 ActionScript 코드를 보면 다음과 같다.
01.
class
Example
implements
IExternalizable {
02.
03.
public
var
one:
Boolean
;
04.
public
var
two:
Boolean
;
05.
public
var
three:
Boolean
;
06.
public
var
four:
Boolean
;
07.
public
var
five:
Boolean
;
08.
public
var
six:
Boolean
;
09.
public
var
seven:
Boolean
;
10.
public
var
eight:
Boolean
;
11.
public
function
writeExternal(output:IDataOutput) {
12.
var
flag:
int
=
0
;
13.
if
(one) flag |=
1
;
14.
if
(two) flag |=
2
;
15.
if
(three) flag |=
4
;
16.
if
(four) flag |=
8
;
17.
if
(five) flag |=
16
;
18.
if
(six) flag |=
32
;
19.
if
(seven) flag |=
64
;
20.
if
(eight) flag |=
128
;
21.
output.writeByte(flag);
22.
}
23.
public
function
readExternal(input:IDataInput) {
24.
var
flag:
int
= input.readByte();
25.
one = (flag &
1
) !=
0
;
26.
two = (flag &
2
) !=
0
;
27.
three = (flag &
4
) !=
0
;
28.
four = (flag &
8
) !=
0
;
29.
five = (flag &
16
) !=
0
;
30.
six = (flag &
32
) !=
0
;
31.
seven = (flag &
64
) !=
0
;
32.
eight = (flag &
128
) !=
0
;
33.
}
34.
}
데이터 통신을 위해 쓸데없이 Boolean객체를 주고 받을 필요없다. 위 코드처럼 직렬화를 커스터마이징한다면 송수신 데이터 자체의 크기도 줄일 수 있다. 이는 매우 유용하다.
참고글
flash.utils.IExternalizable
커스텀 직렬화의 사용
ActionScript 3.0 데이터 유형 및 직렬화(serialization)
Serializing between ActionScript and Java
AMF 3 스팩
AS3 BitmapData AMF solution using IExternalizable
Flex and PHP: remoting with Zend AMF
글쓴이 : 지돌스타(http://blog.jidolstar.com/644)
2010년 12월 14일 화요일
[펌]GlassFish와 Tomcat 비교
에너지 효율적 데이터센터 구축을 위한 팁 10가지
애플리케이션의 성능, 사용 용이성, 민첩성을 자세히 비교해 수 있는 방법을 알려 드립니다.
|
하지만 IT 관리 측면에서 '회사가 어떤 애플리케이션 서버 기술을 사용하고 있는가' 에 대해 의문을 던지는 CEO는 거의 찾아보기 힘듭니다. 이는 상당히 중요한 사안이라고 볼 수 있는데, 왜냐하면 개발팀이 사용하는 애플리케이션 서버와 기업의 애플리케이션 성능/민첩성 간에는 직접적인 연관성이 있기 때문입니다.
좀 더 구체적으로 설명하자면, 애플리케이션 서버에서 사용되는 웹 컨테이너 기술은 애플리케이션의 품질과 개발자의 생산성을 결정짓는 중요한 요소입니다. 결국 웹 컨테이너 기술을 올바로 도입한다면 개발팀의 업무 효율이 향상되고 고성능 애플리케이션을 신속하게 개발할 수 있는 효과가 있겠지만, 선택을 잘못 할 경우에는 이 자체가 애물단지로 전락할 공산이 커집니다.
따라서 여러분의 비즈니스에서 애플리케이션이 중추적 역할을 담당하고 있다면 애플리케이션 서버, 웹 컨테이너 기술은 물론, 이들이 개발팀에 어떠한 영향을 미치는지 세심하게 조사해봐야 합니다. 이에 본 기사에서는 현재 널리 애용되고 있는 오픈 소스 옵션인 GlassFish와 Tomcat를 비교해 보는 시간을 마련했습니다.
많은 차이점을 보이는 두 가지 인기 옵션
GlassFish 커뮤니티가 제공하는 오픈 소스 애플리케이션 서버 GlassFish는 2005년 썬을 통해 선보인 이래로 개발자들 사이에서 큰 인기를 끌고 있습니다. GlassFish는 현재 두 가지 버전의 보급판(GlassFish v2와 새로 발표된 Glassfish v3 Prelude)이 시중에 나와 있으며, 그 중 GlassFish v2는 현재까지 다운로드 횟수가 약 9백만 카피에 달하며 2009년 한 해에만 이미 30만 개의 제품이 등록되었습니다. 여기에 신기능을 추가하고 기존 기능을 더욱 강화한 Glassfish v3 Prelude는 Java나 동적 언어(jRuby 등) 기반의 리치 인터넷 애플리케이션 개발에 더할 나위 없는 최고의 플랫폼으로 평가 받고 있습니다.GlassFish는 웹 컨테이너의 일종인 Java EE 컨테이너에 속해 있는 반면, Tomcat은 그 자체로 웹 컨테이너를 구성하고 있습니다. 그리고 이러한 두드러진 차이점은 GlassFish에 여러 이점을 가져다 줍니다.
일반적으로, Tomcat 상에서 구동되는 애플리케이션은 GlassFish에서도 그대로 사용이 가능하나, 두 제품 간에는 애플리케이션의 성능, 확장성, 사용성 및 개발자 생산성에 영향을 미칠 수 있는 중요한 차이점이 있습니다.
이러한 차이점을 이해하기 위해서는 먼저 웹 컨테이너 기술에 대해 살펴보는 것이 중요합니다. 애플리케이션 서버에 속하는 웹 컨테이너는 서블릿, JSP(JavaServer Pages), 기타 웹 티어 컴포넌트의 관리를 담당합니다.
GlassFish는 웹 컨테이너의 일종인 Java EE 컨테이너에 속해 있는 반면, Tomcat은 그 자체로 웹 컨테이너를 구성하고 있습니다. 그리고 이러한 두드러진 차이점은 GlassFish에 여러 이점을 가져다 줍니다.
- 간편한 마이그레이션. GlassFish v2를 이용하면 Enterprise Java Beans(EJBs), Java Persistence API(JPA), Java Message Service(JMS) 등의 이점을 확실하게 누릴 수 있습니다. 반면에 Tomcat의 경우 상기의 기술을 개별적으로 추가해 줘야 하는 번거로움이 있으며, 개발자는 그 기능을 일일이 구현해서 이들이 제대로 돌아가도록 해야 합니다.
- 향상된 클러스터링 및 페일오버 지원. GlassFish v2가 제공하는 클러스터링과 정교한 고가용성 기능은 애플리케이션이 엄격한 엔터프라이즈급 SLA(Service Level Agreement)를 충족할 수 있도록 설계되었습니다. GlassFish v3 Prelude는 아직 클러스터링 프로파일이 적용되지 않은 관계로 로드 밸런서를 통해 클러스터링을 지원하고 있습니다.
- 강력한 관리/모니터링 기능. GlassFish v2와 v3 Prelude 모두 관리 콘솔과 Command Line Interface(CLI)를 이용한 중앙식 관리 방식을 지원하고 있고, 특히 GlassFish v2에서 제공하는 Callflow Monitoring 기능은 애플리케이션 개발자 또는 서버 관리자가 애플리케이션이 가장 많이 사용되는 영역을 파악할 수 있도록 도와줍니다(이 기능은 GlassFish v3에서도 지원됨). 또한 여타 벤더들도 GlassFish Update Center를 통해 GlassFish를 간편하게 설치하고 각자의 소프트웨어를 활용할 수 있습니다. 반면에 Tomcat의 경우 새로운 소프트웨어 사용 시 이를 일일이 구성해주어야 하는 불편함이 있습니다. Update Center는 또한 EJB 3.1 같은 최신 기술을 손쉽게 액세스할 수 있게 해주므로 애플리케이션을 EAR 파일로 패키징하는 대신 EJB를 WAR로 번들링하는 것이 가능합니다.
- 다양한 스크립팅 언어 지원. GlassFish는 Ruby/JRuby, Python/Jython, Groovy, PHP, JavaScript/Phobos, Scala 같은 다양한 언어들을 지원합니다(또는 지원 예정).
본격 비교: 웹 컨테이너와 관련한 또 다른 차이점
앞서 언급한 일반적인 이점 외에도 GlassFish이 Tomcat에 비해 두드러지는 점은 바로 웹 컨테이너 기능입니다. 몇 가지 예를 들면 다음과 같습니다.- v3 Prelude의 애플리케이션 재배치 시 세션 유지 능력은 개발자가 보다 단기간에 Java 웹 애플리케이션을 개발할 수 있도록 도와줍니다.
- GlassFish v2/v3 Prelude는 서버를 재시작하지 않고 가상 서버와 HTTP Listener를 동적으로 재구성할 수 있게 해주는 반면, Tomcat에서는 리소스 풀을 변경할 경우 보통 애플리케이션 서버를 재시작해야 합니다.
- v2와 v3 Prelude의 고성능·고확장 Grizzly Framework가 요청/응답 쓰루풋을 극대화합니다. 또한 GlassFish의 하위 웹 티어 레이어가 이 Grizzly Framework을 통해 구현되는데, Java로 제작된 이 프레임워크는 NIO API(확장 네트워크 및 파일 I/O)의 이점을 최대한 활용함으로써 높은 확장성 뿐 아니라 고도의 커스터마이징 능력까지 제공합니다.
- GlassFish v2와 v3 Prelude는 다양한 성능 최적화 기능을 제공합니다. 그 중 하나로 각 밸브의 호출 방식을 간소화함으로써 Stack Depth를 제한하고 성능을 개선해 주는 밸브 아키텍처 수정판인 "Flattened Valve Invocation"을 들 수 있습니다. 또한 GlassFish v3 Prelude은 Tomcat 스타일의 밸브도 지원합니다.
GLASSFISH | TOMCAT | |
---|---|---|
작업/초 | 6988.9 | 6615.3 |
평균 응답 시간 | 0.242 | 0.358 |
최대 응답 시간 | 1.519 | 3.693 |
90% 응답 시간 | 0.6 | 0.75 |
올바른 제품 선택을 위해서는
애플리케이션 서버는 개발팀은 물론이고 기업 운영의 많은 부분에까지 영향을 미치는 중요한 요소입니다. 따라서 애플리케이션 서버 선택은 그 중요도 면에서 고도의 전략적 비즈니스 의사결정을 내리는 것과 맞먹는다고 할 수 있습니다. 이에 우리 썬은 GlassFish가 차세대 애플리케이션을 개발하는 개발자들에게 있어 최상의 옵션이 될 것이라고 자신 있게 주장하는 바이지만, 동시에 다른 대안에 대해서도 신중하게 검토해볼 것을 권하고 싶습니다. GlassFish와 Tomcat에 대한 추가 정보 및 비교 자료를 원하시면 썬 백서를 참조하십시오.
피드 구독하기:
글 (Atom)