 |
|
난이도 : 초급 Richard Hightower, CTO, Mammatus Inc.
원문 게재일 : 2009 년 8 월 11 일 번역 게재일 : 2009 년 9 월 01 일 얼마 전까지만 해도 Google App Engine은 Python 개발자만을 위한 것이었기에 조금은
암울한 시기였습니다. 그러나 2009년 4월에 Google Inc.에서 클라우드 컴퓨팅 플랫폼에 대한 문호를 Java™
개발자에게도 개방했습니다. 세 편의 기사로 구성된 이 시리즈에서는 Java 기술 저자이자 강사인 Rick
Hightower가 Java 기반 개발을 위한 이 안정적이고 강력하면서도 재미 있는 플랫폼을 소개합니다. 이
기사에서는 Google App Engine for Java를 확장성 높은 킬러 애플리케이션의 전개 플랫폼으로 사용하기에
적합한 이유를 살펴본 후 Google Plugin for Eclipse를 사용하여 GWT(Google Web Toolkit)와 Java Servlet
API를 기반으로 하는 두 가지 예제 애플리케이션을 작성합니다. 애플리케이션을 처음부터 작성하는 과정과
애플리케이션을 전개하여 최대 5백만 개의 보기를 조정하는 과정을 통해 Google App Engine for Java의
진정한 가치를 알 수 있습니다. (게다가 이 제품은 무료 버전입니다.)
아이디어란 가려움증에 비교할 수 있다. 긁어서 해결이 되어야만 그 시원함을 느낄 수 있다는 점이
바로 그렇다. 우리와 같은 소프트웨어 개발자들은 다양한 종류의 애플리케이션에 대한 아이디어를 짜내는
데 많은 시간을 투자하는데, 그에 대한 해답을 찾는 일을 상당히 즐긴다. 우리에게는 어떻게 하면 소프트웨어
제품을 보다 완성도있게 만들 수 있는지 고민하는 것이 항상 어려운 문제이다. 머릿 속에서 상상한 것을
실제로 구현하는 것은 무척 신나는 일이다. 물론 그렇지 못할 경우에는 상당한 좌절감을 느끼게 되겠지만
말이다.
많은 애플리케이션이 시작 단계에서 주저 앉게 되는 이유 중 하나는 필요한 인프라가 없다는
데서 찾을 수 있다. 유지 관리가 잘 되는 인프라에는 대개 시스템 관리자, DBA 및 네트워크 관리자로
구성된 팀이 존재하며 이러한 인프라는 대부분 최근까지도 자금이 풍부한 기업의 몫이었다. 써드파티에
비용을 지불하고 애플리케이션을 호스팅하더라도 애플리케이션의 인기가 수직 상승하면서 방문자 수가
급격히 늘어날 경우 그 원인을 충실히 검증하기가 쉽지 않다. 소위 슬래시닷 효과(Slashdot effect)로
인해 좋은 아이디어가 다 사라질 수 있다. 그 이유는 최대 부하량의 예측이 어렵기 때문이다.
하지만 모두가 알고 있듯이 세상은 변하고 있다. 그리고 웹 서비스의 전제 조건도 발전을 거듭하고 있으며
오늘날에는 클라우드 컴퓨팅 및 PAAS(platform-as-a-service)라는 도구를 통해 애플리케이션을 더욱 쉽게 작성,
전개 및 배포할 수 있다. 앞으로 클라우드 플랫폼에서 Twitter를 작성하여 전개하게 되면 확장 가능한 Twitter를
만나볼 수 있을 것이다. 멋진 미래가 기대된다.
이 세 편의 기사에서는 소프트웨어 개발의 혁신적인 변화를 이끌고 있는 클라우드 컴퓨팅/PAAS에 대해
살펴본 후 현재 프리뷰 릴리스로 제공되고 있는 Java 개발을 위한 흥미롭고 새로운 플랫폼인 Google App
Engine for Java를 사용하는 방법에 대해 설명한다. 먼저 제공되는 애플리케이션 서비스 유형을 포함한 App
Engine for Java에 대한 개요를 살펴본 후 애플리케이션 예제를 검토한다. 두 예제 중 첫 번째 예제에서는
App Engine for Java Google Plugin for Eclipse를 사용한다. 이 첫 번째 애플리케이션 예제에서는 Java Servlet
API에 대한 App Engine for Java의 지원을 이용한다. Part
2에서는 App Engine for Java의 서블릿 및 GWT에 대한 지원을 각각 사용하여 간단한 연락처 관리 애플리케이션을
작성한다. 그리고 Part 3에서는 JDO(Java Data Objects)와 JPA(Java Persistence API)를 기반으로 작성된 사용자 정의
애플리케이션을 사용하여 App Engine for Java의 Java 기반 지속성 지원을 탐색한다.
서론은 여기에서 마무리 짓고 이제 본론으로 들어가자.
Google App Engine for Java 소개
유명한 검색 엔진을 개발한 업체이기도 한 Google에서 2008년 4월에 Google App Engine을 처음으로 발표했다. 초기
릴리스는 공백을 블록에 사용해야 한다고 생각하는(Python에 대한 책을 쓴 필자는 당연히 알고 있다.) Python 프로그래머만을
위한 것이었기에 많은 Java 개발자에게 실망을 안겨주었다. 이러한 사실을 알고 있던 Google에서는 2009년 4월에 Google
App Engine for Java를 발표하여 Java 개발자의 요구에 부응했다.
Google App Engine for Java는 엔터프라이즈 Java 개발을 위해 사용하기 쉬운 브라우저 기반 Ajax GUI, Eclipse 도구
지원 및 Google App Engine 플랫폼으로 구성된 엔드 투 엔드 솔루션을 제공한다. 사용 편의성과 다양한 도구는 다른
클라우드 컴퓨팅 솔루션과 비교되는 Google App Engine for Java의 장점이다.
App Engine for Java에서 애플리케이션을 개발한다는 것은 Google의 리소스를 사용하여 Java 오브젝트를 저장 및 검색한다는
것을 의미한다. 데이터 저장소는 BigTable을 기반으로 하지만 JDO 및 JPA 인터페이스가 지원되므로
BigTable에 직접 연결되지 않은 코드를 작성할 수 있다. 실제로 Google에서 많은 API에 대한 표준 기반 지원을 제공하고 있으므로
App Engine for Java 플랫폼 이외의 기술을 활용한 코드를 작성할 수 있다.
App Engine for Java에서는 다음과 같은 표준 Java API를 사용한다.
java.net.URL: HTTP 및 HTTPS 프로토콜을 이용한 다른 호스트와의 통신을 통해 서비스 검색
- JavaMail: 메일 메시지 전송
- Memcache에 대한 JCache(JSR 107) 인터페이스: 쿼리 및 계산 결과를 캐싱하기 위한 고속의 임시 분산 저장소 제공
 |
WebSphere/DB2에 App Engine for Java 전개하기
Google 및 IBM®의 담당자는 App Engine for Java를 발표하면서 DB2®/WebSphere®에도 동일한 샘플
애플리케이션을 전개했다. IBM에서는 App Engine for Java용으로 개발된 애플리케이션을 IBM WebSphere/DB2 스택에서도
실행할 수 있도록 Tivoli® LDAP 및 DB2를 위한 하위 레벨 Google API 지원을 제공하기 위해 노력하고 있다.
|
|
App Engine for Java에서는 다음과 같은 애플리케이션 서비스에 대한 지원도 제공한다.
- 사용자 인증 및 권한 부여
- CRON
- 데이터 가져오기/내보내기
- 방화벽 데이터에 대한 액세스
데이터 가져오기/내보내기 기능은 다른 소스와 App Engine for Java 애플리케이션 사이의 데이터 이동을 위해
중요하다. 이 기능은 App Engine for Java에 의존하지 않는 또 하나의 방법이다. Google의 CRON 지원은 특정 스케줄에
따라 방문하게 되는 내부 URL을 기반으로 하기 때문에 App Engine for Java에 대한 의존성이 낮은 좋은 서비스를
만들 수 있다. 사용자 인증 및 권한 부여 메커니즘은 App Engine for Java 전용이지만 ServletFilter,
특성 또는 Spring Security 플러그인을 작성하여 밀접한 연관성을 최소화할 수 있다.
첫 번째 App Engine for Java 애플리케이션 작성하기
지금까지 이 기사를 읽었다면 이제 첫 번째 App Engine for Java 애플리케이션을 작성할 준비를 모두 마친
셈이다. 가장 먼저 수행할 작업은 App Engine for
Java용 Google Plugin for Eclipse를 설치하는 것으로 이 작업을 수행하고 나면 이후 단계를 진행할 수 있다.
Eclipse IDE를 열면 그림 1과 같이 프린터 단추 옆에 새로운 세 개의 단추 즉, G가 표시된 파란색 공, G가
표시된 빨간색 도구 상자 및 App Engine for Java 소형 제트기 모양의 단추가 표시됩니다.
그림 1. Eclipse IDE에 추가된 깔끔한 모양의 새 단추
이들 단추의 기능은 다음과 같다.
- 파란색 공은 App Engine for Java 프로젝트 작성 마법사에 액세스하는 데 사용된다.
- 빨간색 도구 상자는 GWT 프로젝트를 컴파일하는 데 사용된다.
- 소형 제트기는 App Engine 프로젝트를 전개하는 데 사용된다.
이제 프로젝트 작성 마법사를 사용하여 두 개의 새 프로젝트를 작성한다. 그 중 하나는 서블릿을 기반으로 하고
다른 하나는 GWT를 사용하여 빌드된다. GWT 프로젝트는 도구 상자 기능을 사용하여 컴파일하며 App Engine 프로젝트를
전개할 준비가 완료되면 소형 제트기를 사용하여 프로젝트를 실행한다.
이제 App Engine for Java 프로젝트를 만들어보자. 먼저 파란색 공을 클릭하여 프로젝트 작성
마법사를 시작한다. 그런 다음 그림 2와 같이 gaej.example 패키지를 사용하여 SimpleServletApp이라는
애플리케이션을 작성한다.
그림 2. 새 프로젝트 시작하기
이 첫 번째 예제에서는 GWT 지원을 선택하지 않았다. 이 단계를 완료하면 Hello World 유형의 서블릿
기능을 제공하는 간단한 서블릿 기반 애플리케이션이 작성된다. 그림 3에서는 프로젝트의 스크린샷을 보여 준다.
그림 3. SimpleServletApp 프로젝트
이 서블릿 기반 새 프로젝트에 자동으로 포함된 JAR 파일은 다음과 같다.
- datanucleus-*.jar: 표준 JDO 또는 하위 레벨 BigTable API를 사용하여 App Engine for Java 데이터 저장소 액세스
- appengine-api-sdk.1.2.0.jar: App Engine for Java Security 등의 비표준 App Engine for Java 애플리케이션 서비스 사용
- geronimo-*.jar: JTA(Java Transaction Management API), JPA 등의 표준 Java API 사용
- jdo2-api-2.3-SNAPSHOT.jar: JDO API 사용
App Engine for Java의 영속성 API와 일부 App Engine for Java 애플리케이션 서비스를 사용하는 방법에 대해서는
이 기사의 Part 2부터 설명한다.
appengine.xml은 Google App Engine의 런타임 컨테이너를 구성하는 데 사용되는 파일이다. 이 예제에서는 appengine.xml을
사용하여 App Engine for Java에 대한 로깅 작업을 수행하도록 logging.properties 파일을 구성한다.
App Engine for Java 서블릿 애플리케이션 살펴보기
프로젝트 작성 마법사에서 모든 구성 작업을 완료하고 나면 App Engine for Java에 의해 기본적인 형식을 갖춘
Hello World 유형의 서블릿 애플리케이션이 작성된다. 이제 코드를 보면서 App Engine for Java Eclipse 도구를
사용하여 애플리케이션을 실행하는 방법을 살펴보자. 이 애플리케이션의 기본 진입점은 SimpleServletAppServlet이다(Listing 1 참조).
Listing 1. SimpleServletAppServlet
package gaej.example;
import java.io.IOException;
import javax.servlet.http.*;
@SuppressWarnings("serial")
public class SimpleServletAppServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
resp.setContentType("text/plain");
resp.getWriter().println("Hello, world");
}
}
|
이 서블릿은 web.xml에서 /simpleservletapp URI로 맵핑된다(Listing 2 참조).
Listing 2. web.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<servlet>
<servlet-name>simpleservletapp</servlet-name>
<servlet-class>gaej.example.SimpleServletAppServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>simpleservletapp</servlet-name>
<url-pattern>/simpleservletapp</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
|
또한 프로젝트 작성 마법사는 새 서블릿에 대한 링크를 가지고 있는 index.html 파일을 제공한다(Listing 3 참조).
Listing 3. 프로젝트 작성 마법사에 의해 생성된 index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set -->
<!-- the browser's rendering engine into -->
<!-- "Quirks Mode". Replacing this declaration -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout. -->
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- -->
<!-- Any title is fine -->
<!-- -->
<title>Hello App Engine</title>
</head>
<!-- -->
<!-- The body can have arbitrary html, or -->
<!-- you can leave the body empty if you want -->
<!-- to create a completely dynamic UI. -->
<!-- -->
<body>
<h1>Hello App Engine!</h1>
<table>
<tr>
<td colspan="2" style="font-weight:bold;">Available Servlets:</td>
</tr>
<tr>
<td><a href="simpleservletapp"/>SimpleServletAppServlet</td>
</tr>
</table>
</body>
</html>
|
 |
App Engine for Java의 호환성
Google에서는 App Engine for Java와 호환되는 도구 및 프레임워크 목록을 관리하고 있다(참고자료
참조). 예를 들어, App Engine for Java는 BeanShell, Groovy, Scala, JRuby, Jython 및 Rhino를 포함한 여러 JVM 언어를
지원한다. App Engine for Java에서는 Servlets, JSP, JPA, JavaMail, JAXP(Java API for XML Processing) 등의 몇 가지 Java
SE 및 Java EE API가 지원되므로 많은 기존 프레임워크를 기본적으로 사용할 수 있다. 예를 들어, Spring ORM에 대한 약간의
추가 작업이 필요하기는 하지만 Spring 프레임워크를 사용할 수 있다. Tapestry, Wicket, DWR, Tiles, SiteMesh 및 Grails도
사용할 수 있으며 Struts 2도 작은 패치를 적용하면 사용할 수 있다. App Engine for Java에서 작동하지 않는 프레임워크로는
Hibernate 및 JDBC(관계형 데이터베이스에 대한 지원 없음), JMX, Java WebServices, JAX-RPC 또는 JAX-WS, JCA, JNDI, JMS,
EJB 및 Java RMI가 있다.
|
|
이제 소수의 Java API만을 사용하여 간단한 서블릿 애플리케이션을 작성하는 작업이 완료되었다. 이
예제에서는 표준 Java API를 사용하여 App Engine 기능이 랩핑되어 있다는 점에 주목해야 한다. 즉, App
Engine for Java에서는 이러한 랩핑을 통해 Java 플랫폼에 사용할 수 있는 다양한 프레임워크를 지원한다.
애플리케이션 전개하기
App Engine for Java Eclipse 도구에서 서블릿 기반 애플리케이션을 실행하려면 먼저 프로젝트를 마우스 오른쪽 단추로
클릭하고 Run As 메뉴를 선택한 다음 파란색 공과 함께 표시된 "Web Application"을 선택한다(그림 4 참조).
그림 4. App Engine for Java 개발 서버 실행하기
이제 브라우저에서 http://localhost:8080/simpleservletapp를 입력하면 애플리케이션이 실행되면서 Hello World 메시지가 표시되어야 한다.
 |
GWT와 일반적인 Java 웹 애플리케이션 비교
GWT에서 작업할 경우에는 Java 코드를 JavaScript로 컴파일한 다음 브라우저에서 웹 애플리케이션 GUI를
실행하게 되며 그 결과는 일반적인 Java 웹 애플리케이션보다는 일반적인 GUI 애플리케이션에 가까운 모습으로
나타난다. GWT에는 브라우저에서 실행되는 클라이언트 구성 요소가 있다. 이 클라이언트 구성 요소는 사용자가
작성한 RMI 유형의 Java 클래스를 통해 Java 서버측 코드와 통신한다. |
|
App Engine for Java/GWT 애플리케이션 작성하기
지금까지 간단한 App Engine for Java 서블릿 애플리케이션의 작동 방법을 살펴보았으므로 이제는 GWT
애플리케이션을 위한 App Engine for Java Eclipse 도구를 살펴볼 차례이다. 먼저 Eclipse IDE 도구 모음에서
파란색 공을 클릭하여 Google 프로젝트 작성 마법사를 실행한다. 이번에는 그림 5와 같이 GWT에 대한 지원을
선택한다.
그림 5. App Engine for Java 프로젝트 작성 마법사를 사용하여 간단한 GWT 애플리케이션 작성하기
그림 6을 보면 알 수 있듯이 GWT 애플리케이션에는 간단한 서블릿 기반 애플리케이션에 비해 훨씬 많은 코드 아티팩트가
기본적으로 제공된다. 이 예제는 GWT에서 수행되는 GUI로 인사 서비스 애플리케이션과 통신한다.
그림 6. GWT 애플리케이션에 제공된 코드 아티팩트
GWT 애플리케이션에는 gwt-servlet.jar라는 추가 JAR 파일이 있으며 이 파일은 서블릿 기반 애플리케이션에서는 사용되지 않는다.
다음과 같은 아티팩트도 제공된다.
- src/gaej/example: SimpleGWTApp.gwt.xml: GWT 모듈 디스크립터
- src/gaej.example.server: GreetingServiceImpl.java: 인사 서비스의 구현
- src/gaej.example.client: GreetingService.java: 인사 서비스를 위한 동기 API
- src/gaej.example.client: GreetingServiceAsync.java: 인사 서비스를 위한 비동기 API
- src/gaej.example.client: SimpleGWTApp.java: 시작 GUI를 빌드하는 기본 진입점
- war/WEB-INF: web.xml:
GreetingServiceImpl을 구성하는 전개 디스크립터
- war: SimpleGWTApp.html: GWT GUI를 표시하는 HTML 페이지
- war: SimpleGWTApp.css: GWT GUI의 스타일시트
애플리케이션의 아키텍처와 소스 코드를 살펴보기 전에 먼저 실행 결과를 확인해 보자. 애플리케이션을
실행하기 위해 도구 모음에서 빨간색 도구 상자를 클릭하고 Compile 단추를 클릭한다. 그런 다음에는
앞에서 했던 대로 프로젝트를 마우스 오른쪽 단추로 클릭하고 Run As —> Web Application
메뉴 항목을 선택한다. 이번에는 GWT 애플리케이션 작업을 수행하고 있으므로 GWT Hosted Mode Console과
브라우저가 표시된다. 이제 웹 애플리케이션에서 자신의 이름을 입력하고 응답을 확인한다. 그림 7에서는
필자의 이름을 입력하고 받은 결과를 보여 준다.
그림 7. 샘플 GWT 애플리케이션 실행하기
이후 섹션에서는 예제 GWT 애플리케이션에 대해 설명한다. 참고자료에서 GWT 또는 GWT 튜토리얼에 대한 자세한 정보를 볼 수 있다.
GWT 애플리케이션 자세히 보기
제공된 구성을 바탕으로 살펴보면 Eclipse의 GWT 도구에서 HTML 프론트엔드 기능을 제공하는 시작
애플리케이션(SimpleGWTApp.html, Listing 10 참조)이 작성된다. 이 애플리케이션에서는
simplegwtapp.js와 simplegwtapp.nocache.js가 로드된다. 이 코드는 사용자의 Java 코드를 기반으로
GWT에서 생성된 JavaScript 코드로 gaej.example.client 패키지 아래의 src 디렉토리에 있다(Listing
6, 7 및 8 참조).
Listing 8에서는 GUI 작성을 위한 기본 진입점인 gaej.example.client.SimpleGWTApp를
보여 준다. 이 클래스는 GWT GUI 요소를 작성한 후 SimpleGWTApp.html의 HTML DOM 요소에 연결한다(Listing 10
참조). SimpleGWTApp.html에서는 nameFieldContainer와 sendButtonContainer라는
두 개의 DOM 요소(테이블의 열)를 정의한다. SimpleGWTApp 클래스는 RootPanel.get("nameFieldContainer")을
사용하여 DOM 요소에 연결된 패널에 액세스하여 이들 요소를 GUI 요소로 대체한다. 그런 다음 SimpleGWTApp
클래스는 이름을 입력 받고 인사말을 보내는 데 사용되는 텍스트 상자와 단추를 정의한다(Listing 10 참조).
SimpleGWTApp.gwt.xml에서 SimpleGWTApp 클래스가 entry-point 요소로 지정되어
있으므로 GWT는 이 클래스를 애플리케이션의 기본 진입점으로 인식한다.
SimpleGWTApp에는 sendButton이라는 단추가 연결되어 있다. 이
단추를 클릭하면 SimpleGWTApp가 GreetingService의 greetServer
메소드를 호출한다. GreetingService 인터페이스는 src/gaej.example.client.GreetingService.java에
정의되어 있다(Listing 6).
Ajax는 기본적으로 비동기식으로 작동되므로 GWT는 원격 서비스에 액세스하기 위해 비동기 인터페이스를 정의한다. SimpleGWTApp는
src/gaej.example.client.GreetingServiceAsync.java에 정의된 비동기 인터페이스를 사용한다(Listing 7
참조). GreetingServiceImpl(src/gaej.example.server.GreetingServiceImpl.java)에서는 GreetingService에
정의된 greetServer 메소드를 구현한다(Listing 5). GreetingServiceImpl.greetServer
메소드는 인사 메시지 String을 리턴하며 이 문자열은 SimpleGWTApp에서 작성된 대화 상자에 인사 메시지를 표시하는 데 사용된다.
GWT 모듈 디스크립터는 GUI 애플리케이션의 기본 진입점인 gaej.example.client.SimpleGWTApp를 선언한다(Listing 4 참조).
Listing 4. GWT 모듈 디스크립터(src/gaej/example/SimpleGWTApp.gwt.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN"
"http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/
distro-source/core/src/gwt-module.dtd">
<module rename-to='simplegwtapp'>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<!-- Other module inherits -->
<!-- Specify the app entry point class. -->
<entry-point class='gaej.example.client.SimpleGWTApp'/>
</module>
|
GreetingServiceImpl은 인사 서비스 애플리케이션의 실제 구현(Listing 5 참조)으로
서버측에서 실행되며 클라이언트 코드에서 원격 프로시저 호출을 통해 호출된다.
Listing 5. 인사 서비스 애플리케이션의 구현(src/gaej.example.server.GreetingServiceImpl.java)
package gaej.example.server;
import gaej.example.client.GreetingService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
/**
* The server side implementation of the RPC service.
*/
@SuppressWarnings("serial")
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
public String greetServer(String input) {
String serverInfo = getServletContext().getServerInfo();
String userAgent = getThreadLocalRequest().getHeader("User-Agent");
return "Hello, " + input + "!<br><br>I am running " + serverInfo
+ ".<br><br>It looks like you are using:<br>" + userAgent;
}
}
|
Listing 6의 GreetingService는 클라이언트 코드에서 사용되는 원격 프로시저 호출에 대한 인터페이스이다.
Listing 6. 동기 API(src/gaej.example.client.GreetingService.java)
package gaej.example.client;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
/**
* The client side stub for the RPC service.
*/
@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
String greetServer(String name);
}
|
Listing 7의 GreetingServiceAsync는 클라이언트 코드에서 사용되는 실제 인터페이스이다.
각 메소드는 원격 프로시저 호출이 완료될 때 사용자에게 비동기적으로 통보를 보내는 콜백 오브젝트를 제공한다. GWT는
기본적으로 Ajax를 사용한다. 클라이언트에서 Ajax를 사용할 경우에는 클라이언트와 비동기 호출을 차단하지 않는 것이 가장
좋다. 비동기 호출을 차단하는 것은 Ajax를 사용하는 목적에 부합되지 않는다.
Listing 7. 비동기 API(src/gaej.example.client.GreetingServiceAsync.java)
package gaej.example.client;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* The async counterpart of <code>GreetingService</code>.
*/
public interface GreetingServiceAsync {
void greetServer(String input, AsyncCallback<String> callback);
}
|
SimpleGWTApp에서는 대부분의 작업이 발생하며 GUI 이벤트를 등록한 다음 Ajax 요청을 GreetingService에 보낸다.
Listing 8. 시작 GUI를 빌드하는 애플리케이션의 기본 진입점(src/gaej.example.client.SimpleGWTApp.java)
package gaej.example.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class SimpleGWTApp implements EntryPoint {
/**
* The message displayed to the user when the server cannot be reached or
* returns an error.
*/
private static final String SERVER_ERROR = "An error occurred while "
+ "attempting to contact the server. Please check your network "
+ "connection and try again.";
/**
* Create a remote service proxy to talk to the server-side Greeting service.
*/
private final GreetingServiceAsync greetingService = GWT
.create(GreetingService.class);
/**
* This is the entry point method.
*/
public void onModuleLoad() {
final Button sendButton = new Button("Send");
final TextBox nameField = new TextBox();
nameField.setText("GWT User");
// You can add style names to widgets
sendButton.addStyleName("sendButton");
// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("nameFieldContainer").add(nameField);
RootPanel.get("sendButtonContainer").add(sendButton);
// Focus the cursor on the name field when the app loads
nameField.setFocus(true);
nameField.selectAll();
// Create the popup dialog box
final DialogBox dialogBox = new DialogBox();
dialogBox.setText("Remote Procedure Call");
dialogBox.setAnimationEnabled(true);
final Button closeButton = new Button("Close");
// You can set the id of a widget by accessing its Element
closeButton.getElement().setId("closeButton");
final Label textToServerLabel = new Label();
final HTML serverResponseLabel = new HTML();
VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.addStyleName("dialogVPanel");
dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
dialogVPanel.add(textToServerLabel);
dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
dialogVPanel.add(serverResponseLabel);
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
dialogBox.setWidget(dialogVPanel);
// Add a handler to close the DialogBox
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dialogBox.hide();
sendButton.setEnabled(true);
sendButton.setFocus(true);
}
});
// Create a handler for the sendButton and nameField
class MyHandler implements ClickHandler, KeyUpHandler {
/**
* Fired when the user clicks on the sendButton.
*/
public void onClick(ClickEvent event) {
sendNameToServer();
}
/**
* Fired when the user types in the nameField.
*/
public void onKeyUp(KeyUpEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
sendNameToServer();
}
}
/**
* Send the name from the nameField to the server and wait for a response.
*/
private void sendNameToServer() {
sendButton.setEnabled(false);
String textToServer = nameField.getText();
textToServerLabel.setText(textToServer);
serverResponseLabel.setText("");
greetingService.greetServer(textToServer,
new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
// Show the RPC error message to the user
dialogBox
.setText("Remote Procedure Call - Failure");
serverResponseLabel
.addStyleName("serverResponseLabelError");
serverResponseLabel.setHTML(SERVER_ERROR);
dialogBox.center();
closeButton.setFocus(true);
}
public void onSuccess(String result) {
dialogBox.setText("Remote Procedure Call");
serverResponseLabel
.removeStyleName("serverResponseLabelError");
serverResponseLabel.setHTML(result);
dialogBox.center();
closeButton.setFocus(true);
}
});
}
}
// Add a handler to send the name to the server
MyHandler handler = new MyHandler();
sendButton.addClickHandler(handler);
nameField.addKeyUpHandler(handler);
}
}
|
웹 전개 디스크립터(Listing 9의 web.xml)는 GreetingService를 서블릿 기반 웹 리소스로
맵핑한다. 이 웹 전개 디스크립터에서는 SimpleGWTApp에서 로드하여 호출할 수 있도록
GreetingService 서블릿을 /simplegwtapp/greet라는 이름으로
맵핑한다. 또한 SimpleGWTApp.html이 항상 로드되는 애플리케이션의 시작 페이지로 지정되어 있다.
Listing 9. GreetingServiceImpl을 구성하는 전개 디스크립터(war/WEB-INF/web.xml)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- Default page to serve -->
<welcome-file-list>
<welcome-file>SimpleGWTApp.html</welcome-file>
</welcome-file-list>
<!-- Servlets -->
<servlet>
<servlet-name>greetServlet</servlet-name>
<servlet-class>gaej.example.server.GreetingServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>greetServlet</servlet-name>
<url-pattern>/simplegwtapp/greet</url-pattern>
</servlet-mapping>
</web-app>
|
Listing 10의 SimpleGWTApp.html은 HTML 프론트엔드이다. 이 파일은 simplegwtapp.js와 simplegwtapp.nocache.js를
로드하는 페이지로 사용자의 Java 코드를 기반으로 GWT에 의해 생성된 JavaScript 코드이다. 앞에서 언급한 대로 이
코드는 gaej.example.client package 아래의 src 디렉토리에 있다(Listing 6, 7
및 8 참조).
Listing 10. GWT GUI를 표시하는 HTML 페이지(war/SimpleGWTApp.html)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<!-- The HTML 4.01 Transitional DOCTYPE declaration-->
<!-- above set at the top of the file will set -->
<!-- the browser's rendering engine into -->
<!-- "Quirks Mode". Replacing this declaration -->
<!-- with a "Standards Mode" doctype is supported, -->
<!-- but may lead to some differences in layout. -->
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!-- -->
<!-- Consider inlining CSS to reduce the number of requested files -->
<!-- -->
<link type="text/css" rel="stylesheet" href="SimpleGWTApp.css">
<!-- -->
<!-- Any title is fine -->
<!-- -->
<title>Web Application Starter Project</title>
<!-- -->
<!-- This script loads your compiled module. -->
<!-- If you add any GWT meta tags, they must -->
<!-- be added before this line. -->
<!-- -->
<script type="text/javascript" language="javascript"
src="simplegwtapp/simplegwtapp.nocache.js"></script>
</head>
<!-- -->
<!-- The body can have arbitrary html, or -->
<!-- you can leave the body empty if you want -->
<!-- to create a completely dynamic UI. -->
<!-- -->
<body>
<!-- OPTIONAL: include this if you want history support -->
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1'
style="position:absolute;width:0;height:0;border:0"></iframe>
<h1>Web Application Starter Project</h1>
<table align="center">
<tr>
<td colspan="2" style="font-weight:bold;">Please enter your name:</td>
</tr>
<tr>
<td id="nameFieldContainer"></td>
<td id="sendButtonContainer"></td>
</tr>
</table>
</body>
</html>
|
GWT를 사용하면 Listing 11과 같이 CSS를 통해 애플리케이션의 모습을 제어할 수 있다.
Listing 11. GWT GUI의 스타일시트(war/SimpleGWTApp.css)
/** Add css rules here for your application. */
/** Example rules used by the template application (remove for your app) */
h1 {
font-size: 2em;
font-weight: bold;
color: #777777;
margin: 40px 0px 70px;
text-align: center;
}
.sendButton {
display: block;
font-size: 16pt;
}
/** Most GWT widgets already have a style name defined */
.gwt-DialogBox {
width: 400px;
}
.dialogVPanel {
margin: 5px;
}
.serverResponseLabelError {
color: red;
}
/** Set ids using widget.getElement().setId("idOfElement") */
#closeButton {
margin: 15px 6px 6px;
}
|
Google App Engine에 전개하기
이 세계적인 킬러 애플리케이션(사용자에게 친근한 인사 애플리케이션이 절실히 필요한 상황이기 때문)을
작성한 후에는 전개하는 작업만 남게 된다. Google App Engine을 사용하는 주된 이유는 Google의 안정적인
인프라에 애플리케이션을 전개하여 애플리케이션의 확장성을 강화하기 위해서이다. App Engine 홈 페이지에
언급된 대로 Google App Engine은 "인프라에 대한 걱정 없이 수백만 명의 사용자까지 확장할 수 있는" 애플리케이션을
작성할 수 있는 플랫폼을 제공하기 위해 설계되었다. 이 인프라를 사용하려면 Google
App Engine for Java 계정이 필요하다.
세상의 다른 많은 서비스와 마찬가지로 이 서비스도 처음에는 무료로 사용할 수 있다. App Engine for
Java의 무료 버전을 사용하여 애플리케이션을 전개할 경우에는 약 5백만 회의 페이지뷰를 충분히 지원할 수
있는 용량의 CPU, 대역폭 및 저장소가 제공된다. 이 초기 용량을 초과한 이후에는 사용량에 따라 비용을
지불하게 된다. (물론 이 요금 체계는 이 기사를 집필하던 당시에 제공되고 있던 App Engine for Java 플랫폼의
프리뷰 릴리스에만 해당된다.)
계정을 받고 나면 App Engine for Java 사이트에서 빈 애플리케이션 목록을 볼 수
있다. 이 사이트에서 Create New Application 단추를 클릭하면 그림 8과 같은 양식이 표시된다. 애플리케이션
ID와 함께 확인 메시지가 표시된 후 고유 애플리케이션 이름과 설명을 입력한다.
이 ID는 애플리케이션의 app.yaml 파일에 저장된다. 그리고 ID는 변경할 수 없다. 애플리케이션에 대해 Google
인증을 사용하는 경우에는 애플리케이션에 액세스할 때 "GAEj Article For Rick Part 1"이 Sign In 페이지에
표시된다. 이제 App Engine for Java Eclipse 플러그인을 통해 gaejarticleforrick을
사용하여 Google App Engine에 애플리케이션을 전개할 수 있다.
그림 8. 새 App Engine for Java 애플리케이션 작성하기
애플리케이션 ID를 설정한 후에는 Eclipse에서 애플리케이션을 전개할 수 있다. 먼저 그림 9와 같이
Google App Engine 로고(날개와 꼬리가 있는 제트 엔진) 모양의 도구 모음 단추를 누른다.
그림 9. App Engine for Java Eclipse 플러그인
그림 10의 대화 상자에서 원하는 App Engine for Java 프로젝트가 선택되었는지 확인하고 Deploy를
클릭한다. 그러면 이메일 주소와 사용자 이름으로 구성된 Google 자격 증명을 요청하는 메시지가 표시된다.
그림 10. 프로젝트 전개하기
그림 10의 대화 상자에는 "App Engine Project setting" 링크가 있다. 프로젝트 설정
파일에서도 액세스할 수 있는 이 링크를 클릭한 다음 그림 11과 같이 애플리케이션 ID(이
경우에는 gaejarticleforrick)를 입력한다. 애플리케이션 ID를
입력하고 OK를 클릭한 다음 Deploy를 클릭한다.
그림 11. Google App Engine에 대한 프로젝트 설정
애플리케이션을 전개한 후에는 http://<application id>.appspot.com/에서 애플리케이션을
사용할 수 있다. http://gaejarticleforrick.appspot.com/에서도
애플리케이션을 사용할 수 있다.
결론
이 기사에서는 Google App Engine for Java를 소개했다. 지금까지 App Engine for Java에 대한 개요를
살펴본 후 App Engine for Java Google Plugin for Eclipse를 사용하는 기본적인 방법도 살펴보았다. 그리고
서블릿과 GWT를 기반으로 하는 간단한 두 시작 애플리케이션을 작성한 다음 GWT 애플리케이션을 Google App
Engine 플랫폼에 전개해 보았다.
이 기사의 예제에서는 YouTube 또는 Facebook 규모까지도 확장할 수 있는 Java 기반 애플리케이션을
손쉽게 작성하여 전개하는 데 도움이 되는 도구와 기능을 살펴보았다. Part
2에서는 App Engine for Java를 이용하는 Java 개발자에게 유용한 기능을 계속해서 살펴본다. 그리고
이 기사에서 실행해 본 예제 애플리케이션과는 많이 다른 사용자 정의 연락처 관리 애플리케이션을 작성하게
되며 이 애플리케이션은 Part 3에서도 App Engine for Java의 데이터 저장소 및 GUI 프론트엔드를 살펴보기
위한 주요 예제로 사용된다.
참고자료 교육
- "Google App Engine for Java,
Part 2: Building the killer app"(Rick Hightower 저, developerWorks, 2009년 8월): App Engine for Java를 이용하는 Java 개발자에게
유용한 기능을 계속해서 살펴본다.
-
"Google Web Toolkit, Apache Derby, Eclipse를 사용하여 Ajax 애플리케이션
구현하기, Part 1: 환상적인 프론트엔드"(Noel Rappin 저, developerWorks, 2006년 10월): GWT(Google Web Toolkit)를 사용하여 Ajax 프로그램을
작성하는 과정을 소개하는 네 편의 기사로 구성된 튜토리얼의 첫 번째 기사이다.
- "클라우드에 연결하기,
Part 1: 애플리케이션에서 클라우드 활용하기"(Mark O'Neill 저, developerWorks, 2009년 4월): 세 편의 기사를 통해 주요 공급자(Amazon,
Google, Microsoft® 및 SalesForce.com)의 클라우드 컴퓨팅 플랫폼에 대한 개요를 살펴본다.
- DataNucleus 프로젝트: Java 환경에서 애플리케이션 데이터를 관리하는 제품을 제공한다.
-
Google App Engine 홈 페이지: App Engine에 대한 자세한 정보를 볼 수 있다.
-
Google App Engine Java 설명서: App Engine for Java를 이용한 Java 개발에 유익한 정보를 제공한다.
-
Will it play in App Engine for Java?:
App Engine for Java와 호환되는 표준 Java API 및 프레임워크를 확인할 수 있다.
-
BigTable이란?: Google Research Publications에서 제공하는 BigTable에 대한 정보를 읽어보자.
-
"GWT: The most important announcement at JavaOne?"(Rick Hightower 저,
Java Developers Journal, 2006년 6월): GWT가 Java 애플리케이션 개발의 새 지평을 연 도구로 평가되는 이유를 알 수 있다.
제품 및 기술 얻기
토론
필자소개  | |  | Rick Hightower는 클라우드 컴퓨팅, GWT, Java EE, Spring 및 Hibernate 개발과 관련된
교육 서비스를 전문으로 제공하는 회사인 Mammatus Inc.의
CIO이다. 유명한 Java Tools for Extreme Programming의 공동 저자이며 TheServerSide.com에서
여러 해 동안 다운로드 수가 가장 높았던 Struts Live 초판의 저자이다. IBM developerWorks에
많은 기사와 튜토리얼을 기고하고 있는 그는 Java Developer's Journal의 편집위원으로 활동하고 있으며
DZone에도 Java 및 Groovy와 관련된 많은 글을 기고하고 있다. |
기사에 대한 평가
 |
| 이 문서 북마킹 하기
|
|