2011
마이크로소프트웨어

글: 안병현, 진성주, 변현규, 김진영, 임선용 | http://momo-time.com / 2011-08


<실전 강의실>

구글 앱 엔진과 안드로이드 하모니

구글 앱 엔진 XMPP 활용해
안드로이드 푸시 서비스 구현Ⅰ


최근 IT 업계의 이슈는 단연 스마트폰과 클라우드 컴퓨팅이다. 많은 스타트업 기업들이 클라우드 서비스를 사용하고 있는데 그 중에서도 구글 앱 엔진과 안드로이드를 택해 서비스하는 경우가 많다. 이에 3회의 연재를 통해 구글 앱 엔진과 안드로이드를 활용해 효과적으로 네트워크 통신을 하는 방법을 공유하려고 한다. 해당 내용은 <제11회 자바개발자컨퍼런스>에서 “Google App Engine과 Android 하모니”라는 주제로 발표한 바 있다.


애플의 아이폰으로 시작된 스마트폰 열풍은 안드로이드의 가세로 거세지고 있다. 주위를 둘러보면 스마트폰 열풍을 쉽게 느낄 수 있다. 이같은 스마트폰 열풍에 따라 다양한 애플리케이션이 등장하고 있으며, 최근에는 카카오톡이나 마이피플과 같은 스마트폰 메신저 서비스를 통해 연락하는 것이 일반화돼 피처폰 사용자는 따돌림 아닌 따돌림을 받기도 한다. 이와 함께 대표적인 스마트폰 게임 앵그리 버드는 게임 판매 수익으로 천문학적인 돈을 벌어들이고 있다. 이렇게 많은 스마트폰 애플리케이션들이 성공적인 수익을 내는 것에 자극을 받아 우리나라 IT 업계가 다시 한 번 꿈틀대고 있다.

 

클라우드 컴퓨팅

스마트폰 애플리케이션 중에는 스마트폰 기기 자체의 리소스 만을 활용해 기능을 수행하는 것이 있는 반면 별도의 서버를 통해 랭킹 시스템이나 게시판 기능 등 다양한 서비스를 제공하는 것도 있다. 이렇게 별도의 서버를 구축하면 훨씬 다양하고 폭넓은 서비스를 제공할 수 있다는 장점이 있지만 서버 구입, 설치, 관리 등에 들어가는 비용을 무시할 수 없는 실정이다. 특히 창업을 시작하는 작은 기업들이 서비스 확장을 고려해 거대한 서버를 구축하는 것은 너무나 힘든 일이다. 또한 애플 앱스토어나 구글 안드로이드 마켓 등을 통해 애플리케이션을 전세계에 쉽게 배포할 수 있는 환경에서 자신이 만든 애플리케이션이 얼마나 많은 사용자를 끌어들일 수 있을지 예상하는 것은 불가능하다고 볼 수 있다. 우리는 클라우드 컴퓨팅 서비스를 사용해 이같은 문제점들을 효과적으로 해결할 수 있다.

 

클라우드 컴퓨팅 서비스란 다른 기업의 거대한 서버 자원의 일부를 빌려서 사용하고 그에 맞는 금액을 지불하는 것이라고 할수 있다. 클라우드 컴퓨팅 서비스를 크게 나눠 보면 IaaS(Infrastructure as a Service), PaaS(Platform as a Service), SaaS(Software as a Service)로 구분할 수 있다. 문자 그대로 각각 소프트웨어, 플랫폼, 인프라스트럭처를 제공한다. 현재 아마존, 마이크로소프트, 구글, KT 등 많은 기업들이 클라우드 컴퓨팅 서비스를 제공하고 있다. 아마존과 KT는 IaaS이며, 구글과 마이크로소프트는 PaaS다. 따라서 아마존이나 KT의 클라우드 컴퓨팅 서비스를 사용한다면 자신의 취향에 맞춰 서버를 구성할 수 있다는 장점이 있지만 직접 실행환경이나 DBMS 등을 구축해야 한다는 단점도 있다. 반대로 구글, 마이크로소프트의 클라우드 컴퓨팅 서비스를 사용하면 좀 더 편리하게 서비스를 구축할 수 있다. 단, 각각의 서비스에 종속적이 된다는 것은 단점으로 지적되고 있다.

 

구글 앱 엔진

구글 앱 엔진은 구글의 인프라스트럭처를 활용해 클라우드 컴퓨팅을 제공하는 PaaS다.

 

다양한 클라우드 컴퓨팅 서비스 중에서 구글 앱 엔진을 선택하게 된 데는 몇 가지 이유가 있다. 가장 먼저 비용이 저렴하다는 장점이 있다. 구글 앱 엔진은 기본적으로 무료로 사용할 수 있다.

 

구글 앱 엔진 계정 생성
<화면 1> 구글 앱 엔진 계정 생성

 

직접 서버를 구축하기 위해 서버를 구입하고 설치하는 등에 필요한 비용을 절약할 수 있으며, 다른 클라우드 컴퓨팅 서비스 대비 저렴하다. 다른 클라우드 컴퓨팅 서비스처럼 구글 앱 엔진 역시 사용량에 따라 비용을 청구하며, CPU Time, Bandwidth in, Bandwidth out, Storage 등을 기준으로 삼는다.

 

두 번째로 사용이 쉽다는 장점이 있다. 직접 서버환경을 구축할 필요가 없으며, 파이썬, 자바, 고의 세 가지 언어를 지원한다. 많은 사용자층이 있는 자바를 지원한다는 것은 큰 장점이다. 이클립스 플러그인을 통해 원클릭으로 쉽게 배포할 수 있다.

 

세 번째로 확장이 쉽다. 대시보드를 통해 사용량을 확인하고 몇 가지 과금정책 중에서 원하는 것을 선택하면 CPU Time이나 Bandwidth, Storage를 쉽게 늘릴 수 있다.

 

마지막으로 구글이라는 거대한 기업이 관리하고 있기 때문에 비교적 안전하다고 볼 수 있다. 구글 앱 엔진 SDK는 현재 버전 1.5.1로 code.google.com/appengine에서 더 자세히 살펴볼 수 있다.

 

앞서 구글 앱 엔진은 사용이 쉽다는 장점이 있다고 했다. 모든 예제의 기본인“Hello world”를 통해 구글 앱 엔진과 서비스를 배포하는 법을 알아보자. 우선 https://appengine.google.com/에 접속해 구글 앱 엔진 계정을 생성한다. 갖고 있는 구글 계정을 이용하거나 새로 가입하면 된다. <Create Application> 버튼을 누르면 SMS 인증을 받아 구글 앱 엔진 계정을 생성하게 된다. SMS 인증을 받기 위한 핸드폰 번호가 010 1234 5678이라면 우리나라 국가번호 82를 포함하고 그 뒤에 0을 뺀 82 10 1234 5678을 입력하면 된다. 하나의 전화번호로 하나의 계정만 생성할 수 있으며, 하나의 계정당 10개의 서비스를 생성할 수 있다. 구글 앱 엔진 계정을 생성한 뒤 다시 <Create Application> 버튼을 누르면 애플리케이션 고유의 식별자와 이름을 입력해 애플리케이션을 생성할 수 있다. 애플리케이션을 생성해야 그 애플리케이션에 프로젝트를 배포할 수 있다.

 

기본설정으로 하나 생성해 보자.

 

언어는 자바를 사용하며 IDE는 이클립스를 사용하겠다. 우선 구글 앱 엔진 SDK를 설치해야 하는데 이클립스 플러그인을 설치하면 자동으로 SDK까지 다운받아 설치된다. 관련 웹사이트(code.google.com/intl/ko/eclipse/docs/getting_started.html)에 접속해 사용 중인 이클립스 버전에 맞는 업데이트 위치를 확인하고 설치한다. 이클립스 3.6 헬리오스(Helios) 버전의 경우 [Help] 메뉴의 [Install New Software]를 선택한 뒤 [Work with] 란에 http://dl.google.com/eclipse/plugin/3.6을 입력한다. 그 다음 설치할 목록을 체크하고 설치한다.

 

이클립스 구글 앱 엔진 플러그인 설치 이클립스 구글 앱 엔진 플러그인 설치

<화면 2> 이클립스 구글 앱 엔진 플러그인 설치

 

구글 앱 엔진 플러그인 설치가 끝나면 프로젝트를 생성한다.

[New]의 [Other 프로젝트]를 잘 살펴보면 [구글] 폴더에 [웹 애플리케이션 프로젝트] 항목을 찾을 수 있다. 웹 애플리케이션 프로젝트를 선택하고 프로젝트 이름과 패키지 이름을 작성한 뒤 [구글 SDKs] 항목을 살펴보자. 구글 웹 툴킷은 사용하지 않으므로 체크박스를 해제한다. 구글 앱 엔진 SDK 버전이 최신의 것인지 확인하고 <Finish> 버튼을 클릭해 프로젝트를 생성한다. 생성된 프로젝트를 보면 구글 앱 엔진 SDK가 자동으로 추가돼 있고 web.xml, appengine-web.xml 파일을 통해 기본 설정을 확인할 수 있다. 자동 생성된 파일을 살펴보고 이제 구글 앱 엔진에 프로젝트를 배치하자.

 

“Hello World” 프로젝트 생성
<화면 3> “Hello World” 프로젝트 생성

 

이클립스의 툴바에서 구글 앱 엔진 아이콘을 클릭하거나 프로젝트를 오른쪽 버튼으로 클릭해 [구글] 메뉴의 [Deploy to App Engine] 메뉴를 선택하면 구글 로그인 페이지가 뜬다. 앞서 생성한 계정으로 로그인 한 뒤 [Deploy] 창이 뜨면 [App Engine project settings] 메뉴를 클릭, 이 프로젝트를 배치할 애플리케이션 ID와 버전을 선택한다. 애플리케이션 ID는 애플리케이션을 생성할 때 입력했던 것을 입력하면 된다. 버전은 기본값이 1이며, 하나의 애플리케이션에 여러 버전의 프로젝트를 배치할 수 있다. 애플리케이션에서 설정한 버전의 프로젝트로 서비스된다. 배치가 완료되면 Application ID.appspot.com을 통해 성공적으로 배치됐는지 확인할 수 있다.

 

“Hello World”배치 성공
<화면 4> “Hello World”배치 성공

 

XMPP 소개

우리는 지금 구글 앱 엔진을 이용해 스마트폰, 특히 안드로이드 애플리케이션을 위한 서버를 구축하고 있다. 스마트폰은 항상 휴대할 수 있다는 장점이 있는데 이같은 장점을 잘 살리기 위해서는 즉각적으로 서비스를 제공할 수 있어야 한다. 따라서 스마트폰 애플리케이션을 위한 서버라면 푸시 서비스를 제공해야 한다. 현재 구글에서는 안드로이드에 푸시 서비스를 제공하기 위해 애플의 APNS와 같이 C2DM이라는 자체 푸시 서버를 운영하고 있다. 하지만 C2DM은 안드로이드 버전 2.2 이상에서만 동작하기 때문에 현재 약 30%에 달하는 2.1 버전 이하의 사용자들을 포용할 수 없다. 그래서 XMPP 프로토콜을 사용해 안드로이드 버전에 상관 없이 푸시 서비스를 제공하려고 한다.

 

XMPP는 eXtensible Messaging and Presence Protocol의 약자로서 메신저 서비스를 개발하기 위해 만들어진 프로토콜이다. XMPP 서버와 정의된 XML 데이터를 주고받음으로써 사용자들이 메시지를 주고받을 수 있도록 고안됐다. xmpp.org를 방문하면 정의된 XML 데이터 형태를 살펴볼 수 있다.

 

현재 구글의 구글토크, 페이스북의 채팅 서비스 등 많은 곳에서 XMPP이 사용되고 있다. XMPP는 기본적으로 사용자들이 메시지를 주고받는 메신저를 위해 개발됐다. 따라서 각각의 사용자를 구분할 수 있는 식별자가 필요하다. XMPP는 JID라는 식별자를 통해 각각의 사용자를 구분한다. JID의 형태는 다음과 같다.

 

UserID@xmpp.com/ResourceName

 

이메일 주소와 비슷한 형태로 @ 앞에 있는 것이 ID, @ 뒷 부분이 XMPP 서버주소다. 여기까지는 일반적인 이메일 주소와 같다. 하지만‘/’뒤에 있는 ResourceName이 중요한 역할을 한다.

 

XMPP를 사용하면 하나의 ID로 PC, 안드로이드, 아이폰 등 다양한 리소스에서 다중접속이 가능하다. XMPP는 Resource Name까지 하나의 JID로 보기 때문에 JID가 다를 경우 각각의 다른 사용자로 인식한다.

 

따라서 사용자 A가 하나의 ID로 PC와 스마트폰을 이용해 xmpp.com 서버에 접속하더라고 다른 사용자 B가 친구목록을 확인하고 사용자 A의 PC 혹은 스마트폰 한 곳으로만 메시지를 보내는 것도 가능하다.

 

XMPP를 사용해 친구와 메시지를 주고받는 과정을 간단하게 요약하면 서버에 접속하고 상태값을 변경한 후 메시지를 전송하는 3단계로 볼 수 있다. 물론 내부적으로는 좀 더 복잡한 과정을 거치지만 기본적인 컨셉은 그렇게 어렵지 않다. 먼저 서버에 접속하는 단계를 살펴보자. 일반적인 웹사이트에 로그인하는 것처럼 ID와 패스워드 그리고 Resource Name을 서버에 전송함으로써 XMPP 서버에 접속한다. 이제 상태값을 변경해야 한다. 현재 사용 중인 메신저 서비스를 떠올려보자. 여러분은 온라인 혹은 자리비움 등의 상태값을 설정할 수 있다. XMPP는 이렇게 사용자의 상태값을 갖고 있다. <리스트 1>의 소스를 통해 상태값을 변경할 수 있다.

 

<presence type = 'chat'>
<show> chat </show>
<status> Now Available </status>
</presence>

<리스트 1> XMPP 사용자 상태값 변경

 

상태값을 변경했으면 친구에게 메시지를 보내는 과정만 남았다. 이 역시 정의된 XML 데이터를 서버에 전송하는 것으로 간단하게 진행할 수 있다. <리스트 2>의 XML 데이터 예시를 살펴보자.

 

<message
    from='client1@mysite.com/android'
    to='client2@mysite.com/android'
    type='chat'>
<body> Hi! </body>
</message>

<리스트 2> XMPP 메시지 전송

 

정의된 XML 형식에 따라 누가 누구에게 메시지를 전송하는지 JID를 입력하고 메시지 내용과 함께 XMPP 서버에 전송하면 간단하게 메시지를 주고받을 수 있다.

 

구글 앱 엔진의 XMPP

앞서 살펴본 XMPP는 다양한 서비스에서 이미 많이 사용되고 있다. 구글 앱 엔진에서는 XMPP를 제공하고, 구글 앱 엔진에서 제공하는 XMPP를 사용하면 쉽게 푸시 서비스를 구축할 수 있다.

 

먼저 구글 앱 엔진에서 제공하는 XMPP를 어떻게 사용할 수 있는지 알아보자.

 

구글 앱 엔진 XMPP 오버뷰
<화면 5>구글 앱 엔진 XMPP 오버뷰

 

구글 앱 엔진 사이트(code.google.com/intl/ko-KR/appengine/docs/)에서 Service→XMPP→Overview(code.google.com/intl/ko-KR/appengine/docs/java/xmpp/overview.html)로 들어가면 구글 앱 엔진에서 XMPP가 어떻게 구성되며 어떤 코드를 넣고 테스트해야하는지 설명돼 있다. XMPP는 메시지 프로토콜이다. 국민 메신저인 네이트온을 생각하면 이해하기가 쉽다. 네이트온 클라이언트와 네이트온 서버, 네이트온 아이디가 있듯 XMPP 서버, XMPP 클라이언트, ID에 해당하는 JID가 존재한다. 구글 앱 엔진에서 제공하는 것은 XMPP 클라이언트다. 즉, 네이트온 아이디가 하나 발급되는 것이라고 생각하면 편하다. JID의 주소는applicationId@appspot.com가 되는데 applicationId는 구글 앱 엔진에서 인스턴스 계정을 만들 때 기입한 아이디다. 클라이언트가 있으니 어딘가에 접속할 수 있는 XMPP 서버가 있어야 한다. 구글에서 내부적으로 appspot.com XMPP 서버를 관리하고 있다. 구글 앱 엔진에서 몇 가지 설정만 하면 applicationId@appspot.com 아이디가 하나 생성되고 이 아이디로 신청되는 친구, 메시지, 친구의 상태값을 모두 알 수 있는 것이다.

 

푸시 서비스는 서버에서 클라이언트 메시지를 보내는 효과와 같다고 생각할 수 있다. 따라서 applicationId@appspot.com 계정에서 클라이언트 사용자 ID로 메시지를 보내기만 하면 된다.

먼저 샘플 프로젝트를 생성해 보자.

 

샘플 프로젝트 생성
<화면 6>샘플 프로젝트 생성

 

샘플 프로젝트를 생성하면 기본적으로 친구를 추가했을 경우, 메시지를 받았을 경우, 상태값이 변경됐을 경우에 사용자가 원하는 특정한 작업 수행을 하지 못한다. 구글 앱 엔진에서는 특정 이벤트(친구 추가, 상태값 변경, 메시지 수신 등)가 발생했을 때 알려줄 수 있도록 inbound service를 활성화시켜야 해당 이벤트에 특정 처리를 할 수 있다. 활성화 처리는 설정 파일인 /war/WEB-INF/appengine-web.xml 파일을 열어 수정하면 된다.

 

/war/WEB-INF/appengine-web.xml

<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app
xmlns="http://appengine.google.com/ns/1.0">
    <application></application>
    <version>1</version>
    <system-properties>
        <property
        name="java.util.logging.config.file"
        value="WEB-INF/logging.properties" />
    </system-properties>
    <inbound-services>
        <service>xmpp_message</service>
        <service>xmpp_presence</service>
        <service>xmpp_subscribe</service>
    </inbound-services>
</appengine-web-app>

<리스트 3> XMPP Inbound Service 활성화

 

굵게 처리된 부분을 활성화했다면 이제 특정 이벤트를 처리할 수 있다. 정상적으로 활성화됐는지 확인하기 위해 웹 애플리케이션을 구동시키고 다음의 관리자 사이트(localhost:8888/_ah/admin)에 접속해 보자. xmpp_message, xmpp_presence, xmpp_subscribe를 Inbound-Service로 처리했기 때문에 왼쪽 메뉴에 XMPP가 보이는 것을 확인할 수 있다. XMPP 메뉴로 들어가면 <화면 7>과 같이 로컬에서 XMPP를 테스트할 수 있도록 입력폼을 제공한다.

 

XMPP 관리자 페이지
<화면 7>XMPP 관리자 페이지

 

inbound service로 설정된 xmpp_message의 경우 메시지가 들어왔을 때마다 특정한 URL로 다시 요청을 포워딩한다. 메시지가 오면 /_ah/xmpp/message/chat/이라는 주소로 애플리케이션에게 요청을 포워딩해 특정한 작업을 가능케 하는 것이다.

 

1. 특정한 사용자가 applicationId@appspot.com 주소로 메시지 전송
2. appspot.com XMPP 서버가 메시지 수신
3. 애플리케이션에서 xmpp가 inbound service됐는지 확인
4. 특정한 URL로 메시지 포워딩

 

xmpp_presence, xmpp_subscribe도 주소만 다를뿐 같은 과정을 거친다. 요청되는 주소를 정리하면 <표 1>과 같다.

 

인터페이스 역할
xmpp_message /_ah/xmpp/message/chat/
xmpp_presence /_ah/xmpp/presence/available/
/_ah/xmpp/presence/unavailable/
/_ah/xmpp/presence/probe/
xmpp_subscribe /_ah/xmpp/subscription/subscribe/
/_ah/xmpp/subscription/subscribed/
/_ah/xmpp/subscription/unsubscribe/
/_ah/xmpp/subscription/unsubscribed/

<표 1> inbound service URL

 

각 주소로 이벤트가 일어날 때마다 요청되므로 web.xml 파일에 해당 주소를 처리할 서블릿을 등록하고 사용자 정의 처리를 한다.

 

사용자 등록&삭제

서블릿 매핑을 위해 /war/WEB-INF/web.xml 파일을 열어 굵게 표시한 부분을 추가한다. /_ah/xmpp/subscription/subscribe 요청에 SubscriptionServlet 서블릿이 호출될 것이다.

 

/war/WEB-INF/web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/webapp_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>SubscriptionServlet</servlet-name>
<servletclass>
org.softwaregeeks.xmpp.SubscriptionServlet</servletclass>
</servlet>
<servlet-mapping>
<servlet-name>SubscriptionServlet</servlet-name>
<url-pattern>/_ah/xmpp/subscription/subscribe/</urlpattern>
</servlet-mapping>
</web-app>

<리스트 4> SubscriptionServlet 서블릿 등록

 

실제로 처리할 서블릿을 만들면 다음과 같다. 구글 앱 엔진에서는 XMPP를 쉽게 처리하기 위해 XMPPService라는 인터페이스를 제공한다. XMPPServiceFactory를 사용해 실제 객체를 얻은 후 사용할 수 있다. XMPPService를 사용하면 개발자는 XMPP 메시지 내용을 몰라도 쉽게 사용할 수 있는 객체로 반환해 돌려준다. parseSubscription 메소드가 request 객체를 받아 개발자가 쉽게 사용할 수 있는 Subscription 객체로 반환해 주는 것이다.

 

/src/org/softwaregeeks/xmpp/SubscriptionServlet.java

package org.softwaregeeks.xmpp;

import java.io.IOException;
importjavax.servlet.http.*;
import com.google.appengine.api.xmpp.Subscription;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

@SuppressWarnings("serial")
public class SubscriptionServlet extends HttpServlet {

   XMPPServicexmppService = XMPPServiceFactory.getXMPPService();

   public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException {
        doPost(request,response);
    }
    public void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException {
        Subscription subscription = xmppService.parseSubscription(request);
        String from = subscription.getFromJid().getId();
        String to = subscription.getToJid().getId();
        System.out.println("From : " + from);
        System.out.println("To : " + to);
    }
}

<리스트 5> SubscriptionServlet 서블릿

 

어떻게 요청되고 응답받는지를 체크하기 위해 단순히 추가를 원하는 사용자의 ID 값과 추가해야 하는 사용자의 ID 값을 출력해 보자. 이제 테스트를 위해 웹 애플리케이션을 동작시킨 후 관리자 페이지(localhost:8888/_ah/admin/xmpp)에 접속해 보자.

Subscription을 테스트하기 위해 Message Type을 Subscription으로 선택하고 [From] 입력창에 from@xmpp.org, [To] 입력창에 to@xmpp.org를 입력하고 Subscribe initiated 메뉴 선택 후 <Make Request> 버튼을 클릭한다. 요청이 이뤄지고 이클립스의 콘솔 화면에 <리스트 6>과 같은 로그가 남는다.

 

From : from@xmpp.org
To : to@xmpp.org

<리스트 6> 콘솔화면

 

만일 서블릿이 매핑되지 않은 상태에서 요청하면 Message send failure HTTP ERROR 404 오류가 출력될 것이다.

친구 삭제는 /_ah/xmpp/subscription/unsubscribe/ URL을 서블릿 매핑하며 관리자 페이지에서 Notification Type을 변경해 테스트하면 된다.

 

사용자 상태 변경

네이트온을 생각해 보자. 접속한 사용자의 상태를 변경할 수 있다. 온라인, 로그오프, 자리비움, 바쁨, 다른용무중 등 사용자의 상태를 다른 사용자에게 알려주는 기능도 있다. 여기에서 주목해야 할 것은 로그오프 상황일 때다. 사용자가 로그오프되면 구글 앱 엔진의 XMPP 계정(applicationId@appspot.com)으로 로그오프됐다는 상태변경 값이 이벤트가 들어온다.

 

이제 다른 사용자의 상태 변경을 어떻게 알 수 있는지 살펴보자.

 

/war/WEB-INF/web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/webapp_
2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>PresenceServlet</servlet-name>
<servletclass>
org.softwaregeeks.xmpp.PresenceServlet</servletclass>
</servlet>
<servlet-mapping>
<servlet-name>PresenceServlet</servlet-name>
<url-pattern>/_ah/xmpp/presence/available/</url-pattern>
</servlet-mapping>
</web-app>

<리스트 7> PresenceServlet 서블릿 등록

 

XMPP에서는 상태값을 Presence라고 부른다. web.xml에 매핑작업을 했으니 PresenceServlet를 하나 만들어 보자.

 

/src/org/softwaregeeks/xmpp/PresenceServlet.java

package org.softwaregeeks.xmpp;

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.xmpp.Presence;
import com.google.appengine.api.xmpp.PresenceShow;
import com.google.appengine.api.xmpp.PresenceType;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

@SuppressWarnings("serial")
public class PresenceServlet extends HttpServlet {

XMPPServicexmppService =
XMPPServiceFactory.getXMPPService();

   public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException {
        doPost(request,response);
    }
    public void doPost(HttpServletRequest request,
HttpServletResponse response) throws IOException {
        Presence presence = xmppService.parsePresence(request);
        String from = presence.getFromJid().getId();
        String to = presence.getToJid().getId();
        System.out.println("From : " + from);
        System.out.println("To : " + to);
        System.out.println(presence.getPresenceType());

        xmppService.sendPresence(presence.getFromJid(), PresenceType.AVAILABLE, PresenceShow.CHAT, "XMPP");
    }
}

<리스트 8> PresenceServlet 서블릿

 

동일하게 request를 Presence 객체로 반환해 처리한다. 여기서 다시 sendPresence 메소드를 사용해 현재 계정의 상태를 다시 반환함으로써 서로의 상태값을 공유할 수 있도록 한다. 웹 애플리케이션을 구동하고 구글 앱 엔진 관리자 페이지에서 테스트하면 콘솔화면에 <리스트 9>와 같은 값이 찍히는 것을 확인할 수 있다.

 

From : from@xmpp.org
To : to@xmpp.org
AVAILABLE
Sending an XMPP Presence:
    To JID:
        from@xmpp.org
    Show:
        chat
    Status:
        XMPP

<리스트 9> 콘솔화면

 

메시지 수신&발신

구글 앱 엔진에서는 메시지의 수신과 발신을 쉽게 할 수 있다. 수신은 동일하게 URL로 요청이 오므로 서블릿을 만들어 매핑만 하면 처리할 수 있고, 발신은 MessageBuilder 클래스를 활용하면 처리가 가능하다. 예제로 살펴보자.

 

/war/WEB-INF/web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/webapp_ 2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>MessageServlet</servlet-name>
<servletclass>
org.softwaregeeks.xmpp.MessageServlet</servletclass>
</servlet>
<servlet-mapping>
<servlet-name>MessageServlet</servlet-name>
<url-pattern>/_ah/xmpp/message/chat/</url-pattern>
</servlet-mapping>

<리스트 10> MessageServlet 서블릿 매핑

 

src/org/softwaregeeks/xmpp/MessageServlet.java

package org.softwaregeeks.xmpp;

import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.google.appengine.api.xmpp.JID;
import com.google.appengine.api.xmpp.Message;
import com.google.appengine.api.xmpp.MessageBuilder;
import com.google.appengine.api.xmpp.MessageType;
import com.google.appengine.api.xmpp.XMPPService;
import com.google.appengine.api.xmpp.XMPPServiceFactory;

@SuppressWarnings("serial")
public class MessageServlet extends HttpServlet {

    XMPPServicexmppService = XMPPServiceFactory.getXMPPService();

   public void doGet(HttpServletRequest request,
HttpServletResponse response) throws IOException {
        doPost(request,response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 메시지 수신
        Message message = xmppService.parseMessage(request);
        String from = message.getFromJid().getId();
        System.out.println("From : " + from);
        System.out.println("MessageType : " +
        message.getMessageType());
        System.out.println("Body : " + message.getBody());

        // 메시지 발신
        MessageBuildermessageBuilder = new MessageBuilder();
        messageBuilder.withRecipientJids(new JID("to@gmail.com"));
        messageBuilder.withMessageType(MessageType.NORMAL);
        messageBuilder.withBody("Hi!");
        Message reply = messageBuilder.build();
        xmppService.sendMessage(reply);
    }
}

<리스트 11> MessageServlet 서블릿

 

콘솔화면

From : from@xmpp.org
MessageType : CHAT
Body : Message
Sending an XMPP Message:
    Body:
        Hi!
    Type:
        normal
    RawXml:
        false
    To JIDs:
        to@gmail.com

<리스트 12> MessageServlet 매핑

 

누구에게 보내고 무슨 내용을 넣을 것인지만 입력하면 쉽게 메시지를 전송할 수 있다.

 

구글 앱 엔진 XMPP API

구글 앱 엔진에서 XMPP는 com.google.appengine.api.xmpp 패키지에 정의돼 있다. 표로 정리하면 <표 2>와 같다.

 

인터페이스 역할
XMPPService Interface for accessing XMPP status information, sending XMPP messages, and parsing XMPP responses.
클래스 역할
JID Object representing a single Jabber ID.
Message Class that represents an XMPP message.
MessageBuilder Builder used to generate Message instances to represent incoming or outgoing XMPP messages.
Presence Represents presence information returned by the server.
PresenceBuilder Builder used to generate Presence instances to represent incoming XMPP presence stanzas.
SendResponse Represents results of sending a message.
Subscription Represents an incoming subscription stanza from the server.
Subscription Builder Builder used to generate Subscription instances to represent incoming XMPP subscription stanzas.
XMPPService Factory Constructs an instance of the XMPP service.
ENUM 역할
MessageType Types of messages, from RFC3921.
PresenceShow Values for the ‘show’sub-stanza of presences, taken from RFC3921.
PresenceType Values for the ‘type’attribute of presences, taken from RFC3921.
SendResponse.Status Possible per-id responses to sending a message.
SubscriptionType Values for the ‘type’attribute of presences, taken from RFC3921.
Exception 역할
XMPPFailure Exception XMPPFailureException is thrown when any unknown error occurs while communicating with the XMPP service.

<표 2> XMPP API

 

10여 개의 클래스와 인터페이스로 이뤄진 간단한 패키지다. 사용자 추가 및 삭제, 상태값 변경, 메시지 수신&발신을 쉽게 해결할 수 있다.

 

이렇게 클라우드 컴퓨팅의 대표적인 구글 앱 엔진을 알아봤다. XMPP가 무엇인지 구글 앱 엔진에서 XMPP을 활용하면 푸시 서비스를 쉽게 구축할 수 있는데 구글 앱 엔진에서 XMPP가 어떻게 활용되는지도 예제를 중심으로 살펴봤다.

 

다음 호에서는 실제로 푸시 서비스를 구축하기 위해 서버의 아키텍처와 안드로이드의 아키텍처를 알아보자.



/필/자/소/개/

필자 안병현, 진성주, 변현규, 김진영, 임선용

안병현, 진성주, 변현규, 김진영, 임선용 | http://momo-time.com

SW 마에스트로 1기 회원들로 현재 MOMO라는 팀명으로 활동하고 있고 <제11회 자바개발자컨퍼런스>에서 공동 발표를 진행한 바 있다. 함께 성장하며 배운 것을 나누려고 노력하고 있다.



※ 본 내용은 (주)마소인터렉티브(http://www.imaso.co.kr/)의 저작권 동의에 의해 공유되고 있습니다.
    Copyright ⓒ Maso Interactive Corp. 무단전재 및 재배포 금지

맨 위로
맨 위로