Seam이란?
JBoss Seam은 J2EE 5.0에 대한 lightweight 프레임웍입니다. 이것의 의미는 무엇일까요? J2EE 5.0 자체가 "프레임웍"들의 모임아닙니까? J2EE 표준이외에 또 다른 것이 왜 필요할까요?
JBoss Seam은 J2EE 5.0에서 간과한 프레임웍이라고 할 수 있습니다. Seam은 J2EE 5.0 프레임웍 위에서 엔터프라이즈 웹 어플리케이션의 모든 컴포넌트에 대한 일관적이며, 이해하기 쉬운 프로그래밍 모델을 제공합니다.
또한 Stateful 어플리케이션과 프로세스-기반 어플리케이션 개발이 쉽도록 합니다. 다시 말해, Seam은 개발자의 생산성과 어플리케이션 확장성을 위한 것입니다.
1. J2EE 프레임웍의 통합과 확장
J2EE 5.0의 중요 프레임웍은 EJB(Enterprise JavaBeans) 3.0과 JSF(JavaServer Faces) 1.2 입니다. EJB 3.0은 POJO(Plain Old Java Objects)기반의 비지니스 서비스와 데이터베이스 저장을 위한 lightweight 프레임웍입니다.
JSF는 웹 어플리케이션을 위한 MVC(Model-View-Controller) 컴포넌트입니다. 대부분의 J2EE 5.0 웹 어플리케이션은 비지니스 로직을 위한 EJB3 모듈과 사용자 인터페이스를 위한 JSF 모듈로 구성되게 될 것입니다. EJB3와 JSF가 서로 에 대해 보완하는 역할을 하지만, 각각 다른 철학에 따라 각자 설계되어진 프레임웍입니다. 예를 들면, EJB3는 서비스를 구성하기 위해 주석(annotation)을 사용합니다. 반면에 JSF는 XML파일들을 사용합니다. 게다가 EJB3와 JSF 컴포넌트는 프레임웍 레벨에서 서로에 대해 인식하지 못합니다. EJB3와 JSF를 함께 사용할 수 있게 하려면, 비지니스 컴포넌트를 웹페이지에 연결시켜주는 JSF backing Beans같은 facade 객체들과 프레임웍에 걸쳐서 메소드콜을 하는 판에박힌 코드들을을 만들어야 합니다.
EJB3와 JSF같은 기술들을 결합시키는 것이 Seam의 목표 중에 하나입니다. Seam은 일관된 주석(annotaion)기반의 방식으로 EJB3와 JSF를 연결합니다. EJB3에 간단한 몇개의 주석을 추가하여 Seam에서 EJB3 비지니스 컴포넌트를 JSF 웹 폼에서 직접사용하거나 웹 UI 이벤트를 처리할 수 있게 됩니다. Seam은 EJB3 뿐만 아니라 같은 방식으로 POJO에 주석을 달거나, 어플리케이션 컴포넌트에 대해서 사용할 수 있습니다. 다른 웹 프레임웍으로 개발한 어플리케이션과 비교할 때, Seam 어플리케이션은 같은 기능에 대해, 단순하고 적은 양의 Java/XML 코드가 필요합니다.
Seam은 JSF에서 하기 힘들었던 일들을 쉽게 할 수 있도록 합니다. 예를 들면, JSF의 주요 불만중의 하나는 HTTP POST에 너무 많이 의존한다는 것인데, 이렇케 하면 JSF 웹 페이지를 북마크하기하기 힘들어집니다. Seam에서는 북마크가 가능한 RESTful 웹페이지를 쉽게 생성해낼 수 있습니다. Seam은 또 JSF 어플리케이션을 만드는데 필요한 많은 JSF 컴포넌트 태그와 주석을 제공합니다.
동시에 Seam은 EJB3 컴포넌트 모델을 POJO로, Stateful 컨텍스트를 웹 계층에서 비지니스 컴포넌트까지 사용할수 있게 합니다. 게다가 Seam 은 jBPM, JBoss Rules(Drools), JBoss Portal, JBoss Microcontainer등 뛰어난 오픈소스 프레임웍들을 통합합니다. Seam은 단순히 이런 프레임웍을 연결하는 것이 아니라 JSF + EJB3 결합과 같은 방식으로 프레임웍을 확장합니다.
비록 Seam이 J2EE 5.0에 기반하였지만, J2EE 5.0 서버에서만 국한되어 사용되는 것은 아닙니다. Seam 어플리케이션은 Tomcat과 같은 J2EE 1.4 어플리케이션 서버에서도 사용할 수 있습니다.
1 + 1 > 2
여러가지 프레임웍들을 연결하는 또 다른 통합 프레임웍으로 생각하는 것은 Seam을 잘못 이해하는 것입니다. Seam은 주석, EL(Expression 언어) 표현식등을 통해서 프레임웍들을 긴밀히 연결할 수 있게 하는 프레임웍으로 자신의 Stateful 컨텍스트를 관리하고 있습니다. 이런 통합 수준은 Seam개발자들의 다른 프레임웍들에 대한 전문 지식이 있기에 가능했던 것입니다.
2. ORM을 이해하는 웹 프레임웍
객체 관계 매핑(ORM) 솔루션들은 현재 엔터프라이즈 어플리케이션에 널리 사용되고 있습니다. 그러나, 대부분의 현재 비지니스나 웹 프레임웍은 ORM이 설계에 고려되지 않고 있습니다. 대부분의 프레임웍들은 요청이 들어오는 것부터 결과가 완전히 렌더링 될때 까지의 전체 웹 상호작용(interaction)동안 persistence 컨텍스트를 관리하지 않습니다. 그결과 지긋지긋하게 보는 LazyInitializationException을 포함해서 다양한 종류의 ORM에러들을 발생시킵니다. 그래서 "데이터 전송 객체(DTO)"같은 이상한 방식을 사용하게 합니다.
Seam은 세상에서 가장 유명한 ORM 솔루션중 하나인 Hibernate를 만든 Gavin King에 의해서 만들어졌습니다. Seam은 기반부터 ORM의 Best Practice를 사용하도록 장려하게 설계되었습니다. Seam을 사용하면, DTO같은 객체를 작성할 필요가 없으며, lazy 로딩도 잘 작동합니다. 그리고 persistence 컨텍스트가 데이터베이스까지 가는 횟수를 줄이는 캐시처럼 동작하여 ORM 성능이 매우 향상될 수 있습니다.
또한, Seam이 ORM 계층과 비지니즈, 프리젠테이션 계층을 통합하기 때문에, ORM 객체를 직접 표시할 수 있고, 데이터베이스 검사 주석(validator annotation)을 입력 폼에 사용할 수 있고, ORM 에러를 사용자 에러 페이지로 redirect할 수 있습니다.
3. Stateful 웹 어플리케이션을 위한 설계
Seam은 Stateful 웹 어플리케이션을 위해 설계되었습니다. 웹 어플리케이션은 본래 멀티-사용자 어플리케이션이며, 전자상거래 어플리케이션은 본래 stateful이며 트랜잭션인 특성이 있습니다. 그러나, 대부분의 웹 어플리케이션 프레임웍들은 기본적으로 stateless 에플리케이션을 만들도록 하고 있습니다. 사용자 상태를 관리하려면 HTTP 세션 객체로 직접 작업해야 했습니다. 이런 작업은 핵심 비지니스 로직과는 상관없는 일이며, 성능이슈도 일으킬 수 있습니다.
Seam에서는 모든 기본 어플리케이션 컴포넌트들이 본래 stateful 입니다. 명시적으로 Seam에서 상태가 관리되기 때문에 HTTP 세션을 사용하는 것 보다 훨씬 편리합니다. 복잡한 상태 관리 코드를 작성할 필요가 없습니다. 범위, 생명주기(lifecycle) 메소드, 다른 stateful 프로퍼티에 주석을 사용하기만 하면 Seam이 나머지는 처리합니다. Seam의 Stateful 컴포넌트는 일반 HTTP 세션보다 더 상세한 상태를 관리해 줍니다. 예를 들어 HTTP 세션내에 일련의 웹 요청과 비지니스 메소드 호출로 구성되는 여러 개의 "conversations"을 가질 수 있습니다.
또, 데이터베이스 캐시와 트랜잭션은 어플리케이션 상태와 연결됩니다. Seam은 자동으로 데이터베이스 update를 메모리에 가지고 있다가, conversation이 끝나면 데이터베이스에 commit 합니다. 메모리내 캐시는 복잡한 stateful 어플리케이션의 데이터베이스 부하를 상당히 줄여줍니다.
웹 어플리케이션내의 상태관리는 오픈소스 JBoss jBPM 비지니스 프로세스 엔진과 연결을 지원함으로 큰 진전 있습니다. 조직내의 다른 사람들의 워크 플로우를 지정할 수 있거나, UI 이벤트 핸들러와 데이터베이스에만 의존하지 않고 어플리케이션이 진행하는데 워크플로우를 이용할 수 있습니다.
4. 웹 2.0 지원
Seam은 웹 2.0 스타일 어플리케이션에 최적화되어 있습니다. AJAX(Asynchronous JavaScript And XML) 기술을 다양한 방법으로 지원합니다. JavaScript없는 AJAX 컴포넌트, AJAX가능한 기존 JSF 컴포넌트, JavaScript 객체로 브라우저에서 Seam 서버 컴포넌트로 직접 접근할 수 있는 사용자 JavaScript 라이브러리를 사용할 수 있습니다. 내부적으로 Seam은 같은 사용자로 부터의 여러개의 AJAX 요청을 효율적으로 관리할 수 있는 진보된 동시처리 모델을 제공합니다.
AJAX 어플리케이션의 큰 도전은 데이터베이스 부하의 증가입니다. AJAX 어플리케이션은 일반 어플리케이션 보다 서버에 훨씬 더 자주 요청을 보냅니다. 이런 AJAX 요청을 데이터베이스에서 처리해야만 한다면, 데이터베이스가 이런 부하를 감당할 수 없을 지 모릅니다. Seam의 Stateful 저장 컨텍스트는 메모리내 캐시로 동작합니다. 오랜시간 지속되는 conversation 정보를 보관하여 데이터베이스까지 가는 것을 줄일 수 있습니다.
웹 2.0 어플리케이션은 그 데이터 측면에서 복잡한 관계 모델을 사용하는 경향이 있습니다. 예를 들어 소셜네트워크 사이트는 "사용자"간의 관계 정보를 관리하고 표현해 주는 일을 합니다. 이런 사이트에 대해서는 ORM 계층의 lazy 로딩은 결정적인 것입니다. 안그러면, 하나의 쿼리가 전체 데이터베이스를 단계적으로 로딩하는 결과를 낳을 수 있습니다.
Seam은 웹 어플리케이션에 대한 lazy 로딩을 정확히 지원하는 유일한 웹 프레임웍이라고 할 수 있습니다.
5. Dependency Bijection을 통한 POJO서비스 사용
Seam은 POJO(Plain Old Java Objects)를 서비스 컴포넌트로 사용할 수 있기 때문에 더욱 "lightweight 프레임웍"이라고 할 수 있습니다. 어플리케이션에 컴포넌트를 포함시키기 위한 프레임웍 인터페이스나 추상 클래스가 필요하지 않습니다. 물론, 이 POJO가 어플리케이션을 구성하기 위해 어떻게 서로 상호작용하는지 질문이 있을 것입니다. 어떻케 데이터베이스 저장(persistence) 서비스와 같은 컨테이너 서비스와 상호작용할 수 있을까요? Seam은 POJO 컴포넌트들을 잘 알려진 "Dependency Injection(DI)"라는 디자인 패턴을 사용하여 연결합니다. 이 패턴을 사용하여 Seam 프레임웍은 모든 컴포넌트의 생명주기(lifecycle)을 관리할 수 있습니다. 컴포넌트가 다른 것을 사용하고 싶으면, 주석을 사용하여 Seam에 dependency을 선언합니다. 그러면, Seam은 현재 어플리케이션 상태에서 의존하는 컴포넌트를 어디서 가져올 지를 판단하고, 요청한 컴포넌트에 "injects(주입)"해 줍니다.
Dependency injection 개념을 확장하여, Seam 컴포넌트 A는 또 다른 컴포넌트 B를 생성할 수 있으며, 생성된 컴포넌트 B를 컴포넌트 C 같은 다음에 사용할 다른 컴포넌트를 위해 Seam에게 "outjects"해 줍니다.
이런 형태의 양방향 dependency 관리는 가장 간단한 Seam 웹 어플리케이션이더라도 널리 사용됩니다.
Seam에서는 이것을 "Dependency bijection"이라고 합니다.
6. 예외로 설정(Configuration by Exception)
주요 설계 원칙중에 Seam을 사용하기 편리하게 하는 것이 "예외로 설정"하는 것 입니다. 아이디어는 컴포넌트에 대한 기본 동작 집합을 가지고 있는 것입니다. 개발자는 원하는 기본 동작을 하지 않을 때에만 명시적으로 컴포넌트를 설정하면 됩니다. 예를 들어, 컴포넌트 A가 컴포넌트 B의 프로퍼티를 inject한다면, 컴포넌트 A의 Seam 이름은 받는 컴포넌트 B의 프로퍼티가 기본값이 됩니다.
Seam에는 이런 사소하지만 편리한 것들이 많습니다. 전반적으로 Seam의 메타데이터 설정은 다른 Java 프레임웍과 비교하여 훨씬 단순해집니다. 결과로 대부분의 Seam 어플리케이션은 적은 개수의 Java 주석으로 충분히 설정될 수 있게 됩니다. 개발자는 복잡성이 감소하고, 결론적으로 다른 프레임웍에서 개발한 같은 기능과 비교하면 훨씬 더 작은 양의 코딩만 하면 됩니다.
7. XML 남용을 피한다
이미 설명했던 것처럼, Java 주석은 Seam 설정 메타데이터를 표현하고 관리하는데 중요한 역할을 합니다. 설계상에 프레임웍을 사용하기 편리하게 만들어 줍니다.
J2EE의 초기엔 XML은 설정 관리을 위한 "성배(holy grail)"로 여겨졌습니다. 프레임웍 설계자들은 Java 클래스와 메소드이름 등 모든 종류의 설정 정보를 개발자에게 돌아갈 일에 대해 많은 생각없이 XML파일에 넣었습니다. 돌아보면 이것은 큰 실수라고 할 수 있습니다. XML설정은 매우 반복되는 작업입니다. 개발자들은 이미 코드에 있는 정보를 반복적으로 코드와 설정을 연결하기 위해 사용해야 했습니다. 이런 반복작업은 에플리케이션에서 사소한 에러들을 만드는 경향이 있습니다. 설정파일의 클래스 이름 타이핑 오류는 Runtime시 매우 디버그하기 힘든 오류로 나타납니다.
적당한 기본 설정이 부족한 것도 이런 문제들 더 복잡하게 합니다. 사실, 어떤 프레임웍에서는 상당한 양의 틀에 박힌 XML 코드들이 실제 Java 코드와 비슷하거나 더 많을 수도 있습니다. J2EE 개발자들은 이런 XML의 남용을 "XML hell"이라고 말합니다.
엔터프라이즈 Java 커뮤니티는 XML 남용을 인식하고, XML 파일들을 Java 소스내의 주석으로 성공적으로 대체하고 있습니다. EJB3는 공식적인 Java 표준화 기구의 Java 엔터프라이즈 Java 컴포넌트에서 주석을 장려하는 노력의 성과입니다. EJB3에서 XML 파일들을 사용하는 것은 선택사항입니다. 이런 작업은 확실히 올바른 방향으로 나아가는 것이라 생각합니다. Seam은 EJB3 주석에 추가하여 주석-기반 프로그래밍 모델을 전체 웹 어플리케이션으로 확장합니다.
물론, XML은 설정 데이터에 대해 전적으로 나쁜 것 많은 아닙니다. Seam 설계자들은 XML을 웹 어플리케이션의 페이지 플로우나 비지니스 프로세스의 워크 플로우를 정의하는데 사용하고 있습니다. 반대로 Java 소스내에 분산되어 있는 정보들을 XML파일에서 전체 어플리케이션의 워크 플로우를 중앙에서 관리하게 합니다. 워크플로우 정보는 Java 소스코드와 덜 연결되도록 합니다. 그래서 XML 파일에는 소스 코드에 이미 있는 정보를 중복해서 입력하지 않도록 합니다.
8. 테스트를 위한 설계
Seam은 기반부터 쉽게 테스트할 수 있도록 설계되어 있습니다. Seam의 모든 컴포넌트는 주석붙인 POJO이기 때문에 Unit 테스트를 실행하기 매우 쉽습니다. POJO 인스턴스를 Java의 new 키워드를 이용하셔 생성하고 JUnit, TestNG같은 테스팅 프레임웍에서 어떤 메소드나 실행하면 됩니다. 여러 개의 Seam 컴포넌트들 간에 상호작용을 테스트하고 싶다면, 각각의 컴포넌트를 초기화하고 각 컴포넌트 간의 관계를 직접 설정해 주면 됩니다. Seam의 Dependency Injection기능에서 사용하는 것 대신에 setter 메소드를 사용하시면 됩니다.
전체 Seam 어플리케이션의 통합 테스팅을 위해선, Seam 컨테이너내에서 어플리케이션을 실행해야 하기 때문에 좀더 복잡해집니다. Seam은 임베딩가능한 lightweight 컨테이너를 제공하여 이런 형태의 테스팅을 도와줍니다.
사용하시는 테스트 프레임웍에서 Seam 컨테이너를 프로그램에서 로딩하고 테스팅을 실행하시면 됩니다.
9. 개발툴 지원
개발툴 지원은 어플리케이션 프레임웍에 대해선 개발 생산성 측면에서 필수적인 것입니다. Seam은 명령행 어플리케이션 생성기인 "Seam Gen"이 포함되어 배포됩니다. Seam Gen은 Ruby-On-Rails의 툴과 매우 유사합니다. 데이터베이스에서 완전한 CRUD 어플리키에션을 생상할 수 있는 기능을 제공합니다. "수정 /저장 / 리스트", 테스팅 지원 등 웹 어플리케이션 개발을 빠르게 합니다.
더 중요한 것은 Seam Gen은 널리 사용되는 Java IDE인 이클립스와 넷빈즈에서 사용할 수 있습니다. Seam Gen으로 쉽게 Seam을 시작할 수 있습니다.
또, JBoss Tools 프로젝트에서 이클립스에서 사용가능한 Seam 개발툴들을 제공합니다.
10. NO벤더 Lock-in
Seam은 J2EE 어플리케이션 개발자의 오버헤드를 단순화 할 수 있고, 동시에 J2EE 5.0에 새로운 기능을 추가합니다. 그러면, Seam은 JBoss에서만 동작하는 특별한 프레임웍일까 궁금증을 갖게 됩니다.
Seam은 어떤 WAS에서나 동작할 수 있습니다. JBoss 뿐만 아니라, WebLogic, WebSphere, Glassfish 같은 WAS에서도 사용하실 수 있습니다. 뿐만 아니라 EJB3 때문에 Tomcat에서는 사용하지 못할 것이라 생각하실 수 있지만, JBoss의 Embedded JBoss기능을 이용하면 Tomcat에서도 JBoss Seam을 사용하실 수 있습니다.
이미지 출처: http://angelwirecreative.com/images/gnu_gpl_logo.jpg
JBoss 5.0의 포트 기준이 바뀌었습니다.
기존에서는 /jboss-eap-4.3/jboss-as/docs/examples/binding-manager/sample-bindings.xml 파일을 기준으로 conf/jboss-service.xml파일을 수정하여 변경했었습니다. 이 파일의 경우 단지 4개의 포트를 추가할 수밖에 없었고, 새로운 포트를 추가하려면 사용자가 부가적인 작업을 해줬어야 했습니다.
5.0에서는 설정 파일도 달라졌으며, offset을 이용한 port 변경 방식이 도입되었습니다.
$SERVER_HOME/conf/bootstrap 디렉토리를 보게되면 bindings.xml 파일이 존재합니다.
위 파일을 열어보면 다음과 같은 tag가 설정되어 있습니다. 붉은 색으로 나타난 첫번째 파라미터인 "ports-default'가 사용됨을 의미하는데 "${jboss.service.binding.set:ports-default}"는 JMX에 등록된 서비스 이름입니다.
위의 port-default 값은 아래쪽의 xml 설정을 보시면 다음과 같이 정의되어 있습니다.
위에서 중요한 태그는 'ports-default'라는 파라미터 이름과 붉은 부분으로 표시된 offset 값입니다. 기본 port는 RMI 1099, Web 8080, AJP 8009로 시작하며 실제 Ports01Bindings의 경우 기본 offset에 100값을 더한 값으로 세팅되어 있습니다.(bindings.xml 참조). 즉 ports-01을 사용하게 되면 RMI는 1199, Web 8180, AJP 8109 와 같은 형식으로 포트를 변경하게 됩니다.
Offset 파라미터를 조정함으로써 수십개의 포트를 손쉽게 추가할 수 있는 기능이 마련되었습니다.
JBoss 5.0의 경우 설정 방식이나 디렉토리 구조가 조금 바뀌었습니다.
기존 방식에서는 $SERVER_HOME/deploy/jboss-web.deployer 파일을 이용하여 포트를 변경하였으나, 5.0에서 포트를 변경하기 위해서는 변경된 $SERVER_HOME/deploy/jbossweb.sar 디렉토리 하위의 server.xml파일을 변경하시면 됩니다.
<!-- A HTTP/1.1 Connector on port 8080 -->
<Connector protocol="HTTP/1.1" port="8080" address="${jboss.bind.address}"
connectionTimeout="20000" redirectPort="8443" />
<!-- Add this option to the connector to avoid problems with
.NET clients that don't implement HTTP/1.1 correctly
restrictedUserAgents="^.*MS Web Services Client Protocol 1.1.4322.*$"
-->
<!-- A AJP 1.3 Connector on port 8009 -->
<Connector protocol="AJP/1.3" port="8009" address="${jboss.bind.address}"
redirectPort="8443" />
필요에 따라 AJP Connector도 변경하시면 됩니다.