태터데스크 관리자

도움말
닫기
적용하기   첫페이지 만들기

태터데스크 메시지

저장하였습니다.

티스토리 툴바


"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
오랜만에 Javascript 코딩해 보려는데 참 힘드네요. javascript로 컴포넌트 만들고 컴포넌트내에서 ajax 호출시 this.someFunction 식으로 callback 넘겨주는데 scope가 달라져 someFunction 내에서 this 접근이 되지 않습니다. ㅜㅜ 이러면 메소드 못나누고 내가 만든 클래스 못쓰게 되는 사태가 발생!!!
Ext.js에서는 이런 경우를 위해 scope을 지정하는 옵션이 있었는데 쩝~
(이미지출처)

구글신 소환해봅니다. 역시 같은 문제로 고생하는 분 많았네요.
그 중 제 맘에 꼭 드는 솔루션을 제시한 블로그가 있어 소개합니다.

Go! Workaround for jQuery's lask of scope management in event / AJAX handlers


대략적인 내용

글쓴이도 저와 같은 상황으로 고생했고 다음과 같이 scope function 정의해서 간단히 해결했다는 내용입니다.
제 코드는 좀 길고 복잡해서 원문 코드 그대로 사용합니다. 이런 생각을 왜 못했을까?

아래와 같이 scope function 정의(overload)하고...


아래와 같이 쓰면 this 스코드 그대로 가지고 가는 callback function을 넘길 수 있다는 이야기입니다.


이렇게 하지 않으면 위의 element 클릭해서 handeClick function이 호출되더라도 scope 바뀌어 this.doStuff()하면 정의되지 않은 function이라고 에러 나옵니다.

scope를 유지하고 싶다면 그냥 호출하지 말고 아래 코드를 이용하여 넘기세요~


Thanks Kier Darby.

저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
우리는 Spring + Jersey (JAX-RS JSR311 구현체) 프레임워크를 이용하여 웹 애플리션을 구현하고 있습니다. JSR311에 충실한 웹프레임워크라 필요한 기능들은 좀 추가해야 합니다.
Flash scope를 지원하는 프레임워크도 있는데 우리가 쓰는 프레임워크에서는 지원하지 않아 추가해봅니다.
(이미지출처)


Flash 범위란?

기본 Java 웹 애플리케이션에서는 값을 전달하는 방법으로 page, request, session 그리고 application을 지원합니다. 그런데 웹 애플리케이션을 작성하다보면 페이지 리로드(F5)에 의해 여러 번 submit이 되는 것을 방지하기 위해 post/redirect/get 설계방식을 사용합니다. 



1. POST: 등록을 위해 post로 submit 한다.

2. Redirect: 조회페이지로 리다이렉트한다.

3. GET: 브라우저는 GET 방식으로 리다이렉트하는 페이지를 조회한다.


여기서 1.POST3.GET은 서로 다른 요청이기에 이 요청사이의 연결은 이어지지 않습니다. 그런데 등록 후 조회페이지에서 "등록되었습니다."라는 메시지를 출력하고 싶은 경우가 있습니다. Java 웹 애플리케이션에서 기본적으로 제공하는 scope이나 장치를 쓰면.

1. 우선 page, request로는 메시지를 다음 요청으로 넘길 수 없습니다.

2. parameter로 메시지를 넘기는 방법:

   긴 메시지가 URL에 나타나고 페이지 리로드(F5)하면 메시지가 또 출력됩니다.
   사용자가 혼란스러워 합니다.

3. session scope를 이용하면:

   session에 저장된 메시지를 매번 삭제해야 하고 혹 남아 있을 경우 오작동을 할 가능성이 있습니다.

   session 사용은 restful한 서비스 작성에 좋지 않을 뿐더러 전역변수 같은 성격이 있어 좋지 않습니다.

4. application scope??:

   이 놈은 전체가 공유하는 놈이라 쓸 수 없습니다. A 사용자의 메시지가 B 사용자에게도 보일 수 있습니다.
   -.-;


Rails나 Grails 같은 프레임워크는 이러한 경우를 위해 바로 다음 요청까지만 유효한 flash scope를 지원합니다.


구현방법

서블릿 필터를 이용하여 쉽게 구현할 수 있습니다. 구동방식을 아래와 같습니다.

POST

1-1. 먼저 Action 클래스같은 비즈니스 로직에서 등록 후 request scope에 "flash."로 시작하는 키값으로
      메시지를 저장하고 조회페이지로 redirect 합니다.

1-2. 서블릿 필터에서는 "flash."로 시작하는 값이 있을 경우 이를 잠시 session에 넣어둡니다.



Redirect 후 GET

2-1. 서블릿 필터에서는 session에서 위 값을 꺼내고 해당 요청의 request scope에 값을
      "flash." prefix는 제거하고 request scope에 저장합니다.

2-2. 그리고 이 session값은 지웁니다. (이 다음의 요청에서는 사용 못하도록)

2-3. 비즈니스 로직을 실행 후 JSP에서는 request scope에 저장된 메시지를 출력합니다.



예를 들어 등록 후 다음과 같이 메시지를 저장합니다.

다음 요청의 JSP에서는 다음과 같이 메시지를 출력합니다.

서블릿 필터 소스는 아래와 같습니다.


이 소스는 특정 웹 프레임워크와 관계없이 작동합니다. 그대로 사용하거나 본인이 사용하는 프레임워크에 맞게 고쳐 사용할 수 있습니다.


우리의 응용

우리는 비즈니스 로직을 수행하는 Action(Jersey에서는 Resource라고 함)을 POJO로 작성하기에 request 객체 접근이 부자연스럽습니다.(쉽긴 하지만) 그래서 서블릿 필터에서 request 객체를 ThreadLocal에 저장하고 비즈니스 로직에서는 투명하게 메시지를 저장할 수 있도록 변경했습니다. 물론 요청을 빠져 나올 때는 필터에서 ThreadLocal에 저장한 request 객체를 지워야 합니다.

Flash.java

FlashScopeFilter.java

...
if (request instanceof HttpServletRequest) {
 ...
 Flash.setRequest(httpRequest);


chain.doFilter(request, response);

if (request instanceof HttpServletRequest) {
  ...
  Flash.clearRequest();


POST: 등록 후



GET: 다음 요청 JSP에서 메시지 출력은 동일합니다.

참고원문: http://blog.smartkey.co.uk/2011/01/implementing-flash-scope-in-java-web-applications/

 
저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
사용자 삽입 이미지
Strtus2로 개발을 하는데 팀원들이 form을 사용할 때 <s:form>을 제외하고는 그 외의 struts 태그는 사용하지 않는 것을 발견했습니다. struts의 form 관련 태그를 사용하면 입력값 처리와 validation 처리 등 여러가지로 편리한 점이 많은데 왜 사용하지 않는지 이유를 물어보았습니다.


동기

보통 UI는 UI 디자이너가 HTML 작업한 후 UI 개발자가 JSP로 변경하여 필요한 UI 로직 처리를 위한 태그를 삽입합니다. 그런데 아래와 같이 struts2 태그를 작업했더니...
    <tr>
     <td><span id="re">+</span> 회원명</td>
     <td><s:textfield name="name" size="20" maxlength="10"/></td>
    </tr>

테이블이 깨지는 현상이 목격되었습니다. 브라우저 소스 보기를 통해 렌더링된 결과를 살펴보았습니다.
    <tr>
     <td><span id="re">+</span> 회원명</td>
     <td><tr>
    <td class="tdLabel"></td>
    <td
><input type="text" name="name" size="20" maxlength="10" value="" id="save_name"/></td>
</tr>

</td>
    </tr>

스트러츠2의 form 관련 태그들은 친절하게 알아서 자동으로 <table>관련 태그들을 렌더링해 줍니다(이 때 tag의 label속성을 사용하면 태그의 제목(레이블)까지 꾸며서 출력합니다). 그러나 이건 우리가 원하는 결과가 아니었습니다.
이런 자동 태그 렌더링은 다음과 같이 간단히 해결할 수 있습니다.


해결방법

struts2 태그들은 태그를 렌더링할 때 특정 테마를 이용하여 html 태그를 생성합니다. 다음과 같이 simple 테마를 사용하면 별다른 추가 태그 없이 해당 input 태그(select나 다른 것도 동일)만 출력합니다.
    <tr>
     <td><span id="re">+</span> 회원명</td>
     <td><s:textfield name="name" size="20" maxlength="10" theme="simple"/></td>
    </tr>
원하는 데로 아래와 같이 렌더링됩니다.
    <tr>
     <td><span id="re">+</span> 회원명</td>
     <td><input type="text" name="name" size="20" maxlength="10" value="" id="save_name"/></td>
    </tr>


정리

자동화 된 강력한 기능이 가끔 불필요할 때가 있네요.
물론 Strtus2 테마는 개발자가 고유의 것을 만들 수도 있으며 이 테마는 위와 같이 tag 렌더링 방법뿐만 아니라 스트러츠의 validation에서 렌더링하는 client side validation java script도 조정할 수 있습니다.
이를 잘만 활용한다면 일관성 있는 UI를 쉽게 만들 수도 있겠습니다. 이걸 잘 만들려면 UI 디자이너와 많이 논의할 필요가 있겠군요.

뭐 저도 theme을 만들어 본 적은 없고 문서만 읽었을 뿐 아는 지식이 별로 없어 더 이상 할 말은 없습니다. :)

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
스프링 프레임워크가 RC(Release Candidate)에서 벗어나 GA(General Available)로 되었습니다. 이제 거의 확정적이라고 봐도 될 것 같습니다. 물론 뒤에 마이너 버전에서 뭔가 소소히 바뀌겠지만요.
GA 릴리즈 발표가 있고 스프링을 주로 사용하는 사용자로 관심이 많기에 정리해 봤습니다.

원문: http://blog.springsource.com/2009/12/16/spring-framework-3-0-goes-ga/

스프링 프레임워크 3.0 GA 릴리즈

by Juergen Hoeller

긴 여정 끝에, 스프링 3.0 GA(릴리즈)가 마침내 완성된 것을 알리게 된 것을 기쁘게 생각합니다(다운로드 페이지)! 모든 SpringSource는 파티에 참석해 기념하고 있습니다. :)

최근 뉴스에서, 스프링 3.0 GA는 현재 런타임 환경(예: 지난 주 릴리즈된 GlassFish v3)에서 Java EE5와 호환을 하고 최종 JPA 2.0을 지원합니다(EclipseLink 2.0 이용). 또한 컴포넌트 스캐닝을 위해 새로 도입한 @ManagedBean (JSR-250 v1.1)도 지원하고 annotation 기반 dependency injection을 지원하기 위해 @Inject (JSR-330)도 지원합니다.

편의상, 아래에 스프링 3.0 전체의 주요 특징을 요약했습니다.

* Spring expression language (SpEL): 빈을 정의하는데 사용하는 핵심 expression 파서로,  property 값에서 #{...} 문법을 통해  nested된 빈 구조(예: 다른 빈의 속성)와 환경 데이터 구조(예: 시스템 속성 값)를 참조할 수 있습니다.

* Annotation 기반 컴포넌트의 지원 확장: 스프링 JavaConfig로 알려진 configuration 클래스와 annotation 팩토리 메소드 개념입니다. 스프링은 또한 동적 #{...} 표현이나 정적 ${...} 변수(placeholer)을 통해 값을 주입하기 위해 @Value 표현을 사용할 수 있습니다.

* 막강한 스테레오타입 모델: 메터 annotation을 이용해 '축약(shortcut)' annotation을 생성할 수 있습니다. 예를 들어 나만의 기본 스코프, 기본 트랜잭션 특성을 사용자 정의 스테레오타입으로 만든다면, @Service, @Scope("request") 그리고 Transactional(realDonly=true)를 나의 annotation으로 가르키는 @MyService 란 annotation을 생각해 볼 수 있습니다.

* 표준 의존성 주입 annotation: 스프링 3.0은 @Inject 자바 annotation 기반 주입을 위한 JSR-330 스펙을 완벽히 지원합니다. 스프링 고유의 @Autowired 대신 사용하거나 같이 사용할 수 있습니다.

* 제약 annotation을 기반으로한 선언적 모델 검증(validation): Hibernate Validator 4.0과 같은 스프링 스타일의 JSR-303 빈 검증 프로바이더입니다. 스프링 MVC에서도 annotation 으로 검증 옵션을 제공합니다. 스프링의 바인딩 결과 장치를 통해 제약 정합성 검증의 단일화된 뷰를 나타나게 되었습니다.
(역주: 입력값 검사 같은 걸 말합니다. 한글말 찾기 어려워요 ㅠㅠ)

* 향상된 바인딩과 annotation 기반 포맷팅: 컨버터와 포맷터 SPI가 표준 PropertyEditor로 교체됩니다. 포맷팅은 @DateTimeFormat 같이 JSR-303 constraints 비슷한 모습의 annotation으로 됩니다. 또한 스프링 MVC에서 편리한 포맷팅과 검증 설정을 위한 새로운 mvc 네임스페이스도 확인해 보세요.
(역주: SPI - Service Proivider Interface 약자로 인터페이스 스팩이 있고 이 인터페이스에 대한 실체 구현체는 제품마다 또는 필요할 때마다 제공할 경우 이 인터페이스를 SPI라고 말합니다.)
(역주: 네임스페이스 - 스프링 xml contig에서 <bean ...> 외에 별도의 네임스페이스를 포함할 때 그 기능들의 특별한 태그를 쓸 수 있는데 이걸 이야기 합니다. xml config가 더 간단하고 읽기 쉬워집니다.)

* 포괄적인 REST 지원: REST 스타일 request 매핑과 같은 스프링 MVC 본래 REST 기능, @PathVariable 파라메터를 통한 URI 변수 추축 그리고 content negotiation 으로 결정되는 view 결정을 지원합니다. RestTemplate 클래스 같은 방법으로 Client 쪽 REST도 가능합니다.
(역주: content negotiation - HTTP 요청에서 수락가능한 컨텐츠 정보 헤더. 즉 Accept 종류의 헤더들, http://greatkim91.tistory.com/13 에서 마지막에서 두번째 장 참조해 보세요 )

* Rich Portlet 2.0 지원: 스프링 MVC는 Portlet 2.0 환경과 Portlet 2.0의 새로운 이벤트와 자원 요청 모델을 완벽히 지원합니다. 일반적인 portlet 요청 특성을 위해 특화된 매핑 장치도 포함합니다. @ActionMapping, @RenderMapping, @ResourceMapping, @EventMapping 같은게 있습니다.

* 객체/XML 매핑 (OXM): 스프링 웹 서비스로 알려진 것들이 이제 스프링 프레임워크 코어로 포함됩니다. 뛰어난 마셀링과 언마셀링 추상화로 JAXB 2, Castor 등을 지원합니다. 스프링 MVC와 스프링 JMS에서도 통합된 XML 처리 옵션을 지원합니다.

* 차세대 스케줄링 능력: 새로운 TaskScheduler와 Trigger 메커니즘이 최고의 cron을 지원합니다. 스프링 3.0에는 편리한 작업 네임스페이스가 있고 @Async과 @Scheuduled annotation도 지원합니다. 이것은 네이티브 쓰레드 풀 위에서나 서버가 관리하는 쓰레드 풀위에서 실행될 수 있습니다.


이러한 큰 주제들 외에,  스프링 2.5에서 업그레이드 할 때 진가를 찾을 수 있는 수백 가지의 상세한 개선사항들이 있습니다. 변경 로그와 javadoc을 확인해 보십시오.

시스템 요구사항 측면에서, 스프링 3.0은 넓은 범위의 환경을 지원합니다. 두가지 주요 특징으로, 스프링 3.0은 Java SE 5 이상을 지원하고 Servlet 2.4 이상을 지원합니다. 예를 들어 Tomcat 5.x, 6.x와 또한 아직 J2EE 1.4에 기반하는 웹스피어 6.1과 WebLogic 9.2과 같은 일반적인 엔터프라이즈 서버도 지원합니다. 동시에 이미 GlassFish v3를 지원합니다. 스프링은 Java EE 6 레벨 API에도 맞추어져 있습니다.

그 결과, 스프링 3는 완전 새로운 컴포넌트 모델 특징을 갖게 되었습니다. 또한 이미 설치된 서버를 업그레이드 할 필요 없이 프로덱션 환경 구축이 가능하도록 JSR-330 주입과  JSR-303 검증과 같은 표준을 제공합니다. 해야 할 일은 오직 스프링으로 만든 어플리케이션의 라이브러리들을 스프링 3.0으로 업그레이드하는 것 뿐입니다.

스프링의 혜택을 누리십시오. 그리고 상세한 스프링 3 기능에 대한 포스트를 따라가 보십시오. 스프링 3.0에서 돌아가는 예제와 함께요...

---

정리후기

퇴근해야 하기에 뒤에는 막 번역이 되었습니다. 스프링이 점점 엔터프라이즈 급 프레임워크로 자리 잡아 가는 것 같네요. 이제는 정말 재야에 존재하는 기술이 아니라 주도하는 기술이 되는 것 같습니다.
엔터프라이즈 급 기능들 외에 configuration 지옥이라는 오명을 벗기 위해서인지 스프링 설정들이 annotation으로 대부분 대체되는 모양새입니다.
이제는 Annotation 지옥이라는 말이 나올지도 ... (annotation 남발하는 것 갠적으로 아주 싫어합니다 -.-)



크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리

예전에 Javascript와 Flex를 external interface를 이용하여 연계하는 방법을 소개했습니다. (Flex 차트와 Javascript 연결) 이 방법을 사용하면 Flex 차트를 웹 컴포넌트로 사용하고 대부분을 javascript로 화면을 구성할 수 있어 한 번 작성한 Flex 차트를 여러 모로 활용할 수 있는 장점이 있습니다. 즉 Flex 차트는 오직 차트 기능만을 제공하고 그 외 로직은 외부(javascript)로 분리하는 방법이죠.


문제점

이런 방법을 사용하면 동기화 문제가 발생합니다. 즉 다음과 같은 상황이 발생할 수 있습니다.

위 그림이 이것이 동작하는 방식인데 Javascript의 호출이 너무 빨라 Flex 차트가 로딩되기 전에 3번 흐름이 먼저되는 경우가 발생할 수 있습니다. 그럴 경우 개체가 undefined라는 에러가 발생합니다. 즉 Flex가 생성되기 전 Flex를 호출하게 됩니다.

Javascript와 html이 로딩되어 동작하는 것과 Flex가 로딩되는 것이 별도로 진행되기 때문에 이런 에러가 발생합니다. 이것은 비단 이 경우 뿐만 아니라 Flex의 External Interface를 사용할 경우 발생할 수 있는 문제입니다.


해결방법

위 경우 Flex 차트가 준비(로딩)되어야 애플리케이션 동작이 가능하기에 Flex 차트가 로딩되면 위 과정을 수행하는게 좋습니다. 그런데 아쉽게도 javascript에서 Flex가 로딩되었는지 알 수 있는 이벤트는 찾지 못했습니다. 그 대신 Flex가 로딩되었는지 알려주는 이벤트를 다음과 같은 방법으로 구현할 수 있습니다.

1. Flex가 초기화되면 Flex는 Javascript에게 초기화되었다고 알려줍니다. 즉 Javascript의 function을 호출합니다.

2. Javascript는 그제야 서버호출을 하거나 비즈니스 로직을 수행합니다.

3. 그리고 Flex 차트를 호출합니다. (이미 로딩된 후입니다.)





아래 소스는 Flex 초기화 메소드에서 맨 끝에 Javascript를 호출하는 Flex 소스입니다.

14행에서 파라메터로 전달받은 initCallback을 javascript 함수명으로 호출을 합니다.


아래는 HTML내의 Flex 객체 Object 태그입니다. initCallbaack 파라메터로 호출받을 javascript 함수를 넘깁니다.

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
    ...
    codebase="http://fpdownload.macromedia.com/get/flashplayer/current/swflash.cab">
    ...
    <param name="flashvars" value="initCallback=send"/>
    <embed id="LineChart" ...
      ...
      flashvars="initCallback=send">
    </embed>
</object>

이러면 Flex가 초기화되면 send라는 javascript 함수를 호출하겠네요.


마지막으로 Javascript 입니다.

 이 부분은 별반 다를 게 없습니다.


우리는 Flex 컴포넌트를 이런 방법으로 많이 사용하고 있어 이런 방법으로 해결했습니다. 또 다른 방법이 있기는 한데 이 방법이 아직 가장 안정적입니다.

더 좋은 방법을 없을까요?


크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
프로젝트를 하다보면 데이터를 엑셀로 출력해 달라는 요구가 빈번히 발생합니다.
간단하게 CSV 파일로 출력할 수도 있으며 Excel이 HTML 형태의 문서양식도 지원하기에
HTML로 작성하고 mine type만 살짝 바꿔주어 엑셀로 읽을 수 있도록 하는 방법이 있습니다.

물론 CSV로 요구사항을 충분히 만족시킬 수 있을 경우도 있으나 고품질의 엑셀 양식을 요구할 경우 위 두 방법은 웬지 부족함이 있습니다. 이럴 경우 엑셀 포맷으로 출력할 필요가 있는데 여러가지 솔루션이 있어 어렵지 않게 해결할 수 있는 부분이지만 매번 어떻게 처리할 지 고민을 합니다. 여러가지 방법을 사용해 본 후 가장 나은 방법을 추천해 봅니다.

솔루션 찾기

Java로 엑셀을 다루기 위한 방법은 여러가지 있으며 대표적인 것 3가지만 살펴보겠습니다.

JExcel (http://jexcelapi.sourceforge.net/)
Java Excel API은 개발자가 엑셀 스프레드시트를 동적으로 읽고, 쓰고 수정할 수 있도록 하는 성숙한 오픈소스 java API입니다. Java 개발자는 간단한 API를 이용하여 엑셀 스프레드시트를 읽고, 수정하고 쓸 수 있습니다.

장점은 이 패키지는 다른 패키지를 필요로 하지 않고 현재버전(2.6.9.1)의 jar 파일의 크기가 709KB로 부담없이 쓸 수 있다는 것입니다. 비교적 간단한 AP를 제공하나 엑셀의 차트나 그래프 매크로 정보를 생성할 수는 없으며 시트에 PNG 이미지만 추가할 수 있습니다.

POI (http://jakarta.apache.org/poi/)
POI는 Microsoft의 OLE 2 컴포넌트 문서 포맷을 다루기 위한 프로젝트입니다. 따라서 POI에는 엑셀 뿐만 아니라 워드문서를 다루는 API 도 제공하고 몇가지 모듈로 나누어져 있습니다.
이중 HSSF 가 엑셀 파일 포맷을 다루기 위한 자바구현체입니다. 오래되었고 다른 것 보다 큰 이상을 가지고 출발한 프로젝트라 다양한 API를 제공합니다.

apache 재단에서 진행되는 프로젝트이며 POI 패키지는 여러개의 다른 패키지(commons, log4j 같은..)를 필요로 합니다. 풍부한 API를 제공하는 대신에 사용하기 번거로운 점이 있으며 많은 패키지를 필요로 한다는 부담이 있습니다.

jXLS (http://jxls.sourceforge.net/)
jXLS은 엑셀파일 포맷의 템플릿을 이용하여 엑셀 파일을 손쉽게 생성하기 위한 패키지입니다. 또한 XML 설정 파일을 통해 엑셀파일의 데이터를 Java 객체로 읽는 장치도 제공합니다.

사실 jXLS은 Javarta POI 패키지를 기반으로 동작합니다. 따라서 jXLS을 사용하기 위해서는 POI가 사용하는 많은 다른 패키지를 필요로 합니다.

반면 jXLS 자체는 매우 작으며 복잡한 보고서 생성이나 일정한 양식의 엑셀 데이터를 규칙에 따라 읽게 한다는 뚜렷한 목적이 있어 범용성은 약간 떨어지더라도 대부분의 엑셀 관련 작업에 훌륭한 솔루션이 될 수 있습니다.

우리는 jXLS을 이용하여 위 문제를 풀어볼 것입니다.


jXLS 맛보기

jXLS은 템플릿을 기반으로 최종 엑셀파일을 생성합니다.
JSP나 Velocity 또는 Freemarker 같이 템플릿을 만들고 출력할 데이터를 템플릿을 이용하여 변환하면 템플릿 모양대로 최종결과물이 생성하는 구조입니다.
여기서 jXLS은 템플릿으로 엑셀파일을 그대로 쓰며 따라서 템플릿 작성이 매우 쉽다. 또한 엑셀 파일을 그대로 사용하므로 엑셀의 서식과 차트등 엑셀 파일의 대부분의 기능을 그대로 사용할 수 있습니다.


간단한 예제로 설명을 하겠습니다.
Java 객체
출력할 데이터를 만듭니다. 아래는 Customer 클래스를 예로 사용했지만 Map도 지원합니다. 각 속성에 대한 getter, setter는 반드시 존재해야 합니다.
public class Customer {
    private Long no;
    private String name;
    private String cellphone;
    private String email;
    public Long getNo() {
        return no;
    }
    ...
}


변환
출력한 데이터를 만든 후 엑셀템플릿과 출력할 데이터 (Java 빈)을 이용하여 변환합니다.
        // 출력할 객체를 만든다.
        List<Customer> customers = new ArrayList<Customer>();
        Customer customer = new Customer();
        customer.setNo(1L);
        ...
        customers.add(customer);
        ...
       
        Map<String, Object> beans = new HashMap<String, Object>();
        beans.put("customers", customers);
        XLSTransformer transformer = new XLSTransformer();
        transformer.transformXLS("엑셀템플릿파일이름.xls", beans, "엑셀결과파일이름.xls");


엑셀템플릿
엑셀파일로 다음과 같이 작성합니다. 중간에 ${..}로 들어갈 곳은 데이터가 치환되는 부분입니다. 위의 경우 "엑셀템플릿파일이름.xls" 파일을 아래와 같이 생성합니다.

이제 프로그램을 구동하면 자바객체를 이용하여 엑셀파일을 생성할 것입니다.
태그 사용하기
좀 더 세밀한 제어를 위해 jXLS은 여러가지 태그를 제공합니다. 위의 예제를 태그로 변경하면 다음과 같습니다.

jXLS의 자세한 사용법은 jXLS 홈페이지를 참조하세요.


웹환경 실전에서

우리는 경우에 따라 고객목록을 HTML로 출력하거나 엑셀파일로 다운로드할 것입니다. 즉 동일한 데이터가 경우에 따라 표현하는 방법 만 달리하는 경우에 해당합니다.
이런 요구사항은 흔히 발생하고 이런 경우 MVC 모델을 응용한 아키텍처를 많이 사용합니다.
MVC(Model View Controller) 모델은 많이 들어보았을 것입니다. 여기서 우리는 Model과 View를 분리하는 작업을 할 것입니다. 위의 경우 Model은 Customer 클래스에 해당하고 View는 엑셀파일에 해당할 것입니다.



Struts2 Result 구현
위 구현을 위해 우리는 Struts2를 사용할 것입니다. Struts2는 가장 많이 사용하는 프레임워크로 Struts 1에 비해 구조가 많이 개선되었습니다. 위의 목적을 달성하기 위해 우리는 데이터를 엑셀로 만드는 Result Type만 구현하여 Struts2 프레임워크에 붙여(플러그인)주기만 하면 됩니다.

Struts2의 Result Types 참조: http://struts.apache.org/2.x/docs/result-types.html

Excel Result Type 만들기
Struts2에 새로운 Result Type을 만들기 위해서는 com.opensymphony.xwork2.Result를 구현하면 됩니다.

엑셀로 변환하기 위해 필요한 정보는 엑셀 템플릿, 엑셀에 출력할 객체들 그리고 다운로드받을 파일이름 정도일 것입니다. 이것들을 이용하여 변환을 합니다.

JXLSResult.java
아래는 제가 사용하는 Struts2 엑셀 result tyup 클래스입니다.
(아래 소스의 저작권은 넥스트리소프트에 있습니다. 참조하여 사용하는 것은 문제가 없으나 저작권은 반드시 명시하여 주시기 바랍니다.)

public class JXLSResult implements Result {
    /** 엑셀 템플릿 */
    private String template;
    /** 엑셀에 출력할 객체들 */
    private String beans;
    /** 파일이름을 얻어올 키값 */
    private String filenameKey = "filename";

    public void execute(ActionInvocation invocation) throws Exception {
        ActionContext actionContext = invocation.getInvocationContext();
        ServletContext context
            = (ServletContext) actionContext.get(StrutsStatics.SERVLET_CONTEXT);
        HttpServletResponse response
            = (HttpServletResponse) actionContext.get(StrutsStatics.HTTP_RESPONSE);
        // 출력할 bean들을 만든다.
        Map<String, Object> beanParams = new HashMap<String, Object>();
        String[] beanNames = splitBeans();
        for (String beanName : beanNames) {
            beanParams.put(beanName, invocation.getStack().findValue(beanName));
        }
        XLSTransformer transformer = new XLSTransformer();
        InputStream is = null;
        HSSFWorkbook workbook;
        String finalTemplate = TextParseUtil.translateVariables(this.template, invocation.getStack());
        try {
            is = readTemplate(finalTemplate, context);
            workbook = transformer.transformXLS(is, beanParams);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    // 무시
                }
            }
        }
        String filename = invocation.getStack().findString(filenameKey);
        if (filename == null)
            filename = "기본파일이름";
        writeWorkbook(filename, response, workbook);
    }
    /** 엑셀에 출력할 객체이름(key)들을 분리한다. */
    private String[] splitBeans() {
        return this.beans.split(",");
    }
    /** 엑셀 결과를 출력한다. */
    private void writeWorkbook(
        String filename, HttpServletResponse response, HSSFWorkbook workbook)
        throws IOException {
        response.setHeader(
            "Content-disposition", "attachment;filename=" + encodeFileName(filename + ".xls"));
        response.setContentType("application/x-msexcel");
        workbook.write(response.getOutputStream());
    }
    /** 파일이름 인코딩 */
    private String encodeFileName(String filename) {
        try {
            return URLEncoder.encode(filename, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e.getMessage, e);
        }
    }
    /** 엑셀 템플릿을 읽는다. */
    private InputStream readTemplate(
        String finalTemplate, ServletContext context) throws FileNotFoundException {
        String templateFilePath = context.getRealPath(finalTemplate);
        return new FileInputStream(templateFilePath);
    }
    /**
     * @param template 엑셀 템플릿
     */
    public void setTemplate(String template) {
        this.template = template;
    }
    /**
     * @param beans 엑셀에 출력할 객체들
     */
    public void setBeans(String beans) {
        this.beans = beans;
    }
    /**
     * @param filenameKey 파일이름을 얻어올 키값
     */
    public void setFilenameKey(String filenameKey) {
        this.filenameKey = filenameKey;
    }
}



Struts2에 Result Type 추가
struts.xml 에 구현한 result typ을 추가합니다. 상세한 result type 구현 및 추가방법은 struts 사이트를 참조하세요.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
 ...
 <package name="my-default" extends="struts-default">
  <result-types>
   <result-type name="excel" class="com.nextree.fw.commonweb.struts.result.JXLSResult" />
  </result-types>
 ...
 </package>
</struts>



Controller 작성
Struts2의 Action을 controller로 사용하겠습니다. 아래와 같이 Action을 만듭니다. 참고로 struts2는 POJO를 그대로 Action으로 사용할 수 있습니다. 자세한 Struts2 Action 작성방법은 Struts2 site를 참조하십시오.
public class CustomerListController {
    /** 반환할 값 */
    private List<Customer> customers;

    public String execute() {
        ....
        this.customers = ....;   // 여기서 출력해야 할 데이터를 조회한다.
        return Action.SUCCESS;
    }
    /* 출력할 값의 getter 메소드를 반드시 제공한다. */
    public List<Customer> getCustomers() {
        return this.customers;
    }
    /* 위 Excel Result type의 경우 file이름을 controller에서 가져오게 되어 있다. */
    public String getFilename() {
        return "고객목록";
    }
}


Action 정의
Struts2 의 struts.xml에 action을 정의합니다. 우리는 excel로 다운로드하기에 앞에 설치한 excel result type을 사용할 것입니다.
 
...
<package name="mypackage" namespace="/mypackage" extends="my-default">
  <action name="customers" class="test.CustomerListController" method="execute">
   <result type="excel">
    <param name="template">/customer/CustomerList.xls</param>
    <param name="beans">customers</param>
    <param name="filenameKey">filename</param>
   </result>
  </action>
...

이제 http://hostname:port/context/mypackage/customers.do 로 접속하면 Excel 파일을 다운로드 할 것입니다.

결론

위의 방법을 사용하면 그냥 JSP 작성하듯이 엑셀양식을 작성하여 엑셀을 다운로드하는 기능을 구현하기가 매우 쉽습니다.
엑셀을 그대로 사용하기 때문에 엑셀에 화려한 서식뿐만 아니라 차트나 수식등을 넣을 수도 있어 고품질의 엑셀파일을 만들 수 있습니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
현재 페이지의 스크립트에서 다른 서버로 Ajax 호출을 시도하는 것은 허용되지 않습니다. 이것을 cross domain JSON을 이용하여 해결할 수 있는데 다음 URL에서 cross domain JSON을 이용한 원격 호출 방법과 간단한 JavaScript 유틸리티를 제시합니다.
http://www.xml.com/pub/a/2005/12/21/json-dynamic-script-tag.html

그럼 어떻게 JSON을 이용하여 다른 서버의 서비스를 호출할 수 있는지 살펴보겠습니다. 원리를 알면 쉽게 나에게 적합한 유틸리티를 만들수 있을 겁니다.

스크립트 태그

다음은 스크립트 태그입니다. 보통 아래와 같이 사용합니다.

test.js

test.html

그럼 js 디렉토리에 아래와 같은 JavaScript를 생성하는 jsp를 만들고 스크립트 태그에 다음과 같이 수정해봅니다.

test.jsp

test.html


System.getProperty("os.name")은 Host의 OS 이름을 가져오는 Java 코드이고 서버에서 실행되는 코드입니다.
위와 같이 하면 해당 서버의 OS 이름이 나타날 겁니다.


스크립트 src의 URL에 제약이 없다

위의 test.jsp가 이 서버말고 다른 서버(도메인)에 있더라도 동작합니다.
그럼 test.jsp를 다른 서버에 올려놓고 다음과 같이 스크립트 태그를 수정해 봅니다.

test.html

원격 서버의 OS 이름이 나타날 겁니다.

그럼 test.jsp를 다음과 같은 스크립트를 생성하도록 바꾸고, test.html에는 getHostOs라는 JavaScript 함수를 만들어 놓습니다.

(내서버) test.html

(원격서버) test.jsp


7번째 줄 javascript 태그에서 원격에서 생성된 스크립트를 실행합니다. 원격 서버에서 생성된 스크립트를 보니 JSON 데이터를 만들고 그걸 파라메터로 getHostOs 라는 함수를 호출하는군요. 이 함수는 이미 페이지에 준비되어 있습니다. (callback 함수라고 합니다.) 즉 아래와 동일한 결과죠.

여기서 관심있게 볼 부분은 8번째 줄 {osName:'Linux'}이 원격에서 생성한 JSON 데이터라는 것입니다.


실전
원격의 JSON 데이터를 가지고 오기 위해서는 <script type... src="..."></script> 부분이 필요합니다.
원격 서비스 호출을 하는 JavaScript 유틸리티를 만들려면 원격 호출할 때마다 위 방식의 스크립트 태그가 필요하니 동적으로 생성하면 됩니다.

원격 서비스마다 응답 JSON을 처리하는 방법이 다르므로 callback 함수 이름은 요청마다 다르게 해주어야 하며 원격 서버는 callback 이름을 파라메터로 받아 그 함수이름으로 JSON을 파라메터로 넘기는 스크립트를 생성해주어야 합니다. 즉 callback 함수로 감싸면 됩니다.

http://www.xml.com/pub/a/2005/12/21/json-dynamic-script-tag.html  기사에 있는 예제 http://www.xml.com/2005/12/21/examples/jsr_class.zip 소스를 참고하시면 JSON을 이용한 자신만의 cross domain 호출 유틸리티를 만들 수 있을 겁니다.

다른 Cross domain Ajax 방법도 참조하세요.


크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
요즘 웬만한 웹페이지에서는 ajax를 사용하지 않는 페이지가 없을 정도로 많이 사용하고 있습니다. ajax로 페이지를 구성하다보면 다른 서버로도 ajax 호출을 하고 싶은데 보안상의 이유로 JavaScript가 있는 동일 서버 외는 호출이 되지 않습니다. IE6까지는 허용이 되었던 것 같은데 브라우저가 업그레이드 되면서 이러한 제약은 더 심해지고 있습니다. 이러한 것을 cross domain 문제라고 하는데 저는 아래 설명하는 cross domain proxy를 주로 이용해왔습니다. 더 좋은 방법이 없을까 찾아봤는데 깔끔하게 설명된 블로그가 있어 정리해 봅니다.


Cross domain Ajax: 간략 요약

여기에서는 JavaScript를 통해 도메인을 넘어(다른 사이트) 호출하는 가장 일반적인 방법 몇가지를 소개합니다.
그 방법에는 proxy, JSON 그리고 Flash가 있습니다.


Cross domain proxy
이것이 가장 일반적인 접근방법 중 하나입니다. 당신의 스크립트는 당신의 서버를 호출하고 당신의 서버가 실제 호출하려는 원격 서버를 호출합니다. 그리고 그 결과를 클라이언트에 돌려줍니다.

이런 접근방법에는 확실한 잇점이 있습니다. 이 방법은 호출하는 전체 생명주기를 제어할 수 있습니다.
클라이언트에게 반환하기 전 뭔가 처리할 게 있으면 원격 서버에서 받은 데이터를 파싱할 수도 있습니다. 뭔가 에러가 발생하면 원하는 방법으로 에러를 처리할 수도 있습니다.
마지막으로 모든 원격 호출에 대해 로깅을 할 수도 있습니다. 이것으로 성공, 실패 그리고 호출빈도를 추적할 수도 있습니다.


Cross domain JSON
이 방식이 작동하려면 호출하려는 원격서버가 이 방법을 처리할 수 있도록 되어 있어야 합니다.
원격서버는 callback 함수라는 추가 파라메터를 받아들일 필요가 있습니다. 그 다음으로, 원격 URL을 지정할 수 있게 당신의 페이지에 새 스크립트 태그를 넣어야 합니다. 요청에 지정한 callback 함수와 그 파라메터로 JSON 객체가 응답으로 날아와야 합니다. Yahoo를 예를 들면 웹 서비스 API에 이 기능을 구현해 놓았습니다.
이 방법이 훌륭한 이유는 당신의 서버에 어떤 작업을 하지 않아도 웹서비스 호출을 구현할 수 있다는 점입니다.
좀 더 상세한 정보는 Jason Levitt가 XML.com에 쓴  "JSON and the Dynamic Script Tag" 기사를 보십시오.

- 원리를 알고 싶으면 "Cross domain JSON 원리"를 참조하세요.

Flash를 이용한 cross domain
기본적으로 Flash도 Ajax와 같이 원격 서버에 데이터를 요청할 수 없습니다. 그러나 다른 도메인의 요청을 허락하기 위해 원격 서버에 특정 XML 파일을 올려 놓으면 이 기능이 가능합니다. JavaScript가 Flash와 통신할 수 있는 기능을 이용하면 cross domain 요청을 보내기 위해 Flash를 이용할 수 있습니다.
(XML.com 이 기술에 대한 좋은 기사가 있습니다.) 


여전히 Sub-domain들은 다른 도메인이다
아주 미묘한 부분을 한 가지 지적한다면, 우리 대부분은 www.example.com과 example.com과 같은 사이트들을 가지고 있습니다. 이것은 정확히 동일한 위치를 가리키고 있습니다. 우리에겐 그것들이 동일한 것으로 보입니다. 그러나 Ajax는 이것을 다른 도메인으로 봅니다. 따라서 이런 동일 서버에게 ajax 호출을 하려면 요청에 도메인을 코딩하지 마십시오. 그냥 패스만 사용하십시오.


미래에는
JSONRequestContextAgnosticXMLHttpRequest와 같이 미래의 브라우저에 구현할 수 있는 표준을 제정하려는 움직임이 이미 시작되었습니다.
저는 XML 보다는 JSON을 선호하기에 JSONRequest가 가장 유망한 것 같고 그렇게 될 것 같습니다. 이렇게 되기 위한 견인력을 가지려면 다음 몇 년은 지켜봐야 하겠습니다.


크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
점점 웹 애플리케이션도 풍부하고 동적인 UI를 요구하는 경우가 늘어 AJAX 기술을 많이 사용한다.
AJAX를 이용하여 웹개발을 하다보니 점점 UI 렌더링을 서버의 JSP나 서블릿에서 처리하기 보다 Javascript에서 직접처리하는 빈도가 높아지고 있다.

간단한 AJAX 처리 구조는 다음과 같은 방법들이 있을 것이다.

1. 간단하고 쉬운 방법


Javascript에서 AJAX 통신을 하면 응답으로 UI의 일부로 붙일 수 있는 HTML 조각을 받는다. 이것을 UI 특정영역에 삽입하거나 교체한다.
구현하기 매우 쉬우나 서버 입장에서 순수한 데이터만 서비스하는게 아니라 UI 정보도 주어야 하기에 좋은 방법이라 말하기 어렵다. UI 정보도 서비스 할 경우 그 서비스가 다른 곳에서 사용되기 쉽지 않다는 단점도 있고.. 뭐 어쨌든 깔끔하지 않다.


2. 깔끔하지만 머리아픈 방법


서버에서는 순수한 서비스 정보(데이터)만 주고 Javascript에서 이 정보를 이용하여 UI의 DOM을 조작하여 화면을 갱신한다. API 중심이고 프로그래밍 중심이라 깔끔한 방법이고 확장성이 담보된다.
그러나 DOM 조작이 직관적인 방법이 아니고 머리아픈 작업이다. UI가 복잡해지면 (테이블 병합, 다단계 컬럼의 반복...) DOM 조작으로 시간을 모두 버리는 경우가 발생할 수도 있다.
뭐 추천하는 방법이긴하나 구현하기 어렵다.

3. 절충

서버에서는 순수한 서비스 정보만... Javascript에서 이 정보형태(JSON, XML)를 HTML로 변환한다.
이 경우 JSON이나 XML을 HTML으로 변환하는 작업이 필요한데 별 방법 없으면 열심히 루프 돌리거나 문자열 조작해서 변환한다.
그야말로 이 부분이 삽질이 될 수 있다.

4. Tempate Engine
모델과 뷰를 분리한다는 말을 많이 들어보았을 것이다. 즉 순수한 정보와 표현정보를 분리하고 이 둘을 합쳐 결과물을 만들어내는 것이다.
이에 관해 웹 애플리케이션에서 많이 사용하는 기술이 JSP나 FreeMarker, apache velocity등이 있는데 이러한 것들을 template engine이라고 한다.



5. 세 가지 Javascript template engine

위의 3번 방법을 쓸 경우 Javascript를 HTML 변환할 때 template engine을 사용하면 멋지지 않을까?
Google 神 에게 물어보고 다음과 같은 엔진을 찾았다.

PURE (http://beebole.com/pure/)

"javascript template engine"으로 검색한 결과 1등으로 검색된 놈이다. 그래서 신뢰가 가고 열심히 테스트해 보았다.
이 놈은 jquery 기반으로 돌아가서 (Prototype, BooTools도 지원한단다) jquery.js 도 있어야 한다.

간단한 예제는 다음과 같다.
렌더링할 HTML
<div id="hello">
  Hello <span class="who">World</span>
</div>

Javascript 코드
$('#hello').autoRender({ "who": "Mary" })

이 뜻은 "hello" 아이디를 가진 DOM 트리 밑의 "who"라는 클래스를 가지 요소를 "Mary"로 교체하겠다는 뜻이다.
이 정도만 보면 굉장히 좋다는 것을 알 수 있다. 그러나 loop가 필요하거나 특히 중첩된 loop 그리고 한 번 변환한 후 다시 변환할 경우 사용하기가 까다롭다.
일반적인 template engine이 지원하는 문법도 아니라 약간의 학습곡선이 필요하다. 다뤄야 할 기술도 많은데 일관적이지 못한 사용방법은 개인적으로 좋아하지 않는다. 패스...

jTemplates (http://jtemplates.tpython.com/)

이 놈도 jquery 기반이다. (오우~ 막강 jquery) 해당 사이트 가면 첫 대문에 template 예제가 있다.
일반적인 template 문법이라 이해하기 어렵지 않으나 2009년 8월 현재 버전이 0.7.8 이다.
프레임워크 선택 시 중요한 것중 하나가 프로젝트의 성숙도이다. 대충 문서만 읽어보고 패스...

trimpath JavaScriptTemplates (http://code.google.com/p/trimpath/wiki/JavaScriptTemplates)

Google code 에서 진행하는 javascript template 엔진이다. 다른 JS 필요없이 20KB 정도의 template.js만 있으면 된다. 아래 예제는 위 사이트의 "JST(JavaScriptTemplates) 10분 소개"의 내용이다.

먼저 HTML에서 template.js를 로드하고...
 <html>
    <head>
      <script language="javascript" src="trimpath/template.js"></script>
      ...
    </head>
    ...
  </html>

다음, 테스트를 위한 구조적인 javascript 객체를 생성한다. 물론 이부분은 실제 AJAX 통신을 통해 가져올 수 있다.
  <script language="javascript">
    var data = {
        products : [ { name: "mac", desc: "computer",     
                       price: 1000, quantity: 100, alert:null },
                     { name: "ipod", desc: "music player", 
                       price:  200, quantity: 200, alert:"on sale now!" },
                     { name: "cinema display", desc: "screen",       
                       price:  800, quantity: 300, alert:"best deal!" } ],
        customer : { first: "John", last: "Public", level: "gold" }
    };
  </script>

다음은 데이터를 렌더링할 샘플 JST 템플릿이다. 템플릿은 HTML 페이지안에 숨겨진 <textarea>에 넣는다.
  <textarea id="cart_jst" style="display:none;">
    Hello ${customer.first} ${customer.last}.<br/>
    Your shopping cart has ${products.length} item(s):
    <table>
     <tr><td>Name</td><td>Description</td>
         <td>Price</td><td>Quantity & Alert</td></tr>
     {for p in products}
         <tr><td>${p.name|capitalize}</td><td>${p.desc}</td>
             <td>$${p.price}</td><td>${p.quantity} : ${p.alert|default:""|capitalize}</td>
             </tr>
     {forelse}
         <tr><td colspan="4">No products in your cart.</tr>
     {/for}
    </table>
    {if customer.level == "gold"}
      We love you!  Please check out our Gold Customer specials!
    {else}
      Become a Gold Customer by buying more stuff here.
    {/if}
  </textarea>

JST API를 이용하여 tempate 프로세싱을 해보자. (사실 아래에서 첫번째 줄이면 끝이다.)
  <script language="javascript">
    // 한줄로 프로세싱을 끝내는 방법 
    var result = TrimPath.processDOMTemplate("cart_jst", data);
    //  봐 바!  이게 다야  That's it
    // 이제 result 변수에 우리의 JST 렌더링 첫작품을 담고 있어.

    // 다른 방법으로는 명시적으로 템플릿을 파싱할 수도 있어...
    var myTemplateObj = TrimPath.parseDOMTemplate("cart_jst");
    // 이제부터는 파싱 비용이 들지 않지 (즉 계속 사용할 수 있다.)
    var result  = myTemplateObj.process(data);
    var result2 = myTemplateObj.process(differentData);
    // 마지막으로 위 결과를 innerHTML에 넣는 거지
    someOutputDiv.innerHTML = result;
    // document.write() 나 다른 것으로 처리할 수도 있겠지
  </script>

결과는 아래와 같을 것이다.
    Hello John Public.<br/>
    Your shopping cart has 3 item(s):
    <table>
     <tr><td>Name</td><td>Description</td>
         <td>Price</td><td>Quantity & Alert</td></tr>
         <tr><td>MAC</td><td>computer</td>
             <td>$1000</td><td>100 : </td>
             </tr>
         <tr><td>IPOD</td><td>music player</td>
             <td>$200</td><td>200 : ON SALE NOW!</td>
             </tr>
         <tr><td>CINEMA DISPLAY</td><td>screen</td>
             <td>$800</td><td>300 : BEST DEAL!</td>
             </tr>
    </table>
      We love you!  Please check out our Gold Customer specials!

덧붙여 템플릿을 HTML 페이지의 숨겨진 <textarea>에서 얻어왔는데, 바로 JavaScript String을 템플릿으로 사용할 수도 있다.
  <script language="javascript">
    var myStr = "Hello ${customer.first} ${customer.last}, Welcome back!";
    // process() method 사용은 쉽다.
    result = myStr.process(data);
    // 또는 middle-man variable을 사용하는것은 어떤가?
    result = "Hello ${customer.first} ${customer.last}, Welcome back!".process(data);
    // 결과는 똑같이 "Hello John Public, Welcome back!"일 것이다.
    result = "Hello " + customer.first + " " + customer.last + ", Welcome back!";
    // 파싱 비용을 줄이기 위해 한번만 파싱하는 방법을 쓸 수도 있다.
    var myTemplateObj = TrimPath.parseTemplate(myStr);
    var result  = myTemplateObj.process(data);
    var result2 = myTemplateObj.process(differentData);
  </script>  


일반적인 template 문법과 상당히 유사해서 FreeMarker나 velocity를 사용해본 사람이면 바로 쓸 수 있을 것이다. 따라서 PURE에서 이야기했던 문제점은 없고 어떠한 복잡한 템플릿도 쉽게 작성할 수 있다.

또한 템플릿에서 제공하는 문법 (if, else, foreach, ...) 뿐만 아니라 Macro등 유용한 기능들을 지원한다.
그리고 데이터 출력시 포맷팅(날짜, 금액-3자리마다 컴마)을 쉽게하기 위한 Modifier를 지원하는데 이게 참 유용한 기능이다.

적극 추천하는 javascript template engine이다.

끝...

크리에이티브 커먼즈 라이선스
Creative Commons License

'웹기술들' 카테고리의 다른 글

Cross domain JSON 원리  (3) 2009/12/09
Cross domain Ajax: 간략 요약  (0) 2009/12/08
몇가지 Javascript template engine  (3) 2009/08/05
SOAP이냐 REST이냐 - 표준이냐 간결함이냐  (3) 2009/05/28
Struts2 Sitemesh Freemaker  (0) 2009/05/21
Flex 차트와 Javascript 연결  (1) 2008/07/22
Posted by 행복한아빠
"올바른 성장과 따뜻한 나눔"이 있는 넥스트리
객체지향기술을 넘어 소프트웨어 컴포넌트 기술 그리고 이제 서비스를 기반으로 한 기술로 이전하는 추세이다. 이 서비스를 기반으로 한 아키텍처가 SOA(Service Oriented Architecture)인데 여기에서 이야기하는 Service는 기존의 API 수준을 넘어 비즈니스 프로세스 수준에서 재 사용할 수 있는 단위 비즈니스 업무의 서비스를 이야기한다. 즉 기존 기술의 API 보다 좀 더 비즈니스에 가깝게 추상화된 고수준의 서비스이다.
SOA가 추구하고자 하는 것은 서비스의 구현기술에 상관없이 서비스들을 엮어 좀 더 가치있는 새로운 서비스를 쉽게 창출하는데 그 목적이 있다.

SOA 기반기술

SOA 기반의 시스템을 구축하는데 필요한 기술을 이야기하면 SOAP을 많이 이야기하는데 엄밀히 이야기하면 반드시 SOAP을 이용해서 서비스 기반 아키텍처를 만들어야 하는 것은 아니다.
SOA를 설계하고 구축하는데 사용할 기술이나 프로토콜은 SOAP 뿐만 아니라 다른 기술로 가능하며 SOA에서 이러한 기술을 제약하지 않는다. 기업이나 조직의 제반환경이나 아키텍트의 결정에 따라 SOA는 다양한 형태로 구현할 수 있다.
그럼 SOAP외에 어떤 기술을 사용할 수 있을까? 물론 메시징 기반의 기술등 여러 기술이 가능하겠지만 많이 회자되는 기술 중 REST를 빼놓을 수가 없다.
그럼 서비스를 구현하는 기술로 SOAP과 REST가 어떻게 다르고 어떤 장단점이 있을까?

SOAP

SOAP은 웹서비스를 만들기 위한 많은 표준들이 있다. (WSDL, WS-Security, WS-...)
바로 SOAP의 힘은 이러한 표준에서 나온다. 표준에는 매우 강력한 힘이 있다.
마치 국회에서 새로운 법을 지정하면 그 법에 의해 시스템이 돌아가는 것처럼 표준이 있고 그 표준을 지키면 그 서비스는 표준에서 이야기하는 것들이(상호운영, 보안, 라우팅,...) 시스템에서 돌아갈 거라는 확신을 가질 수 있다.
즉 내가 SOAP 표준을 지켜 서비스를 구현하면 기술에 대한 세세한 동작 메커니즘을 설명하지 않더라도 전혀 다른 플랫폼이나 언어로 그 서비스를 사용할 수 있을 것이다. (물론 현실적으로 이게 100% 맞는 이야기인지는 SOAP으로 프로젝트를 해 본 사람이라면 의문을 던질 것이지만...)

SOAP의 단점은 복잡성이다. SOAP 자체가 복잡성이 높다고 보기는 어려울 수도 있지만 SOAP 헤더, 바디, Fault등의 envelope 방법자체도 그렇고 보안, 트랜잭션 등 부수적인 문제해결을 위한 다양한 표준은 사실 이해하기 쉬운 부분도 아니다. (여기서 이야기하는 복잡도는 REST와 비교해서 하는 이야기이다)
한가지 또 단점은 REST에 비해 무겁다는 것이다. 이것은 통신기술에 의존하지 않는 표준이라는 특성에 기인한다.

장점
  • 언어, 플랫폼 그리고 통신에 중립적
  • 분산 컴퓨팅 환경을 다루기 위해 설계
  • 웹서비스를 위해 보급된 많은 표준 (WSDL, WS-*)과 벤더에서 제공하는 도구들
  • 에러 처리에 대한 내용이 기본으로 내장
  • 확장성
단점
  • 개념적으로 REST 보다 어렵고 무거움
  • 시시콜콜한 것들이 많아 복잡함(verbose)
  • 개발하기 어렵고, 보통 도구가 필요함
 
REST

원래 REST는 HTTP의 주요 저자인 Roy Fielding의 2000년 논문에 의해 소개된 네트워크 아키텍처를 위한 구조인데(참조 The Resource-Oriented Architecture) 여기서 이야기하는 REST는 HTTP의 기본 개념에 충실히 따르는 웹서비스를 이야기한다.
이러한 웹서비스를 RESTful 웹서비스라고 하며 단순한 HTTP 요청과 그 결과를 단순한 XML등의 포맷으로 돌려주는 구조이다.

예)
요청:
GET /StockPrice/IBM HTTP/1.1
Host: example.org
Accept: text/xml
Accept-Charset: utf-8

응답:
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Content-Length: nnn

<?xml version="1.0"?>
<s:Quote xmlns:s="http://example.org/stock-service">
     <s:TickerSymbol>IBM</s:TickerSymbol>
     <s:StockPrice>45.25</s:StockPrice>
</s:Quote>


RESTful 웹서비스의 강점은 단순함과 간결함이다. 잘 구현된 RESTful 웹서비스는 간결하고 일관된 인터페이스 구조를 지니고 있어 어려움 없이 쉽게 사용이 가능하다.

반면 RESTful 서비스에 대한 표준은 없어 그 구현방법이 다양해질 수 있다. 또한 보안이나 에러처리등과 같은 부수적인 문제는 직접 해결해야 한다.

장점
  • 언어, 플랫폼에 중립적
  • SOAP보단 개발하기 단순함
  • 학습곡선이 작고 도구가 거의 필요없음
  • 간결함. 추가적인 메시징 계층이 없다.
  • 웹에 가까운 설계와 철학
단점
  • point-to-point 통신 모델을 가정함. 둘 이상을 대상으로 상호작용하는 분산환경에는 유용하지 않음
  • 보안, 정책 등에 대한 표준이 없음. 이런 것까지 고려해서 구현할 경우 좀 더 어려움.
  • HTTP 통신 모델에 의존
 
표준이냐 간결함이냐

표준의 힘은 위에 이미 언급을 하였다. 그러나 간결함의 힘도 무시할 수 없다.

단순함 간결함은 직관적이며 쉽게 개발하거나 사용할 수 있어 기술의 전파 속도가 굉장히 빠르다는 것이다.
단순함이나 간결함을 가진 기술은 굳이 따로 배우지 않더라도 바로 사용할 수 있는 힘이 있다.
따라서 아키텍처나 설계에 있어 단순함, 간결함을 매우 강조하는 이유가 여기에 있다.

SOAP은 통신에 중립적인 구조로 설계되어 있어 부수적인 메시징 정보를 또 포함하고 있다.
HTTP를 이용하여 SOAP을 이용할 경우 이미 HTTP에 포함된 메시징 정보(URL 또는 각종 헤더)에 또 메시징 정보를 포함한다.
이는 편지를 붙이기 위해 주소를 적은 편지봉투를 다시 조금 더 큰 편지봉투에 넣어 보내는 것과 같은 꼴이 된다.

SOAP이 HTTP를 이용하지 않고 다른 프로토콜로 구현한다면 복잡하고 장황한 스팩이 나름대로 의미가 있겠지만 HTTP를 이용한 SOAP 통신이 주를 이루고 있어 위와 같은 상황이 되는게 사실이다.

그러나 SOAP을 무시할 수 없는게 표준이라는 힘에 의해 많은 개발도구들이 SOAP을 지원하여 SOAP을 이용하면 특별한 노력없이 바로 연동이 가능한 점이 있다.

이렇게 표준의 힘을 사용할 것인가 아니면 간결함의 힘에 의지할 것인가에 대한 고민이 있고 이에 대한 논의는 계속되는 상황이다.


트랜드

물론 두 가지의 기술 중에 한 가지만 고집하고 사용할 필요는 없다. 여력이 되는데로 모두 배우고 상황에 맞춰 선택하여 사용해도 될 것이다. 그러나 앞으로 어떤 기술이 주를 이룰지 예측해보는 것은 가치가 있는 일이다.

여러 웹 사이트에서 자료를 수집해 보니 역시 단순한 기술의 전파력이 무섭다는 것을 알 수 있다.

아마존은 SOAP과 REST 서비스 모두 제공하는데 85% 정도가 REST 서비스를 사용하는 것으로 알려졌다. (2003년)

Google은 SOAP 기술에 대해 그리 좋게 보지 않고 있으며 더 이상 SOAP API에 대한 지원을 하지 않는다. (http://radar.oreilly.com/archives/2006/12/google-depreciates-SOAP-API.html)

야후는 현재까지 SOAP API를 제공할 계획이 없다. (http://developer.yahoo.com/faq/#soap)
 

이런 전쟁에서 항상 우수한 기술이나 표준 기술이 이겨온 것은 아니다.
필자는 1997년도 즈음 CORBA를 이용했는데 그 기술의 비전과 우수성만 보고 온 세상은 CORBA로 통합될 줄 알았다.
그러나 현실은 그렇지 않았다. 배우기 어렵고 성숙한 구현기술이 부족한 CORBA는 자취를 감추고 IIOP만 J2EE 스팩에 남아있을뿐이다.

SOAP이 CORBA와 같은 길을 갈 것 같다는 생각이 드는 이유는 뭘까?
 
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by 행복한아빠