 |  |
|
난이도 : 중급 Michael Galpin, 소프트웨어 엔지니어, eBay
옮긴이: 박찬욱 dwkorea@kr.ibm.com
2008 년 5 월 27 일 이클립스의 첫 명성은 자바(Java™) 기술을 위한 통합개발환경(IDE)이라는 점이었습니다. 이클립스의 플러그인 아키텍처는 이클립스 성공의 가장 큰 이유가 됐습니다. 우리가 이용할 수 있는 매우 다양한 플러그인이 있으며, 자신만의 플러그인도 매우 쉽게 만들 수 있습니다. 이 두 특징 덕에 이클립스는 이베이(eBay)와 같은 특화된 아키텍처를 갖는 시스템에도 완벽하게 맞아 들어갑니다. 이베이의 이클립스 사용을 다루는 2부작 중 첫 번째인 글인 이번 글에서는 이베이 아키텍처와 이베이 아키텍처에 적합한 이클립스를 어떻게 갖게 됐는지 살펴보겠습니다. 이번 글에서는 이베이에 초점을 맞추겠지만, 독자의 아키텍처에 적합한 이클립스 만드는 데 이 글의 내용을 사용할 수 있을 것입니다.
이번 글은 독자들이 이클립스에 기본적으로 친숙하다고 가정한다. 우리는 이클립스 V3.2와 JDK 5.0이나 그 이상 버전(참고자료에 있는 링크 참조)에 기반을 둔 이베이 개발 환경(EDE)에 대해 얘기할 것이다. HTML, 자바스크립트와 CSS를 포함한 웹 개발에 친숙하면 매우 유용하지만 필수조건은 아니다.
이클립스는 훌륭한 플러그인 시스템을 갖고 있는 자바 IDE로 알려져 있다. 이클립스 V3.3(Europa)은 특화된 이클립스 배포판이다. 유로파에는 자바 개발자를 위한 이클립스와 자바 EE 개발자를 위한 이클립스가 포함된다. 추가적으로 이클립스 C/C++ 개발 툴킷(CDT)과 이클립스 PHP 개발 툴킷(PDT)을 사용할 수 있다.
이클립스 배포판은 모두 기본 이클립스 플랫폼에 플러그인이 조합된 구조다. 이것이 이클립스의 아름다움이다. 심지어 사람들이 대부분 이클립스에 통합되길 바라는 일반적인 자바 개발 도구가 모두 실제로 플러그인 형태로 존재한다. PHP 개발을 하고 싶은 경우에는 자바 빌더로 고생할 필요가 전혀 없다. 자바, C++와 PHP는 모두 프로그래밍 언어이므로 그 자체적으로도 상당히 폭넓은 이야깃거리가 있다. 그렇지만 같은 프로그래밍 언어를 사용할 수도 있는 다른 시스템보다 훨씬 더 전문화해야 할 필요성이 있을 것이다.
여러분의 IDE가 자신의 시스템에 특화되어 있다고 생각하는가? 이클립스의 바탕이 되는 철학과 이클립스에 기반을 두고 EDE를 만드는 데 이베이에서 사용한 원칙은 정확하게 일치한다. EDE의 독특한 필요성을 이해하고, 필요에 맞게 이클립스를 사용하는 방법을 이해하기 위해 이베이 아키텍처를 먼저 살펴보자.
이베이 아키텍처
이번 절의 전체 내용은 이베이 아키텍처와 역사의 다양한 면을 설명하려고 작성했다. 이번 글에서는 이베이의 프리젠테이션 아키텍처(presentation architecture)를 진화시킨 방법과 특별히 이베이에서만 필요한 특징을 어떻게 이클립스 플랫폼을 사용해 해결했는지에 대해 집중해볼 것이다. 우리는 현재 아키텍처의 문제를 해결해야 하는 도전을 이해하기 위해 이베이 아키텍처의 역사를 요약해 살펴 볼 것이다.
역사
이베이는 1995년 웹 경매 사업을 처음으로 시작했다. 최초 사이트는 펄(Perl)로 작성했다. 사이트가 성장함에 따라 백엔드를 C++로, 프론트엔드는 XSL을 사용해 다시 작성했다. HTML을 생성하는 XSL 사용은 1990년대 후반 당시만 해도 최첨단 기술이었다. 이베이는 1998년에 주식을 공개했고, 급격한 성장세를 계속 보여 주었다.
트래픽 압력이 지속적으로 높아지자 2001년부터 자바 프로그래밍 언어로 이베이 백엔드를 재작성할 것이 강력히 요구됐다. 프론트엔드 아키텍처는 변하지 않았다. 펄로 된 V1과 C++/XSL로 된 V2에 이어, 자바+XSL 아키텍처는 내부적으로 V3 아키텍처에 의해 참조되었다. V3 아키텍처는 이베이가 현재 규모에서 세계적으로 가장 많은 방문자를 자랑하는 사이트 중 하나로 성장할 수 있는 견고한 확장성이 있음을 증명했다. 단지 높은 트래픽을 처리하는 사이트가 아니라, 전 세계 수십 개 국가에서 품목을 보고, 이를 바인딩을 하고, 구입하는 수백만 명의 엄청난 트랜잭션을 처리하는 사이트가 됐다. 물론 완벽한 해결책은 아니지만, 이베이처럼 지속적인 성장을 보이는 시스템에는 적절한 방법이 될 수 있었다.
도전
백엔드 관점에서 V3 아키텍처는 웹 애플리케이션에 의해 발생할 수 있는 가장 어려운 문제에 달려들었다. 순수하게 데이터 관점에서, 이베이에서는 에러나 불일치를 극단적으로 거의 허용하지 않으면서 엄청난 양으로 발생하는 쓰기, 읽기 작업을 처리한다고 설명할 수 있다. 결론적으로 우리는 여기서 사람들의 돈에 대해 얘기하고 있다. 검색 인덱스나 새로운 수집자(aggregator)에 대한 단점을 알아차리거나, 흥분하는 경우는 그다지 많지 않지만, 경매에 입찰해 돈을 받아야 할 때 사람들은 대부분 에러를 즉시 알아차린다. 예측하기 힘든 이메일이나 인스턴스 메시지를 받는 시간은 어느 정도 기다릴 수 있지만, 사람들은 경매에 값을 매겼을 때는 바로바로 알기를 바란다. V3 아키텍처는 믿을 수 없을 만큼 어려운 문제를 해결해냈다. 그러나 여러분이 이베이만큼 성공했을 때 직면할 까다로운 프론트엔드 문제가 많다.
예를 들어 규모가 크고, 불규칙하게 구성된 웹 사이트는 사이트에 있는 모든 이미지를 관리하는 것처럼 사소하게 보이는 것도 완전히 복잡하게 될 수 있다. 이미지를 청소하거나 삭제하려고 하는 예를 들어보자. 어떤 이미지가 사용되는지 어떻게 알 수 있겠는가? XSL 기반 시스템에서는 XSL의 수백만 줄(텍스트의 수백만 줄)을 통해 모든 검색을 할 수 있다. XSL이 쉽게 문자열에 추가될 수 있기 때문에 그것마저도 불완전할 수도 있다. 따라서 이미지를 나타내는 최종 문자열을 한 번에 받아올 수 없을지도 모른다. XSL에는 적용될 수 없지만 텍스트/스크립트 기반 프론트엔드 아키텍처에는 적용할 수 있다.
이미지는 단지 빙산의 일각일 뿐이다. 현재 이베이에서는 CSS를 매우 중요하게 사용한다. 브라우저가 캐시할 수 있도록 CSS를 한 페이지에 넣기보다는, CSS를 외부 파일에 넣는 것이 좀 더 효율적일 수도 있다. 여러 페이지에서 CSS 파일을 재사용하는 것이 더 좋다면, 이에 더해 브라우저 캐시를 사용해 효과를 얻을 수 있다. 그러나 한 페이지에서 스타일을 변경해야 할 경우가 있다고 예를 들어보자. 다른 페이지에 영향을 줄 수 있다면 어떤 방법으로 수정할 수 있는가? 그뿐만 아니라 위 시나리오는 자바스크립트에도 적용할 수 있다.
웹 사이트에서 페이지를 옮겨 본 적이 있는가? 페이지에 있는 모든 링크에 대해 변경이 있는지 확인할 필요가 있다. 이제 이베이처럼 엄청난 규모의 사이트에서 해야 할 일이 상상이 간다. 더군다나 그 링크를 나타내는 여러 모양의 문자열을 엄청난 텍스트 속에서 검색하느라 아무것도 못하는 지경이 될지도 모른다.
포털이 유명해지기 시작한 때가 언제인지 기억하는가? 여러 면에서 포털들은 현대적인 매시업의 선조격이다. 전형적인 포털 형태 페이지는 보통 다른 페이지 요소를 포함하는데 대개 CSS와 자바스크립트다. 이제 충돌 가능성이 떠오를 것이다. 동일한 유형의 이름과 같은 제목은 다양한 장소에서 사용할 수 있겠지만, 다른 값을 가질 수 있다. 비슷하게, 같은 자바스크립트 함수나 변수는 다양한 장소에서 정의될 수 있다. 심지어 아무런 충돌 없이, 한 페이지에서 완전히 동일한 데이터(CSS 혹은 자바스크립트)를 두 번, 세 번, 혹은 그보다 많이 갖고 사용하는 중복이 있을 수 있다. 이러한 종류의 문제는 이베이의 오래된 XSL 기반의 프론트엔드 아키텍처를 사용해서는 쉽게 해결할 수 없다. 기존 방법은 반드시 텍스트 검색에 기반을 두어야 하기 때문이다.
마지막으로 아마도 가장 큰 문제가 될 지역화(localization)가 있다. 이베이처럼 세계적인 웹 사이트는 30개 이상의 언어로 번역된다. 표면적인 원칙으로는 XSL 기반 시스템은 모든 XSL 파일에 대해 지역화가 필요하다. "자바에 있는 ResourceBundles 같은 프로퍼티 파일을 사용하면 되는 거 아니야?"하는 생각을 하면서 웃을지도 모르겠다. 다시 한 번 생각해보자. 특정 언어에서 특정 성을 나타내는 문자를 어떤 방법으로 알 수 있겠는가? 혹은 "입찰 1" 대 "입찰 2"처럼 보기 드문 변경은 어떻게 할 것인가? 지역화해야만 하는 이미지는 어떤가? 상이한 지역에서 상이한 마크업(문장 내에 있는 링크 위치처럼)이 필요한 경우 어떻게 지역화할 생각인가?
이 모든 요소는 각 XSL 파일을 지역화하는 이베이에 반드시 필요했다. XSL이 코드란 점을 잊지 말자. 한 페이지에서 낱말을 변경하는 것처럼 사소한 것도 천 개 이상의 서버에서 새로 코드를 받아내 릴리스할 필요가 있다.
몇몇 웹 사이트는 이러한 문제에 직면해 있다. 이러한 문제를 모두 해결하는 오픈 소스가 없다는 점은 놀라운 일도 아니다. 이베이는 매우 영리하고, 매우 특화된 도구를 만들 필요가 있었다. 그리고 이베이는 이 일을 정확하게 해냈다.
V4
V3 아키텍처는 까다로운 백엔드 확장성(scalability) 문제를 해결했을 뿐이며, 더욱 어려운 프론트엔드 확장성 문제는 V4로 이름 붙은 새로운 프론트엔드 아키텍처로 문제를 해결했다. V4 아키텍처는 백엔드에서 사용하는 강력한 자바 객체 타입(Typed Java Object)을 프론트엔드에서도 계속 사용할 수 있도록 모든 내용을 표현할 수 있는 접근법을 갖고 있다. 여러분의 페이지가 이미지를 사용하는가? 그렇다면 여기서 이미지는 자바 객체가 된다. 이미지를 삭제하고 싶다면, 다른 자바 클래스를 삭제하는 것만큼이나 간단하거나 복잡할 수 있다. 또한 링크, CSS와 자바스크립트 또한 이와 똑같이 적용할 수 있다. V4는 HTML을 생성하는 데 서블릿이나 JSP를 사용하지 않는다. 대신 실제 자바 언어로 표현되는 실제 HTML DOM을 사용하기 때문에, 서버에서 CSS와 자바스크립트를 함께 묶어 사용할 수 있다.
이 중에서 가장 어려운 문제는 무엇일까? 필자는 콘텐츠라고 생각한다. V4의 콘텐츠 시스템은 유일하며 강력하다. 콘텐츠 계약을 만드는 V4만의 XML 기반 언어를 사용한다. 콘텐츠란 낱말의 선택은 의도적이다. 계약은 하나의(단위) 콘텐츠 변수를 생성하는 데 필요한 데이터 종류가 무엇인지 정의한다. 예를 들어, 특정 단위는 ("Raymond has 4 cars"처럼) 정수나 영어 문자열을 필요로 하지만, 또 다른 언어에서는 두 개의 정수와 문자열이 필요할 수도 있다. 또한 '성'이나 계수(cardinality)에 대한 다수의 변수가 될 수도 있다. 계약은 모두 XML에 정의한다. 계약은 특정 애플리케이션/페이지/컴포넌트에서 지원하는 각 언어로 구현한다. 물론 여기서 사용하려는 모든 동적 데이터(위 예에서 "Raymond"와 "4"처럼)는 강력한 자바의 객체 타입으로 표현해야 한다. 포함된 모든 내용이 강력한 자바 객체 타입이므로 모든 내용이 컴파일 시에 강제될 수 있다. 애플리케이션 개발자가 자바 코드에 두 개의 매개변수를 주지 않았다면, 코드는 컴파일되지 않는다.
이 글만 읽으면 모든 내용을 자바로 표현하게 된다는 내용이 매우 좋게 들릴 수 있다. 하지만 이미지나 페이지에 일부 자바스크립트를 사용하고자 할 때마다 새로운 자바 클래스를 작성해야 한다는 것이 상당한 부담이 됨을 알 수 있다. 이제 이클립스에 대해 얘기할 차례다.
이클립스와 V4
지금까지 문제 범위와 원하는 해결 방법을 이해했으니, 이제부터는 이베이가 해결책을 구현하는 데 이클립스가 어떻게 도움이 되었는지 찾을 수 있다. 위에서 살펴본 많은 이미지, 링크, 스타일과 자바스크립트 코드들은 반복되는 코드(boilerplate code)로 생각할 수 있고, 적어도 그 일부는 지속적으로 반복되는 코드로 생각할 수 있다. 이 점이 프레임워크에서 나타나는 일반적인 문제다. 여기서 필요로 하는 일부 타입의 코드들은 대부분 단지 프레임워크에서만 필요로 한다. 이 점은 디버깅을 어렵게 하고, 성능을 저하시킬 수 있다. V4 프레임워크는 코드 생성 도구라는 다른 기술을 사용한다. 자바스크립트를 표현하는 자바 객체가 필요한 경우, 객체를 직접 표현하는 것보다는 코드를 작성하지 않아도 되면 작성하지 않는 방법이 더 좋다. 대신 우리를 위해 이클립스 플러그인이 코드를 작성하게 하자.
코드 생성기 사용
이클립스의 자바와 자바 EE 버전은 코드 생성기를 매우 중요하게 사용한다. 이클립스에는 많은 아티팩트(Artifact)를 생성하는 전형적인 마법사 폼이 있다. 예를 들어 자바 EE를 사용하는 경우, New Dynamic Web Application 마법사를 사용할 수 있다. 이 마법사는 웹 애플리케이션에 맞는 디렉터리 구조와 web.xml 파일을 만들어 쉽게 WAR 파일로 압축해 웹 컨테이너에 배포할 수 있게 해준다. 이베이에서도 코드 생성기는 비슷한 목적으로 쓰인다. 우리가 필요한 반복되는 코드를 코드 생성기가 만들어주기 때문에, 우리는 애플리케이션의 "비즈니스 로직"과 애플리케이션 코드에만 집중할 수 있다.
V4 프레임워크에 사용하는 도구의 일부로 이베이에서 제작한 코드 생성 이클립스 플러그인을 살펴보자.
자바스크립트
자바스크립트는 웹에서 사용하는 언어이며 V4에서 매우 중요하다. V4 프레임워크는 일반적인 방법으로 자바스크립트 코드를 작성할 수 있게 하지만, 몇 가지 특별한 구성을 제공한다. 자바스크립트에서는 근본적으로 객체 지향 문법을 사용하지만, 반드시 요구되는 것은 아니다. 또한 자바스크립트에 대한 특정 메타 데이터를 뽑아내기 위해 오픈 소스 프로젝트인 JsDoc에서 제공하는 코드 코멘팅 문법(code-commenting syntax)을 사용한다. 이 두 특징은 자바스크립트 클래스당 자바스크립트 참조(JsRef) 클래스를 생성해 함께 일하게 해준다. JsRef는 모든 V4 DOM 트리 내에서 참조될 수 있는 자바 클래스다. 예제를 함께 살펴보자.
Listing 1. 이베이 자바스크립트 클래스
vjo.needs("vjo.samples.classes.Person");
vjo.type("vjo.samples.classes.HelloPerson")
.props({
/**
* @return boolean
* @access public
*/
helloPerson:function(){
var person1 = new vjo.samples.classes.Person();
person1.setName("John");
alert("Hello " + person1.getName());
return false;
}
});
|
vjo.*에 있는 다양한 생성자는 자바 파일 선언에 있는 생성자를 모방했다. JsDoc 어노테이션은 자바스크립트에는 제공하지 않는 강력한 타입을 제공한다. 이 어노테이션은 아래 예와 같이 이클립스 플러그인이 자바스크립트에 대응되는 JsRef를 생성할 수 있도록 해준다.
Listing 2. 대응되는 JsRef
package vjo.samples.classes;
import com.ebay.dsf.resource.pattern.js.JsType;
import com.ebay.dsf.spec.component.BaseComponentSpec;
import com.ebay.dsf.aggregator.jsref.internals.JsCmpMeta;
import com.ebay.dsf.spec.component.IComponentSpec;
import vjo.bootstrap.VjBootstrap;
import com.ebay.dsf.resource.pattern.js.JsResource;
import com.ebay.dsf.aggregator.jsref.JsObj;
import com.ebay.dsf.aggregator.jsref.JsFunc;
import com.ebay.dsf.jstojava.codegen.JsRefGenerator23;
import com.ebay.dsf.resource.pattern.js.IJsResourceRef;
import com.ebay.kernel.CodeGenerated;
/*
Generator: JsRefGenerator23 version: 2.3
Generated: Mon Jan 28 11:01:13 PST 2008
Source URL: file:/D:/Views/v4flash/v4samplecode/VjoSample/src/vjo/samples/
classes/HelloPerson.js
*/
/**
* <pre>vjo.needs("vjo.samples.classes.Person");
* vjo.type("vjo.samples.classes.HelloPerson")
* .props({
* //*
* * @return boolean
* * @access public
* //
* helloPerson:function(){
* var person1 = new vjo.samples.classes.Person();
* person1.setName("John");
* alert("Hello " + person1.getName());
* return false;
* }
* });
* </pre>
*/
@com.ebay.dsf.resource.utils.CodeGen(JsRefGenerator23.class)
@com.ebay.dsf.jstojava.codegen.JsRefGeneratorVersion(2.3)
public class HelloPersonJsr extends JsObj implements CodeGenerated{
private static final long serialVersionUID = 1L;
public static final JsResource RESOURCE = JsResource.viaName("HelloPerson");
public static class ResourceSpec extends BaseComponentSpec{
public static final IJsResourceRef REF = jsRef(RESOURCE, JsType.DefOnly);
private static volatile IComponentSpec s_instance;
public static IComponentSpec getInstance() {
if (s_instance != null) {
return s_instance;
}
synchronized (
ResourceSpec.class) {
if (s_instance == null) {
s_instance = new ResourceSpec();
}
}
return s_instance;
}
private
ResourceSpec() {
addJsRef(jsRef(RESOURCE, JsType.DefOnly));
addDependentComponent(VjBootstrap.ResourceSpec.getInstance());
addDependentComponent(vjo.samples.classes.PersonJsr.ResourceSpec.
getInstance());
}
}
protected HelloPersonJsr(JsCmpMeta cmpMeta, boolean isProto) {
super(cmpMeta, isProto);
}
/**
* @return boolean
@access public
* @jsfunc helloPerson
*
* function () {
* var person1 = new vjo.samples.classes.Person();
* person1.setName("John");
* alert("Hello " + person1.getName());
* return false;
* }
*/
public static final JsFunc<Boolean> helloPerson(){
JsFunc<Boolean> func = new JsFunc<Boolean> (getInstance(),
"helloPerson",true,true);
return func;
}
public static final JsCmpMeta META = new JsCmpMeta("vjo.samples.classes.HelloPerson",
"HelloPerson", ResourceSpec.getInstance());
private static volatile HelloPersonJsr s_instance;
private static HelloPersonJsr getInstance(){
addResourceSpec(ResourceSpec.getInstance());if (s_instance !=null){
return s_instance;
}
synchronized (HelloPersonJsr.class){
if (s_instance == null) {
s_instance = new HelloPersonJsr(META,false);}return s_instance;}
}
}
|
이클립스 도구를 사용하는 개발자들은 이 내용을 매우 간단하고, 직관적으로 만들 수 있다. 자바스크립트에서 마우스 오른쪽 버튼을 클릭하면, 아래에서 볼 수 있는 것처럼 필요한 자바 코드를 간단하게 생성할 수 있다.
그림 1. 이클립스를 사용해 JsRef 생성
JsRef는 자바 코드 내에서 프로그래밍하는 데 사용될 수 있다. 이 플러그인을 쓰면 JsRef 자바 코드에 자바스크립트를 묶는 작업이 매우 쉬워진다. 예를 들어, 사용자가 버튼을 클릭했을 때 폼의 유효성 검증을 수행하는 위 예에서 나온 자바스크립트를 호출하고 싶을 수 있다.
Listing 3. 서버에 클라이언트 측 이벤트 묶기
DButton button = new DButton("Hello Person - click me");
button.add(EventType.CLICK, HelloPersonJsr.helloPerson());
|
또한 JsDoc 어노테이션은 자바 import 구조로 묶이기 때문에 의존성을 쉽게 추적을 할 수 있다. 사용하는 자바스크립트가 의존하는 다른 자바스크립트 라이브러리들을 포함하고 있는지 기억할 필요가 없다. 단지 코드에 의존성을 작성해두기만 하면 된다. 자바스크립트를 변경하고(예를 들어 함수에 새로운 매개변수를 추가한다든지 하는) 코드도 함께 리팩터링하지 않았다면, 이는 해당 자바스크립트를 사용하는 코드의 컴파일 에러 원인이 될 것이다. 또한 패키지 문법은 자바스크립트 간의 충돌을 방지하는 명시적인 네임스페이스를 제공한다. 자바스크립트를 표현하는 자바 클래스를 가짐으로써 위와 같은 이익을 모두 얻을 수 있으며, 이를 이클립스 플러그인의 도움을 받아 이 이익을 얻는 데 필요한 추가적인 일을 최소화할 수 있다.
자바스크립트는 이베이의 V4 프레임워크에서 독특한 방법으로 다루는 웹 애플리케이션의 일부분이며, 이클립스 플러그인은 V4를 사용하는 개발자들에게 더 많은 힘을 준다. 또 다른 중요한 예는 CSS다.
CSS
자바스크립트에 대한 V4의 접근법은 기존 자바스크립트는 그대로 유지하고, 단지 자바스크립트를 자바 코드로 표현하는 것이다. CSS에 대한 접근법은 약간 다르다. 자바스크립트는 그 자체로 완벽한 프로그래밍 언어다. CSS는 복잡할 수는 있지만, 자바스크립트와 같은 수준으로 볼 수는 없다. CSS를 처리하는 동안 CSS 파일은 단지 시작 지점으로만 사용한다. CSS 파일은 실시간으로 CSS를 만들어내는 데 사용하는 자바 클래스를 생성하는 데 사용된다. 최초 CSS 파일은 사용하지 않으므로 버린다. 이에 반해 체크인되는 실제 자바스크립트 파일은 소스 컨트롤의 일부분이 되는 자바 클래스이며, JsRef는 제품으로 출시하는 데 사용하는 코드가 있는 빌드 시스템이나 개발자가 사용하는 시스템에서 이클립스를 사용해 생성되는 자바 클래스다. 여전히, CSS로 시작하는 게 좀 더 자연스럽다면, 아마도 아직까지는 시각적인 실험 단계일 것이다.
이렇게 이베이는 CSS를 작업할 수 있는 이클립스 플러그인을 구축하게 됐고, Java-CSS(JCSS)를 "부트스래핑"했다. Listing 4에서는 전형적인 CSS 파일을 볼 수 있다.
Listing 4. CSS 파일
* {
margin: 0;
padding: 0;
}
body {
font-size: 100.01%;
font-family: Verdana,sans-serif;
padding: 10px;
text-align: center;
}
h1, h2, h3, h4, h5 {
font-family: Georgia,serif;
}
h1 {
color: #9A351D;
background-color: transparent;
margin: 1em 0;
}
h3 {
font-size: .9em;
}
div h3 {
color: #58B;
text-transform: uppercase;
font-size: .7em;
}
div h3:before {
content: "by";
font-style: italic;
text-transform: none;
color: #000;
display: block;
margin-bottom: 1em;
}
h4 {
text-align: center;
font-style: italic;
color: #666;
margin: 2em 0 0 0;
font-size: .7em;
font-weight: normal;
}
.footer {
color: #999;
font-size: .5em;
font-family: Verdana,sans-serif;
font-weight: normal;
}
|
다시 한번 이클립스에서 마우스 오른쪽 버튼을 클릭해 JCSS 플러그인으로 들어가 JCSS 클래스를 만들어 보자.
그림 2. JCSS 플러그인 사용
이 플러그인은 자바 클래스를 생성한다.
Listing 5. CSS에 대응되는 JCSS 클래스
package com.ebay.css.example;
import com.ebay.dsf.css.CssClassConstant;
import com.ebay.dsf.javatocss.JCssDef;
import com.ebay.dsf.javatocss.JCssStyleRule;
import com.ebay.dsf.javatocss.prop.BackgroundColor;
import com.ebay.dsf.javatocss.prop.Display;
import com.ebay.dsf.javatocss.prop.FontStyle;
import com.ebay.dsf.javatocss.prop.FontWeight;
import com.ebay.dsf.javatocss.prop.TextAlign;
import com.ebay.dsf.javatocss.prop.TextTransform;
import com.ebay.kernel.CodeGenerated;
/*
Generator: class com.ebay.dsf.csscodegen.generator.def.JCssDefGenerator
version: 1.0
Generated: Mon Jan 28 17:37:36 PST 2008
Source: com.ebay.css.example cover.css
*/
/**
* <pre>
* {margin:0; padding:0}
body {font-size:100.01%; font-family:Verdana, sans-serif; padding:10px;
text-align:center}
h1, h2, h3, h4, h5 {font-family:Georgia, serif}
h1 {color:#9a351d; background-color:transparent; margin:1em 0}
h3 {font-size:0.9em}
div h3 {color:#58b; text-transform:uppercase; font-size:0.7em}
div h3:before {content:"by"; font-style:italic; text-transform:none;
color:#000; display:block; margin-bottom:1em}
h4 {text-align:center; font-style:italic; color:#666; margin:2em 0 0 0;
font-size:0.7em; font-weight:normal}
.footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public class CoverJCssDef extends JCssDef implements CodeGenerated {
private static final String SCOPE_NAME = "";
public interface Clz {
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
CssClassConstant footer_ = new CssClassConstant(SCOPE_NAME, "footer");
}
private static CoverJCssDef s_instance= new CoverJCssDef();
public static CoverJCssDef getInstance(){
return s_instance;
}
public static interface DEFAULT {
public static final JCssStyleRule STYLE_RULE_1 = s_instance.rule()
.select(any)
.set( properties()
.margin("0")
.padding("0"));
public static final JCssStyleRule STYLE_RULE_2 = s_instance.rule()
.select(body)
.set( properties()
.fontSize("100.01%")
.fontFamily("Verdana, sans-serif")
.padding("10px")
.textAlign(TextAlign.CENTER));
public static final JCssStyleRule STYLE_RULE_3 = s_instance.rule()
.select(h1)
.select(h2)
.select(h3)
.select(h4)
.select(h5)
.set( properties()
.fontFamily("Georgia, serif"));
public static final JCssStyleRule STYLE_RULE_4 = s_instance.rule()
.select(h1)
.set( properties()
.color("#9a351d")
.backgroundColor(BackgroundColor.TRANSPARENT)
.margin("1em 0"));
public static final JCssStyleRule STYLE_RULE_5 = s_instance.rule()
.select(h3)
.set( properties()
.fontSize("0.9em"));
public static final JCssStyleRule STYLE_RULE_6 = s_instance.rule()
.select(div.desc(h3))
.set( properties()
.color("#58b")
.textTransform(TextTransform.UPPERCASE)
.fontSize("0.7em"));
public static final JCssStyleRule STYLE_RULE_7 = s_instance.rule()
.select(div.desc(h3.pseudoBefore()))
.set( properties()
.content("\"by\"")
.fontStyle(FontStyle.ITALIC)
.textTransform(TextTransform.NONE)
.color("#000")
.display(Display.BLOCK)
.marginBottom("1em"));
public static final JCssStyleRule STYLE_RULE_8 = s_instance.rule()
.select(h4)
.set( properties()
.textAlign(TextAlign.CENTER)
.fontStyle(FontStyle.ITALIC)
.color("#666")
.margin("2em 0 0 0")
.fontSize("0.7em")
.fontWeight(FontWeight.NORMAL));
public static final JCssStyleRule STYLE_RULE_9 = s_instance.rule()
.select(any.with(Clz.footer_))
.set( properties()
.color("#999")
.fontSize("0.5em")
.fontFamily("Verdana, sans-serif")
.fontWeight(FontWeight.NORMAL));
}
}
|
이 파일은 우리의 정의 스타일을 보여준다. 관심의 분리(separation of concern) 원칙에 따라, 분리한 클래스는 HTML 문서에서 CSS를 구현하는 데 사용된다. 물론 이 클래스는 이클립스 플러그인으로 생성할 수 있다.
그림 3. 플러그인을 사용해 CSS 구현체 생성
플러그인이 생성한 구현체 클래스 예제를 보자.
Listing 6. 생성된 CSS 구현체
package com.ebay.css.example;
import com.ebay.css.example.CoverJCssDef;
import com.ebay.dsf.css.CssClassConstant;
import com.ebay.dsf.csscodegen.generator.realizer.CssRealizerGenerator;
import com.ebay.dsf.csscommon.BaseCssRealizer;
import com.ebay.dsf.html.dom.BaseCoreHtmlElement;
import com.ebay.dsf.resource.pattern.css.CssResource;
import com.ebay.kernel.CodeGenerated;
/*
Generator: class com.ebay.dsf.csscodegen.generator.realizer.CssRealizerGenerator
version: 2.0
Generated: Tue Jan 29 15:20:08 PST 2008
Source: com.ebay.css.example.CoverJCssDef
*/
/**
* <pre>
* {margin:0; padding:0}
body {font-size:100.01%; font-family:Verdana, sans-serif; padding:10px;
text-align:center}
h1, h2, h3, h4, h5 {font-family:Georgia, serif}
h1 {color:#9a351d; background-color:transparent; margin:1em 0}
h3 {font-size:0.9em}
div h3 {color:#58b; text-transform:uppercase; font-size:0.7em}
div h3:before {content:"by"; font-style:italic; text-transform:none;
color:#000; display:block; margin-bottom:1em}
h4 {text-align:center; font-style:italic; color:#666; margin:2em 0 0 0;
font-size:0.7em; font-weight:normal}
.footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
@com.ebay.dsf.resource.utils.CodeGen(CssRealizerGenerator.class)
public class CoverCssr extends BaseCssRealizer implements CodeGenerated {
/** Answers the actual text this realizer was genned from */
public static final String gentext =
"* {margin:0; padding:0}\n"
+ "body {font-size:100.01%; font-family:Verdana, sans-serif; padding:10px;
text-align:center}\n"
+ "h1, h2, h3, h4, h5 {font-family:Georgia, serif}\n"
+ "h1 {color:#9a351d; background-color:transparent; margin:1em 0}\n"
+ "h3 {font-size:0.9em}\n"
+ "div h3 {color:#58b; text-transform:uppercase; font-size:0.7em}\n"
+ "div h3:before {content:\"by\"; font-style:italic; text-transform:none;
color:#000; display:block; margin-bottom:1em}\n"
+ "h4 {text-align:center; font-style:italic; color:#666; margin:2em 0 0 0;
font-size:0.7em; font-weight:normal}\n"
+ ".footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}\n"
;
public static final CssResource src = CssResource.viaDef(
CoverJCssDef.getInstance());
public static final Classes classes = new Classes() ;
public static final Utils utils = new Utils() ;
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public static final class Classes {
private Classes() { /* prevent instantiation */ }
private static final CssClassConstant s_footer = new CssClassConstant("footer");
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public final CssClassConstant footer = s_footer;
}
public static final class Utils {
private Utils() { /* prevent instantiation */ }
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public Utils addClass_footer(final BaseCoreHtmlElement element) {
addCssClass(element, Classes.s_footer);
return this;
}
/**
* <pre>
* .footer {color:#999; font-size:0.5em; font-family:Verdana, sans-serif;
font-weight:normal}
* </pre>
*/
public Utils setClass_footer(final BaseCoreHtmlElement element) {
setCssClass(element, Classes.s_footer);
return this;
}
}
}
|
구현체는 모든 시각적인 요소에 스타일 적용을 쉽게 해준다. 한 가지 대안으로 기본 스타일 정의가 있는 하나의 페이지에 하나 이상의 JCSS 정의를 등록해 사용할 수 있다. 위 두 가지 방법에 대한 예는 아래에서 볼 수 있다.
Listing 7. JCSS 사용
// Create a div, attach a style to it
DDiv footer = new DDiv();
CoverCssr.utils.addClass_footer(footer);
// create DOM, register the CSS for it
HtmlDisplayer2 doc = new HtmlDisplayer2(footer);
doc.registerCss(CoverJCssDef.getInstance());
|
우리는 실시간으로 (JCSS 패키지) 네임스페이스를 갖는 이득을 얻었으며, 우리가 사용한 CSS는 다른 어떤 CSS와도 충돌하지 않을 것이다. 우리는 "본래의" CSS 장점을 여전히 유지하면서 이와 같은 장점을 동시에 얻는다. 핵심은 이클립스 플러그인 사용이 CSS와 JCSS 사이의 간격을 이어준다는 데 있다.
지금까지 우리는 패턴을 살펴봤다. V4는 강력하게 타입을 보장하는 생성자를 모두 필요로 하며, 이클립스는 이 모든 내용을 지원하면서 더 쉽게 할 수 있게 해준다. 우리는 이제 V4에 있는 리소스 중 가장 도전적인 주제인 콘텐츠에 대해 언급할 것이다. 이클립스가 콘텐츠를 어떻게 지원하는지 함께 살펴보자.
콘텐츠
V4 콘텐츠 시스템은 콘텐츠 계약(contents contract)에 의존한다. 이 계약은 자신만의 XML 언어를 갖고 있지만, 여기서도 이클립스 플러그인을 사용하면 각 계약에 대해 조금 더 직관적인 편집기를 쓸 수 있다.
그림 4. V4 콘텐츠에 구조화된 편집기
우리는 콘텐츠 계약을 표현하도록 생성된 자바 코드를 필요로 하며, 이클립스를 사용해 필요한 코드를 생성할 수 있다.
그림 5. 콘텐츠 계약을 위한 자바 코드 생성
콘텐츠 계약은 이베이 애플리케이션에서 찾을 수 있는 복잡한 구조를 표현할 수 있는 강력한 문법을 갖고 있다. 이 문법은 직관적으로 사용할 수 있는 표현적인 API를 이끌어냈지만, 손으로 작성하기에는 매우 성가시다. 하지만 이는 이클립스 플러그인을 사용하면 별 문제가 안 된다.
지금까지 본 것처럼, 이베이는 방대한 시나리오에서 개발자를 위해 코드 생성을 제공하는 이클립스 플러그인을 사용한다. 이 플러그인들은 이베이에 의해 작성된 플러그인의 단지 한 종류일 뿐이다. 이베이에서 쓰는 다른 유형의 플러그인을 살펴보고, 이 플러그인들이 어떤 유형의 문제를 해결할 수 있는지 살펴보자.
다른 V4 플러그인들
또한 이베이는 자바를 사용해 웹 페이지의 모든 요소가 어떻게 표현되는지 알 수 있도록, 이베이만의 고유한 애플리케이션 실행과 디버그 방법을 제공한다. 이 방법으로 생산성에서 탁월한 이득을 얻을 수 있고, 애플리케이션 개발과 이미 배포된 애플리케이션 디버그가 매우 쉬워진다.
이클립스에서 웹 애플리케이션 실행
혹시 여러분 중 웹 서버 시작이나 재시작을 하지 않고, 배치 없이 웹 페이지를 실행하기를 희망한 적이 있는 사람이 있었는지 모르겠다. 이베이의 V4 이클립스 플러그인과 함께라면 가능하다.
그림 6. "dervlet"으로 웹 애플리케이션 실행
여기서 "dervlet이 무엇인가?"라는 질문을 할 수 있다. 이름이 암시하듯이, dervlet은 서블릿과 비슷하다. V4 프레임워크는 웹 애플리케이션의 빠른 프로토타이핑과 테스트가 가능하도록 dervlet을 부트스트랩 클래스로 사용할 수 있도록 했다. 이는 패키지에 있는 다른 클래스들을 테스트하는 데 사용하는 main 메서드와 비슷하다. 모든 작업에서 마우스 오른쪽 버튼을 클릭하고, dervlet을 실행할 수 있다. 확실한 장점은 실행 가능한 모든 코드를 코드의 각 단계를 밟아가면서, dervlet으로 디버그할 수 있다는 점이다. 그 내부에서 이클립스 플러그인은 제티(Jetty) 서블릿 컨테이너의 임베디드 버전을 실행하며, 제티에서 건네 받은 URL에 대한 내용을 웹 브라우저로 연다.
이 이클립스 플러그인을 쓰면 개발 동안 V4 애플리케이션에 대한 디버그를 매우 쉽게 할 수 있다. V4 프레임워크와 이클립스가 실제 제품 애플리케이션에 대한 디버그를 더 쉽게 해주는 방법에 대해 알아보자.
이클립스에서 제품 애플리케이션 디버깅하기
이클립스에서 제품 애플리케이션 디버깅하기
그림 7. Spyglass로 본 이베이 페이지
여러 개의 빨간 상자가 무엇을 의미하는지 알겠는가? 각 빨간 상자는 V4 컴포넌트를 의미한다. 마우스를 특정 박스 위에 올리면 컴포넌트의 완전한 이름(qualified name)을 볼 수 있다. 페이지에서 시각적인 결함이 있다면, (컴포넌트) 코드의 어느 부분이 문제의 원인이 되는지 매우 쉽게 발견할 수 있다. V4는 즉각 코드를 발견하는 것보다 한 단계 더 진보된 방법을 제공한다. 파란 상자에 있는 링크가 무엇인지 알겠는가? 이베이 개발자들은 이베이의 모든 상품 V4 페이지에서 이클립스로 바로 이동할 수 있다. XSL, JSP나 다른 모든 언어에 대해서도 시도해보자. 이클립스에서 리스너(listener) 서버 실행을 통해 해볼 수 있다. 리스너는 이클립스를 사용하는 개발자를 도와 이베이 아키텍처를 발전시킬 수 있도록 이베이가 작성한 또 다른 플러그인이다.
결론
지금까지 이베이가 직면한 독특한 도전에 대해 이야기했다. 우리는 문제를 해결하는 데 요구되는 아키텍처를 어떻게 구축했는지 살펴봤다. 이러한 종류의 아키텍처는 성가신 반복 코드 작성에서 개발자들을 구해주는 강력한 도구가 있어야만 구축할 수 있다. 앞서 설명한 문제와 해결책 중 몇몇은 이베이에만 적용되는 특이한 방법일 수 있지만, 사용한 기술 자체가 그런 것은 아니다. 독자들의 조직에서 특화된 고도의 아키텍처를 요구한다면, 이베이의 페이지를 도입할 수 있고, 개발자들의 생산성을 해치지 않으면서 문제를 잘 해결해주는 이클립스 플러그인을 만들 수 있다.
참고자료 교육
제품 및 기술 얻기
토론
-
이클립스 플랫폼 뉴스 그룹은 이클립스에 관련된 질문을 토론하는 첫 번째 장소가 될 수 있다(이 링크를 누르면 기본 유즈넷 뉴스 리더 애플리케이션이 실행되고 eclipse.platform 뉴스그룹이 열린다).
-
이클립스 뉴스 그룹은 이클립스를 사용하고, 확장하는 데 흥미가 있는 사람을 위한 많은 자원을 갖고 있다.
-
developerWorks 블로그에 들어가 developerWorks 커뮤니티의 토론에 참여하자.
필자소개  | 
|  | Michael Galpin은 1998년부터 전문적으로 자바 소프트웨어를 개발하고 있으며 이베이에서 근무중이다. 캘리포니아 공대에서 수학을 전공하였다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|  |