 |  |
|
난이도 : 중급 Brandon J.W. Smith, Software Engineer, IBM Hanumanth R. Kanthi, IT Architect,
IBM
2008 년 2 월 26 일 Project Zero는 서비스 지향 아키텍처(SOA) 기반 Web 2.0 애플리케이션의 신속한 개발에 초점을 맞춘 단순화 된 개발 플랫폼입니다. Project Zero의 라이브러리 중에 으뜸은 무엇보다도 SQL 쿼리를 실행하는 단순한 API입니다. 이러한 API를 활용하여 wiki를 구현하는 방법을 배워봅시다.
시작하기 전에
여러분이 Project Zero에 대한 기본적인 지식이 있는 것으로 간주한다. 기술자료 "웹 애플리케이션용 RESTful 서비스 구현하기"와 튜토리얼 "Project Zero와 PHP 사용하기"를 참조하면 더욱 좋겠다. Project Zero를 능숙하게 사용할 수 있다면, 다운로드 하여 직접 샘플 애플리케이션을 작성해 보도록 한다. (참고자료).
머리말
Larry Wall은 다음과 같이 말한다: 쉬운 것은 쉽게, 어려운 일은 가능하게 하라.
데이터 액세스는 단순한 유스 케이스부터 매우 복잡한 유스 케이스까지 넓은 범위를 갖고 있다. 마찬가지로, 많은 개발자들은 단순한 케이스는 말할 것도 없이, 복잡한 상황을 핸들 할 수 있을 정도로 유연한 데이터 액세스 API를 좋아한다. Project Zero의 데이터 액세스 API(zero.data)는 이 정확한 고통 점을 다루고 있다. 이것은 Hibernate나 Java™ Persistence Architecture 같은 추상 레이어로서 의도된 것은 아니다. 오히려, SQL 주위에 얇은 래퍼를 제공함으로써 쉬운 것은 쉽게, 어려운 것은 가능하게 하려는 라이브러리이다. 예를 들어, Listing 1은 간단한 쿼리를 실행하고 빈(bean) 인스턴스의 리스트로서 결과를 받아들이는 방법을 보여준다:
Listing 1. zero.data를 사용하여 빈의 리스트와 Map 인스턴스 리턴하기
Manager data = Manager.create("myDb");
List<Person> results = data.queryList("SELECT * FROM person", Person.class);
List<Map<String,Object>> resultsMap = data.queryList("SELECT * FROM person");
|
지난 해, Project Zero 팀은 IBM®의 Information Management 개발자들과 긴밀히 협력하여, 자바 애플리케이션용 개발 툴, API, 고급 런타임을 포함하고 있는 고성능 데이터 액세스 플랫폼인 pureQuery에 매진했다. zero.data는 pureQuery의 API와 런타임을 활용한다. zero.data는 pureQuery의 툴이 자바 생성물을 만들기 때문에 pureQuery의 툴세트 사용을 배제하지 않는다. 하지만, 이 글을 쓰고 있는 현재, 이러한 강력한 툴 세트와 Project Zero 스팩의 통합은 없다. (pureQuery에 대한 자세한 내용은 참고자료 참조). 그림 1은 Project Zero의 API와 pureQuery간 관계이다:
그림 1. Project Zero의 zero.data 관계형 데이터 액세스 API들 간 관계
 |
Project Zero 커뮤니티
Project Zero 웹사이트에서 현대적인 웹 애플리케이션의 개발 및 실행 플랫폼으로서의 Project Zero의 면모를 살펴보기 바랍니다. 활발한 커뮤니티에서는 프로젝트 개발 문제가 논의되고, 개발자를 지원하며, 여러분들의 의견을 기다립니다! |
|
zero.data는 pureQuery의 API와 런타임에 의해 제공된 기능 주위에 얇은 래핑 API를 제공한다. 이것은 Project Zero 애플리케이션(예를 들어, 설정과 연결 풀링)을 단순화 하고, Groovy와 PHP 스크립트가 각각의 언어에 맞는 방식으로 이러한 강력한 API를 활용할 수 있는 공통 API를 제공하기 위해서 pureQuery보다 더욱 선호된다. 예를 들어, Groovy 인터페이스에 노출된 여러 메소드들은 클로저(closure)를 활용하는 반면, PHP 인터페이스는 PHP 리소스 구분자를 사용한다.
이 글에서는 Groovy API에 집중하고자 한다. Project Zero 문서에서 자바와 PHP API를 자세히 다루고 있다. 이 글 나머지 부분에서는, Zero 애플리케이션에서 zero.data 사용 방법의 기초를 배울 것이다. 먼저, zero.data.groovy.Manager 인터페이스를 사용하여 데이터를 관리하는 방법과, 디자인 뒤에 숨은 동기를 배우며, 자바와 Groovy 관점에서 API를 조명한다. 그런 다음, 실제로 zero.data API를 사용하는 애플리케이션을 만든다. 애플리케이션이 만들어지면, 간단한 wiki를 구현하고, 테이블을 초기화 하고, 마지막으로 애플리케이션을 코딩한다. 다음과 같은 아웃라인으로 진행된다:
데이터 관리하기
zero.data.groovy.Manager (이후 Manager)는 사용자가 관계형 데이터베이스를 쿼리 및 조작할 수 있도록 편리한 API를 정의한다. 이 API의 가장 기본적인 용도는 SQL 스트링을 전달하는 것이다. Groovy에서는 다음과 같은 스트링에 삽입된 매개변수로 전달한다: data.queryFirst("SELECT * FROM t WHERE id=$id"). Manager는 PreparedStatement를 사용하여 스트링을 준비하고, 이를 실행하며, 결과를 리턴한다. 또한, 생성된 클로징 리소스를 지능적으로 관리한다: 예를 들어, 결과 세트, 문, 연결.
Manager는 일련의 메소드를 제공하여 다음과 같은 연산을 수행한다:
- 쿼리를 실행하고, 각 행을 반복하면서 클로저를 실행한다.
-
쿼리를 실행하고, 다음 중 하나로 변환된 ResultSet의 첫 행만 리턴한다:
- Java Bean 클래스의 인스턴스
- java.util.Map
-
쿼리를 실행하고, 다음 중 하나에 포함된 것으로 변환된 ResultSet의 모든 행을 리턴한다:
- java.util.Iterator
- java.util.List
- a Java array
- 쿼리를 실행하고 ResultSet을 리턴한다.
-
생성된 키를 위해 특화된 메소드를 포함하여 데이터 조작 문(INSERT, UPDATE, DELETE)을 실행한다.
간단한 API
데이터 포인트로서, Manager가 어떻게 보일러플레이트 코드를 줄이고, 잠재적인 버그 위험이 있는 코드 보다는 기능에 집중할 수 있도록 하는지를 비교하는 것이 중요하다. 예를 들어, Listing 2는 Listing 1에 나타난 코드의 JDBC 전용 구현으로서, Manager가 쉬운 것을 어떻게 쉽게 처리하는지를 보여준다:
Listing 2. JDBC를 사용하여 Map의 인스턴스를 리턴하면 더욱 장황하다.
// assumes Connection has already been initialized
// see JDBC documentation for more details
List<Map<String,Object>> results = new ArrayList<Map<String,Object>>();
try {
PreparedStatement stmt =
connection.prepareStatement("SELECT * FROM person");
ResultSet rs = stmt.executeQuery();
try {
while(rs.next()) {
ResultSetMetaData meta = rs.getMetaData();
int numColumns = meta.getColumnCount();
Map<String,Object> row = new HashMap<String,Object>(numColumns);
for (int i = 1; i <= numColumns; i++) {
row.put(meta.getColumnName(i).toLowerCase(), rs.getObject(i));
}
results.add(row);
}
} finally {
rs.close();
}
} finally {
stmt.close();
}
|
애플리케이션 코드는 JDBC를 직접 활용하지 않는다.(Listing 2). 대부분의 프레임웍은 어느 정도 까지는 인다이렉션 레이어를 제공한다. Manager가 간단한 일을 쉽게 하고 어려운 일을 가능하도록 하기 위해서 엄청난 길이로 늘어날 것이라는 것을 알고 있다. 예를 들어, 단일 인스턴스 또는 자바 빈 클래스나 Map의 List, Iterator, Array (Listing 1)를 리턴하는 것 외에도, zero.data는 개발자 또는 서드 파티가 복잡한 ResultSet 프로세싱을 구현할 수 있도록 하는 템플릿 메소드 방식을 제공한다.
이러한 템플릿 메소드 방식이라는 고급 용법은 나중에 developerWorks 기술자료에서 다루도록 하겠다. 앞서 언급했듯이, zero.data는 pureQuery 런타임 엔진을 기반으로 구현되었다. pureQuery 런타임은 오픈 API를 사용하여 예견 가능하고 유연한 방식으로 데이터베이스 액세스를 발생시키는 프레임웍을 제공할 뿐만 아니라, 코드 재사용도 도모한다. 예를 들어, 많은 pureQuery 런타임은 개발자들이 엔진을 확장하기 위해 구현할 수 있는 인터페이스들의 구현들로 구성된다. 이러한 방식으로, pureQuery 런타임은 '1회성" 확장부터, 재사용 가능한 ResultSet
프로세싱 컴포넌트의 라이브러리까지 모든 것을 다룰 정도로 유연해진다.
그림 1에 묘사된 것처럼, Manager는 zero.data.Manager를 실제로 래핑하면서, 보다 Groovy 친화적인 숏컷을 제공한다. 아래 표에 Manager의 데이터 액세스 API와 zero.data.Manager의 API를 비교했다. 이들이 공통된 주제를 수행하고 있다는 것을 알게 될 것이다. 주로, 특별한 유스 케이스를 제외하고는 거의 같은 일을 수행하는 다양한 메소드 유형들이 있다. 예를 들어, queryList는 java.util.List를 리턴하는 반면, queryIterator는 java.util.Iterator를 리턴한다. 다른 설명이 필요 없다. 이 같은 메소드 유형 각각 세 개의 오버로딩 된 메소드를 갖고 있고, 모든 메소드 유형에 걸쳐 서명과 함수가 대칭적이다. 표 1에는 모든 메소드 유형과 오버로딩 된 메소드를 설명해 놓았다. Project Zero의 Javadoc API 문서에서 자세한 내용을 참조하라.
표 1. Manager 메소드 개요
| 메소드 | 설명 | 변이 | Groovy 방식 |
|---|
| queryFirst |
ResultSet의 첫 번째 행을 리턴한다. | Map<String,Object> queryFirst(String, Object...) | Map<String,Object> queryFirst(GString) |
|---|
| T queryFirst(String, Class<T>, Object...) | T queryFirst(GString, Class<T>) | | T queryFirst(String, RowHandler<T>, Object...) | T queryFirst(GString, RowHandler<T>) | | queryList |
ResultSet을 java.util.List로서 리턴한다. | List<Map<String,Object>> queryList(String, Object...) | List<Map<String,Object>> queryList(GString) |
|---|
| List<T> queryList(String, Class<T>, Object...) | List<T> queryList(GString, Class<T>) | | List<T> queryList(String, RowHandler<T>, Object...) | List<T> queryList(GString, RowHandler<T>) | | queryIterator |
java.util.Iterator로서 ResultSet을 리턴한다. | Iterator<Map<String,Object>> queryIterator(String, Object...) | Iterator<Map<String,Object>> queryIterator(GString) |
|---|
| Iterator<T> queryIterator(String, Class<T>, Object...) | Iterator<T> queryIterator(GString, Class<T>) | | Iterator<T> queryIterator(String, RowHandler<T>, Object...) | Iterator<T> queryIterator(GString, RowHandler<T>) | | queryArray |
ResultSet을 자바 어레이로서 리턴한다. | Map<String,Object>[] queryArray(String, Object...) | Map<String,Object>[] queryArray(GString) |
|---|
| T[] queryArray(String, Class<T>, Object...) | T[] queryArray(GString, Class<T>) | | T[] queryArray(String, RowHandler<T>, Object...) | T[] queryArray(GString, RowHandler<T>) | | update | 업데이트를 실행하고, 영향을 받은 행의 수를 리턴한다. | int update(String, Object...) | int update(GString) |
|---|
| insert | 두 번째 매개변수에 지정된 클래스로 던져진 키 값을 실행 및 리턴한다. Groovy 버전은 고유의 방식이 있고 int로 던진다. | T insert(String, Class<T>, String[], Object...) | int insert(GString, List<String>) |
|---|
자바 프로그래밍과 Groovy
표 1을 종합해 보면, zero.data는 다양한 상황에 맞는 포괄적인 데이터 액세스 메소드 세트를 갖고 있다. 우리는 zero.data.groovy.Manager 버전의 queryFirst(GString),
queryList(GString),
update(GString),
insert(GString, List)만 사용할 것이다.
Groovy는 동적인, 객체 지향 프로그래밍 언어로서 자바 플랫폼에서 실행된다. Groovy가 자바 프로그래밍 언어를 대체한다는 오해가 있다. Groovy는 자바 바이트코드로 컴파일 되므로, 자바 언어로 작성될 수 있는 표준 자바 API와 많은 라이브러리를 활용할 수 있다. 따라서, Groovy는 자바 신택스를 대체한다기 보다는, 많은 언어 구조를 포함하고 있는 대안 신택스를 제공함으로써 이를 보완한다고 하는 것이 맞는 표현이겠다.
예를 들어, 자바 버전의 Project Zero zero.data API를 사용하여, 표준 JDBC 메소드 호출에서 코드를 줄일 수 있다. 하지만, Listing 3과 비교해 보면 자바와 Groovy 코드가 기능상 동일하다는 것을 보여준다:
Listing 3. 자바 코드와 Groovy 코드의 기능 비교
// Java
Manager data = zero.data.Manager.create("wiki");
data.inTransaction(new LocalTransaction() {
public void execute() {
data.update("UPDATE mytable SET name=? WHERE id=?", 1, "new name");
data.insert("INSERT INTO anothertable (name) VALUES (?)", "another name");
List<Map<String,Object>> results = data.queryList("SELECT * FROM sometable");
for (Map<String,Object> row : results) {
for (String key : row.keySet()) {
System.out.println("key: " + key + ", value: " + row.get(key));
}
}
}
});
// Groovy
def data = zero.data.groovy.Manager.create("wiki")
data.inTransaction {
data.update("UPDATE mytable SET name=$name WHERE id=$id")
data.insert("INSERT INTO anothertable (name) VALUES ($name)")
data.eachRow("SELECT * FROM sometable") { row ->
row.each { key, value ->
println("key: $key, value: $value")
}
}
}
|
Listing 3에서, 코드 라인의 수가 줄어들었을 뿐만 아니라, Groovy 언어는 정도에 벗어나려고 하는 경향이 있다는 것을 알 수 있다. 예를 들어, 자바 버전의 inTransaction 메소드에서, LocalTransaction의 익명의 인스턴스를 만들어야 한다. 하지만, Groovy에서는 이것을 클로저(closure) 뒤에 숨긴다. 클로저는 "이식성 있는" 코드를 생성하는 Groovy의 방식이다. 자바에서 추상 LocalTransaction 클래스를 익명으로 구현하여 이것을 inTransaction 메소드로 전달하는 것과 비슷하게, Groovy에서는 클로저 괄호("{" 와 "}") 사이에 코드를 압축하여 이것을 보다 정확한 방식으로 inTransaction 메소드로 보낸다. Groovy 문서(참고자료)에는 Groovy 언어의 클로저에 대한 설명과 함께, 클로저를 인수로 취하는 API를 작성하는 방법과 예제가 들어있다.
Listing 3에서 Groovy 버전의 코드는 두 번째와 세 번째 클로저를 사용한다. eachRow 메소드는 클로저를 취하고, 결과에 있는 각 행을 위한 브래킷 사이에서 코드를 실행한다. 리스팅에서 보듯, 이 코드는 또 다른 클로저를 호출하는데, Map 행에 있는 엔트리들을 반목하고 키와 값을 프린트 한다.
애플리케이션 만들기
 |
이 글에서 zero.data API 사용하기
Project Zero는 Groovy를 동적인 언어로서 사용하는데 정적인 컴파일 타임 유형 체크를 수행할 수 있지만 반드시 그럴 필요는 없다. 따라서, 이 API의 Map 버전이 이 글에 사용되는데, ResultSet의 행을 java.util.Map으로 매핑하는데, 컬럼 이름은 Map에 있는 키이고, 값은 행에 있는 값이다. |
|
이 글 나머지 분에서는, 전통적인 Model-View-Controller 패턴을 사용하여 간단한 wiki 애플리케이션을 구현하는 과정을 통해 zero.data의 힘을 설명하도록 하겠다.
Model-View-Controller 패턴
그림 2는 Project Zero 애플리케이션에서의 Model-View-Controller 구현을 묘사한 것이다:
그림 2. Project Zero 애플리케이션에서의 Model-View-Controller 구현
애플리케이션을 실행하려면, Project Zero 애플리케이션을 구현해야 한다. 다음 그림은 Eclipse용 Project Zero 플러그인의 사용법을 보여주는데, 애플리케이션 개발을 위한 사용자 인터페이스 숏컷을 제공한다. (하지만, 이 모든 것 역시 명령행 유틸리티를 사용해서도 가능하다.)
먼저, Zero 애플리케이션을 구현한다. ""Project Zero 소개, Part 1"에서는 Project Zero Eclipse 사용자 인터페이스를 사용하여 애플리케이션을 생성하는 방법을 상세히 설명하고 있다. 더욱이, Project Zero 애플리케이션을 구성하는 다양한 폴더와 생성물들을 설명해 놓고 있다. 이 글에서는 기본적인 단계를 배우도록 한다.
먼저, File > Project.. 메뉴 아이템을 클릭하여 애플리케이션을 생성한다. 다이얼로그 박스에는 프로젝트 마법사를 선택할 수 있는 옵션이 제공된다. Project Zero > Project Zero Application을 선택하여 마법사를 시작한다. (그림 3):
그림 3. Project Zero 애플리케이션 마법사 선택하기
마법사의 첫 번째 스크린은 애플리케이션 이름을 위한 프롬프트가 뜬다. 본인이 원하는 이름으로 할 수 있지만, 이 글에서는 mywiki로 하기로 한다. (그림 4):
그림 4. 새로운 Project Zero 애플리케이션 이름 프롬프트
이것은 Project Zero 애플리케이션 폴더 구조에 있는 Eclipse 워크스페이스에 하나의 프로젝트와 몇 가지 기본 애플리케이션 생성물들을 만든다. mywiki 프로젝트를 찾는다. 그림 5와 같은 모습이다:
그림 5. Project Zero 애플리케이션 폴더 구조
Project Zero가 Ivy 프레임웍을 사용하여 종속물들을 관리하기 때문에, zero.data를 획득하기는 매우 간단하다. 종속물들은
${app_home}/config/ivy.xml에서 선언된다. 단순하게 하기 위해, Apache Derby를 임베디드 모드로 사용하기로 한다. 파일을 편집하여 두 줄의 XML을 포함시킨다. (Listing 4):
Listing 4. 애플리케이션의 ivy.xml에 추가하기
<dependency org="zero" name="zero.data" rev="1.0+"/>
<dependency org="org.apache.derby" name="derby" rev="10.3.1.4"/>
|
 |
패스워드 흐리기
여러분은 데이터베이스에 임베디드 Derby를 사용하기 때문에, 애플리케이션의 프로세스에서 실행되는 데이터베이스에서 요구되는 연결 프로퍼티가 필요 없다. 예를 들어, password 프로퍼티가 없다. Project Zero는 패스워드를 흐리게 하여 플레인 텍스트에는 나타나지 않게 하는 방식을 제공한다. Project Zero
문서에는 이러한 유형의 설정에 대해 자세히 나와 있다. |
|
" Project Zero 소개, Part 1" 에서 새로운 종속물에 대해 참조하기 바란다.
zero.data 설정하기
zero.data는 javax.sql.DataSource를 사용하여 데이터베이스에 연결한다. Project Zero 애플리케이션의 zero.config 파일에 연결 프로퍼티를 설정함으로써, 한 개 이상의 데이터베이스를 설정하여 연결한다. Listing 5는 "wiki"의 키를 가진 데이터베이스를 설정하는 방법이다:
Listing 5. zero.config에서 zero.data 설정하기
/config/db/wiki = {
"class" : "org.apache.derby.jdbc.EmbeddedDataSource",
"databaseName" : "db/wiki",
"connectionAttributes" : "create=true"
}
|
Manager 인스턴스 얻기
데이터베이스 연결 프로퍼티가 올바르게 설정되었다면, Manager 인스턴스를 얻을 준비가 된 것이다. 이러한 상황에서, 쿼리를 실행하고, 앞서 설명한 메소드를 사용하여 데이터를 조작할 수 있다. zero.config 파일에서 선택했던 키를 사용하여 설정된 Manager를 얻는다. 이 경우, 우리는 'wiki'를 선택했다. 이것을 create 메소드에 대한 인자로서 전달한다. (Listing 6):
Listing 6. 설정된 Manager 가져오기
def data = zero.data.groovy.Manager.create('wiki')
|
Listing 4,
Listing 5,
Listing 6에서 보듯, Zero 애플리케이션을 설치하여 실행하기는 매우 간단하다. 이제는, 기본적인 요구 사항들을 정의하고 이것을 코드로 변환하여 간단한 wiki를 구현해 보자.
wiki 구현하기
zero.data의 사용법을 설명하기 위해, 우리는 간단한 wiki 애플리케이션을 구현할 것이다. 이 단순한 폼에서 wiki 페이지를 만들고, 편집하고, 연결하며, 실행될 것이다. 따라서, 이 예제에서는 표준 CRUD 연산에서 Create, Retrieve, Update 연산을 설명하겠다. (Delete 연산은 설명하지 않겠다.)
왜 wiki인가?
zero.data API를 설명하는데 왜 wiki를 구현하는가? 먼저, wiki는 단순하다. 따라서, 우리는 zero.data 파이에 집중할 수 있고, 복잡한 wiki 구현에서 발생하는 다른 구현 상세에 신경 쓰지 않아도 된다. 둘째, wiki는 나중에라도 추가 기능들이 구현될 수 있는 좋은 베이스를 제공하여, zero.data와 Project Zero의 다른 기능들의 고급 사용법을 설명할 수 있다.
 |
wiki란 무엇인가?
wiki는 사용자들이 웹 페이지를 쉽고 빠르게 생성, 편집, 연결할 수 있도록 해주는 소프트웨어이다. 이 용어는 하와이 말로서, 빠른(quick) 또는 비공식적인(informal)이란 뜻을 지녔다. |
|
Wiki 요구 사항
앞서 언급했던 것처럼, 기본적인 wiki 구현은 wiki 페이지를 생성, 검색, 업데이트 해야 한다. 대부분의 wiki 구현에 공통적인 것은 페이지들이 또 다른 페이지로 연결함으로써 생성되어야 한다는 것이다. wiki 사용자가 이와 같은 링크를 클릭하면, 페이지 생성을 나타내는 폼이 디스플레이 된다. 이 페이지가 이미 존재하면, 생성 폼과 비슷한 폼에서 페이지를 편집할 수 있도록 하는 링크로 렌더링 된다.
우리는 Project Zero의 스크립트와 템플릿 기능을 결합하고, 익숙한 MVC 패턴들에 대한 노하우를 활용하여 이 구현을 프로토타이핑 하도록 하겠다. 주로, 애플리케이션의 공개 폴더에 컨트롤러 스크립트를 둘 것이다. 이 스크립트는 URI를 통해 액세스 될 것이다-PHP 애플리케이션과 비슷하다. 파일 시스템의 폴더 계층과 파일들은 URI 구조와 매치된다. 뷰는 애플리케이션의 /app/views 폴더에 있다. 우리는 이것을 Groovy로 구현할 것이므로, 컨트롤러에는 .groovy 접미사가 붙고, 뷰에는 .gt 접미사가 붙을 것이다.
이해를 돕기 위해서, 그림 6에는 애플리케이션 생성물들을 생성한 후 애플리케이션 디렉토리 모습을 그려놓았다:
그림 6. 단계를 완성한 후 Project Zero 애플리케이션 폴더 구조
충분히 이해가 되었을 것이다. 이제 코드를 분석해 보자.
테이블을 초기화 하기
컨트롤러와 뷰 구현에 돌입하기 전에, 데이터를 검색, 디스플레이, 조작하기 위해 사용할 수 있는 모델이 필요하다. wiki 페이지를 데이터베이스 테이블로 관리할 것이다. 간단히 하기 위해, 우리 모델은 데이터베이스 테이블의 행으로 매핑된 Map이 될 것이다. (zero.data를 다룬 글이 많이 없다.) 우리의 스토리지 필요는 매우 단순하다. 페이지에 대한 이름과 실제 페이지 내용이 필요하다. 이것은 setup.sql 파일에 있는 Listing 7의 SQL 코드 스니펫에 반영된다:
Listing 7. setup.sql 내용
CREATE TABLE pages (
id int NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
name varchar(20) NOT NULL,
content varchar(3000),
updated timestamp,
PRIMARY KEY (id)
);
|
시작하기!
이전에 논의된 다른 애플리케이션 생성물들과는 달리, setup.sql에 대한 어떤 규약도 없다. 이 파일에 포함된 데이터 정의는 startup 핸들러를 사용하여 실행된다-애플리케이션이 시작될 때 특정 단계에서 실행되는 Project Zero 애플리케이션에 제공되는 코드이다.
먼저, Project Zero 애플리케이션이 시작될 때 실행되어야 하는 Groovy 스크립트를 만든다. 데이터베이스가 아직 없다면 setup.sql에 있는 문을 실행해야 한다. 우리는 개발 시간 트릭을 수행할 것이므로, 실행 환경에서는 반복되어서는 안된다. 현재, 기존 데이터베이스 스키마를 수정하는 것과 관련한 지원 사항이 없다. 애플리케이션 스키마가 변하면, startup 핸들러를 사용하여 파일 시스템에서 데이터베이스를 삭제해야 한다. 설명을 위해, 제일 먼저 베스트 프랙티스를 보여주도록 하겠다. 이 간단한 구현은 zero.data API의 사용법을 설명하는데 충분하다. (Listing 8):
Listing 8. app/scripts/startup.groovy의 콘텐트
import zero.data.groovy.Manager
//if derby database already exists, don't run the script
def db_dir = new File('db/wiki')
if (!db_dir.isAbsolute())
db_dir = new File(config.root[], 'db/wiki')
if (!db_dir.exists()) {
def buffer = new StringBuffer()
new File('setup.sql').text.split('\n').each() { line ->
line = line.trim()
if (line && !line.startsWith("--") && !line.toLowerCase().startsWith("connect"))
buffer << line << '\n'
}
def statements = buffer.toString().split(';')
def data = Manager.create('wiki')
try {
data.inTransaction() {
statements.each() { statement ->
statement = statement.trim()
if (statement)
data.update(statement)
}
}
} catch (Throwable error) {
throw new RuntimeException("Setup database error: ${error.message}", error);
}
}
|
이 스크립트가 실행되기 전에, 그렇게 하도록 설정해야 한다. config/zero.config 파일에 startup 핸들러로서 스크립트를 등록한다. (Listing 9):
Listing 9. config/zero.config에 추가하기
/config/handlers += {
"events" : "start",
"handler" : "startup.groovy"
}
|
이것은 Project Zero 런타임이 '시작' 이벤트가 종료될 때 모든 상황에서 startup.groovy 스크립트를 실행하도록 명령한다. Project Zero 애플리케이션을 시작하면, CREATE TABLE 문이 실행될 것이다. 이 데이터베이스는 아직 존재하지 않으므로, 삽입된 Derby가 디스크에 이것을 만든다. (여러분은 앞서 연결 프로퍼티를 설정하여 이것을 만들었기 때문이다.)
애플리케이션 코딩하기
애플리케이션을 실행시킨다. Project Zero는 동적이기 때문에, 대부분의 변경 사항들을 짚어낸다. 이제 테이블이 생성되었으므로, 컨트롤러와 뷰를 코딩할 수 있다. 페이지 렌더링을 시작해 보자.
동적 HTML 페이지 렌더링
페이지를 실행하려면, Project Zero 런타임이 컨트롤을 우리의 wiki 애플리케이션에 놓기 위해 사용하는 후크가 필요하다. 우리는 이것을 app/public 디렉토리에 컨트롤러의 형태로 구현할 것이다. render 기능을 'view.groovy'로 하기로 한다. 먼저, Manager가 페이지용 쿼리를 실행해야 한다. 그런 다음, 쿼리를 실행하여 데이터베이스에 있는 행을 검색하도록 한다. 행이 있다면, 이것을 실행한다. 존재하지 않는다면 create 페이지를 디스플레이 한다. 구현은 Listing 10과 같다:
Listing 10. view.groovy 구현
def name = request.params.name[]
def data = new zero.data.Manager('wiki')
def page = data.queryFirst("SELECT * FROM pages WHERE name=$name")
if (page) {
request.page.name = page.name
request.page.content = page.content
request.view = 'view.gt'
} else {
request.page.name = name
request.view = 'create.gt'
}
render()
|
'view.gt'와 'create.gt' 뷰를 생성하여 view.groovy 컨트롤러를 지원해야 한다. 두 뷰 모두 Groovy 템플릿을 사용하여 요청 범위에서 데이터를 실행한다. (Listing 11과 12):
Listing 11. view.gt 구현
<html>
<head>
<title><%= request.page.name[] %></title>
</head>
<body>
<h1><%= request.page.name[] %></h1>
<%= request.page.content[] %>
<hr/>
<a href="<%= "${getRelativeUri('/edit.groovy')}?page=${request.page.name[]}" %>">
Edit this page?
</a>
</body>
</html>
|
Listing 12. create.gt 구현
<html>
<head>
<title><%= request.page.name[] %> - Create</title>
</head>
<body>
<h1><%= request.page.name[] %></h1>
This page does not exist.
<a href="<%= "${getRelativeUri("/edit.groovy")}?name=${request.page.name[]}" %>">
Create?
</a>
</body>
</html>
|
데이터베이스 업데이트
이전 섹션에서는, 두 개의 뷰를 생성했다: 실행된 wiki 페이지와 wiki 페이지를 생성하는 폼. 이 뷰를 지원하기 위해서는 추가 컨트롤러와 뷰를 만들어야 한다. 주로 view.gt 뷰는 edit.groovy 컨트롤러로 연결되고, create.gt HTTP POST는 edit.gt를 실행함으로써 save.groovy로 연결된다. edit.groovy는 데이터를 검색하고 create와 비슷한 폼을 디스플레이 하고, 기존의 wiki 페이지 콘텐트로 채워진다. save.groovy는 행을 삽입하거나 데이터베이스 테이블에서 기존 행을 업데이트 한다. 구현은 Listing 13부터 15까지 참조하라:
Listing 13. edit.groovy 구현
def name = request.params.name[]
def data = zero.data.groovy.Manager('wiki')
def page = data.queryFirst("SELECT COUNT(*) FROM pages WHERE name=$name")
if (page) {
request.page.content = page.content
} else {
request.page.content = ""
}
request.page.name = name
request.view = 'edit.gt'
render()
|
Listing 14. edit.gt 구현
<html>
<head>
<title><%= request.page.name[] %> - Editing</title>
</head>
<body>
<h1>Editing <%= request.page.name[] %></h1>
<form method="POST"
action="<%= "${getRelativeUri('/save.groovy')?name=${request.page.name[]}" %>">
<textarea name="content" rows="20" cols="60"><%= request.page.content[] %></textarea>
<input type="submit" value="Save Page"/>
</form>
</body>
</html>
|
Listing 15. save.groovy 구현
def name = request.params.name[]
def data = zero.data.groovy.Manager('wiki')
def page data.queryFirst("SELECT * FROM pages WHERE name=${name}")
def content = request.params.content[]
if (page) {
data.update("UPDATE pages SET content=${content}")
} else {
data.update("INSERT INTO pages (name,content) VALUES ($page_name,$content")
}
uri = "${getAbsoluteUri('/view.groovy')}?name=${name}"
request.headers.out.Location = uri
request.status = HttpURLConnection.HTTP_MOVED_TEMP
|
애플리케이션 테스트 하기
이제 매우 기본적인 wiki 애플리케이션이 생겼다. 사실 너무 기초적이어서 애플리케이션을 부트스트랩 하기 위해서는 존재하지 않는 wiki 페이지를 제공해야 한다. http://localhost:8080/view.groovy?name=HomePage를 입력하여 그림 7과 같은 페이지를 가져올 수 있다:
그림 7. 소실된 HomePage
이 페이지가 존재하지 않은 것으로 알았다. 실행되고 있다는 것을 의미하므로 괜찮은 것이다. 아무 값이나 URI의 이름 매개변수에 줄 수 있지만, "HomePage" 정도면 출발이 좋다. 그런 다음, HomePage에 대한 콘텐트를 만들어야 한다. 임의의 HTML에 입력한다. (없다는 것을 알고 있지만) 페이지로 연결될 폼에 링크를 추가한다. (그림 8):
그림 8. HomePage 편집
이제, 버튼을 클릭하여 데이터베이스에 페이지를 둔다. 방금 편집했던 페이지를 보기 위해 리다이렉션 된다. (그림 9):
그림 9. 발견된 HomePage
존재하지 않는 페이지로 연결했다면(단 한 페이지만 데이터베이스에 존재하기 때문에 이것은 매우 쉬운 일이다.), 그 링크를 클릭하고, 소실된 페이지 스크린으로 다시 가게 될 것이다. 그림 10처럼 연결된 페이지를 위한 페이지를 만들 수 있다:
그림 10. 소실된 CodingTips
이것이 우리의 wiki이다. 기본적으로, 우리 wiki는 더 많은 일을 수행한다. Markdown 또는 Textile 같은 마크업 텍스트를 사용하여 콘텐트 생성과 링크를 단순화 할 수 있다. (나중에 자세히 설명하도록 하겠다!)
결론
Project Zero는 SOA에 순응하는 Web 2.0 애플리케이션의 신속한 개발에 초점을 맞춘 단순한 개발 플랫폼이다. Project Zero의 라이브러리 중 으뜸은 SQL 쿼리를 실행하는 단순화 된 API이다. 여러분은 이러한 API를 사용하여 간단한 wiki를 구현하는 방법을 배웠다.
pureQuery API의 래핑을 포함하여, zero.data API의 개념에 대해서도 논했다. 또한, zero.data.Manager를 분석하고, 자바와 Groovy 버전의 API를 비교했다. 간단한 wiki를 구현하고, 데이터베이스 테이블을 초기화 하고, 구현을 코딩함으로써, zero.data를 사용한 애플리케이션 생성법을 구체적으로 배웠다. 이 글이 Zero 애플리케이션을 구현하는 여러분에게 도움이 되었기 바란다! Project Zero 커뮤니티에서 많은 도움을 얻을 수 있다.
다운로드 하십시오 | 설명 | 이름 | 크기 | 다운로드 방식 |
|---|
| Sample Project Zero Eclipse project | wa-pz-wiki.zip | 10KB | HTTP |
|---|
참고자료 교육
- "Project Zero 소개, Part 1: 웹 애플리케이션용 RESTful 서비스 구현하기" (developerWorks, 2007년 10월) 강력한 웹 애플리케이션을 생성, 조합, 전개하는 방법을 설명한다. 커뮤니티 중심의 Project Zero를 소개하고, RESTful 웹 서비스를 구현하는 규약을 소개한다
- "
Project Zero 애플리케이션의 보안 유지하기, Part 1: 인증과 권한" (developerWorks, 2007년 11월) Project Zero 보안에 대해 배우고, 사용자 레지스트리 생성, 애플리케이션을 위한 보안 규칙 정의, 가장 대표적인 두 가지 유형의 인증(기본과 폼 기반) 활용 방법을 배워본다.
- "
Project Zero 애플리케이션 보안에 Active Content Filtering 사용하기" (developerWorks, 2007년 11월) 크로스 사이트 스크립팅 같은 일반적인 Web 2.0 기반 애플리케이션 어택을 피하고, Project Zero 애플리케이션의 보안을 향상시키는 방법.
-
IBM Data Studio pureQuery Runtime: 개발 툴, 애플리케이션 런타임 API, 자바 데이터베이스 개발의 라이프 사이클을 관리하는 엔진. Project Zero는 이 런타임 API와 엔진을 사용하여 Web 2.0 애플리케이션 개발에 유연한 환경을 제공하고 있다.
-
Groovy Closures : Groovy의 고급 언어 구조를 활용하는 실질적인 방법 소개.
-
"Template method pattern"
(Wikipedia, 2007년 11월)
- "Project Zero 애플리케이션의 데이터베이스 설정 및 의존성 최적화" (developerWorks, 2007년 9월)
: 데이터베이스 중심 컴포넌트를 설정 및 패키징 하는 베스트 프랙티스를 설명하여, 다른 Zero 개발자들도 재사용 할 수 있도록 하는 방법을 설명한다.
- The
zero.data.Manager Javadoc API
-
Project Zero 커뮤니티와 관련된 모든 buzz 보기!
- Project Zero 튜토리얼 "Project Zero와 PHP 시작하기"
- developerWorks 웹 개발 존
- developerWorks Ajax 리소스 센터
제품 및 기술 얻기
토론
필자소개
 | 
|  | Hanumanth Kanthi는 IBM Software Services for WebSphere 팀의 IT 아키텍트이다. 호주 Victoria University of Technology에서 컴퓨터 공학 석사 학위를 받았다. (kanthi@us.ibm.com) |
기사에 대한 평가
|  |