목공책 하나 들이셔요~

2014년 9월 1일 월요일

Google Talk로 XMPP 배우기 #2

이 글은 Adarsh Ramamurthy가 작성한 "Fun with XMPP and Google Talk, Part 2"를 기반으로 번역하고 내용을 추가한 것입니다. 원문은 하나의 글이지만 내용이 길어서 이를 둘로 나눕니다.  원문은 아래 링크를 확인하세요.

http://www.adarshr.com/papers/xmpp2

지난 글에서 XMPP(Jabber) 프로토콜의 아주 기본적인 내용에 대해서 살펴 보았습니다. 그리고 Google Talk 서버(talk.google.com)와 원시적으로 대화하는 방법도 보았습니다. 흥미로웠을 걸로 기대합니다.



이 속편 글에서는 몇 단계 더 나아가 XMPP의 어플리케이션 레이어 프로그래밍 개념에 대해 소개드리고자 합니다. 그리고 Google Talk을 통해 할 수 있는 몇가지 재밌는 기능들을 구체적으로 구현해 보도록 하겠습니다. 이 글을 다 읽게 되면 제가 그랬던 것처럼 그 무한한 가능성에 매혹될 걸로 기대합니다.

추가적인 XMPP 개념들

XMPP를 프로그래밍하기 위해서는 XMPP 라이브러리가 얘기하는 아래와 같은 복잡한 개념들에 대해 알아야 합니다.

로스터 (Roster)

로스터는 채팅 클라이언트에 표시되는 상대방에 주어진 이름을 의미합니다. Google Talk 용어로는 당신이 로그인하면 볼 수 있는 친구들의 리스트라고도 합니다. 로스터의 구성요소는 당신의 전화번호부입니다. 각 항목들은 JID(Jabber Identifier)라는 유일한 식별자가 매겨집니다. JID는 이메일 주소와 비슷한 모양인데 예를 들어 user@domain 과 같은 식입니다. 이 식별자의 도메인 파트는 보통 접속하는 서버의 것을 사용합니다. 그래서 Google Talk의 경우 gmail.com 이 보통 사용됩니다.

로스터는 서버에 저장됩니다. 그래서 XMPP 클라이언트는 어디서나 최신의 로스터를 유지할 수 있습니다. 로스터는 IQ 스탠자 형식입니다. (아래에 설명이 나옵니다)

메시지 (Message)

메시지는 XMPP의 세가지 기본 스탠자 중 하나입니다. 다른 둘은 IQ와 프레전스(Presence)입니다. 이름이 의미하는 그대로 메시지 스탠자는 메시지를 전송하기 위해 사용됩니다. 메시지 스탠자는 여러모로 이메일 통신방법과 유사하지만, 이메일은 비동기 방식이고 메시지 스탠자는 즉각적인 전송이 이루어진다는 점에서 차이가 있습니다. 사용자는 수신자의 주소를 포함하는 메시지를 서버로 보냅니다. 그러면 서버는 수신자 주소로 메시지를 전달합니다. 즉 "나는 서버로 메시지를 보내고, 서버는 수신자에게 그 메시지를 전달한다"는 메시지 전송 방식입니다.

예를 들어 쥴리엣이 그녀의 친구 로미오에게 메시지를 보낸다고 한다면, 많은 일들이 보이지 않는 곳에서 이루어 집니다.

로미오의 JID는 romeo@gmail.com 이라 하고 쥴리엣의 JID는 juliet@gmail.com 이라고 합시다. 쥴리엣은 그녀가 사용하는 XMPP 클라이언트를 이용하여 서버에 로그인을 합니다. 그리고 서버로부터 그녀의 로스터 (친구 리스트)를 내려 받습니다. 그녀는 로미오가 온라인이고 연락 가능함을 확인할 수 있습니다. 그녀는 "Wherefore art thou, Romeo?"라고 메시지를 입력하고 엔터키를 칩니다. 이제 그녀의 XMPP 클라이언트, gmail.com이라는 서버 그리고 로미오의 XMPP 클라이언트 이 셋이 결합되어 실시간으로 메시지를 전송해야 할 의무가 있습니다.

쥴리엣의 XMPP 클라이언트는 다음과 같이 메시지 스탠자를 만듭니다.

<message 
  to="romeo@gmail.com" 
  from="juliet@gmail.com" 
  type="chat" 
  xml:lang="en"> 
    <body>Wherefore art thou, Romeo?</body> 
</message>

"to"속성은 수신자의 JID를 담습니다. 위의 경우 로미오가 되겠지요. "from"속성은 메시지를 보내는 사람의 JID입니다. 필수항목인 "type"속성은 "chat"이라고 지정되어 있는데, "error", "groupchat", "headline"등과 같은 다른 타입의 메시지와 구분하기 위해 반드시 명시해야 합니다. 다른 메시지 타입에 대해서는 이 글에서 다루지 않겠습니다. "xml:lang" 속성은 메시지의 언어를 의미하고, "en"은 영어를 뜻합니다. 메시지 자체는 메시지 스탠자 내의 <body>태그 내에 위치합니다.

gmail.com 서버는 이 스탠자를 받으면 "to"속성에 있는 "romeo@gmail.com" 사용자에게 메시지를 전달하려고 합니다. 그리고 그 메시지는 "juliet@gmail.com"이 보낸 것입니다. 쥴리엣은 로미오가 온라인인 것을 알고 있으며 이미 로그인된 상태라는 것도 압니다. 즉 로미오의 XMPP 클라이언트도 gmail.com에 연결되어 있는 상태이고, 이는 즉 아직 닫히지 않은 XML 스트림이 있음을 의미합니다. 서버는 단순하게 그 메시지를 로미오의 연결된 채널로 보냅니다. 로미오의 XMPP 클라이언트는 이것을 감지하고, 윈도우를 띄워 쥴리엣이 타이핑한 메시지를 즉시 보여줍니다. 인스턴트 메시징이라고 불러도 하등 이상할 것이 없는 메카니즘입니다.

IQ

IQ는 또 하나의 중요한 스탠자입니다. IQ는 Info/Query를 줄인 말입니다. 이것은 HTTP같은 전통적인 요청/응답 (Request/Response) 패러다임과 유사합니다. IQ 스탠자는 네가지 타입이 있는데 "get", "set", "result", "error"가 그것들입니다. get과 set은 요청 혹은 쿼리에 해당하고, result와 error는 응답 혹은 정보(Info)에 해당됩니다. IQ 스탠자는 다양한 태그들을 포함할 수 있습니다. 예를 하나 들어보도록 하지요.

쥴리엣이 XMPP 클라이언트에 로그인합니다. 클라이언트는 XMPP IQ 스탠자 중 "get" 타입의 것을 gmail.com 서버로 보내 그녀의 로스터를 받아 옵니다.

<iq from="juliet@gmail.com" type="get" id="roster_1">
  <query xmlns="jabber:iq:roster"/>
</iq>

gmail.com 서버는 그녀의 로스터를 XMPP 클라이언트로 응답해 줍니다. 이때는 "result"타입의 IQ 스탠자를 사용합니다.

<iq to="juliet@gmail.com" type="result" id="roster_1">
  <query xmlns="jabber:iq:roster"> 
    <item jid="romeo@gmail.com" name="Romeo" subscription="both">                     
      <group>Friends</group> 
    </item> 
    <item jid="mercutio@gmail.com" name="Mercutio" subscription="from"> 
      <group>Friends</group> 
    </item> 
    <item jid="benvolio@gmail.com" name="Benvolio" subscription="both"> 
      <group>Friends</group> 
    </item> 
  </query> 
</iq>

위에서 보듯이 IQ 스탠자는 내부에 다양한 XML 태그를 포함할 수 있습니다. 그리고 <iq>태그 안에 포함되는 태그 수와 길이에는 제한이 없습니다.

프레전스 (Presence)

프레전스는 XMPP 코어에서 정의하는 세번째 스탠자 타입입니다. 프레전스는 상대방이 연락을 받을 수 있는지 등의 상태를 의미합니다. Google Talk에서 연락처 옆에 볼 수 있는 초록, 빨강, 주황색 동그라미가 바로 이 프레전스를 나타낸 것입니다. 이 스탠자의 하위 태그인 <show>가 이 정보를 담습니다. <show>태그에는 디폴트로 네가지 상태가 있을 수 있습니다.
  • chat : 초록색 동그라미, 상대방이 채팅할 준비가 되어 있음을 의미합니다 
  • dnd : 빨간색 동그라미, 상대방이 바쁘기 때문에 채팅을 거절함을 의미합니다. (dnd = Do Not Disturb) 
  • away : 주황색 동그라미, 아이들 상태로 상대방이 자리에 없음을 의미합니다. 
  • xa : Extended Away의 약자로, 상대방이 아주 오랫동안 away 상태임을 의미합니다. Google Talk는 xa와 away를 따로 구분하지 않기 때문에 둘 다 주황색 동그라미로 표시합니다.
메시지 스탠자와 달리, 프레전스 스탠자는 약간 다른 방식으로 사용됩니다. 프레전스 정보는 보통 사용자의 로스터에 있는 모든 사용자에게 전역(Broadcast)으로 전달됩니다. 이는 "전역 발행/구독 메시징 (Broadcast Publish/Subscribe Messaging)" 이라는 기술로 다음 절에 간단히 설명드릴 겁니다. 궁금하다면 다음 절을 먼저 본 다음 이 내용을 이어봐도 됩니다.

모든 연락처는 프레전스 정보의 발행자이기도 하고 구독자이기도 합니다. 어떤 사용자의 연락처에 있는 사람은 그 사용자의 프레전스 정보를 얻기 위해 구독을 하게 됩니다. 그리고 그 연락처는 그 사용자에 자신의 프레전스를 발행하는 발행자입니다.

예를 들어 로미오가 이제 자려고 XMPP 클라이언트의 상태를 "잘자~ 좋은 꿈꾸고~"라고 바꾼다고 해 봅시다. 그리고 그가 잠들어 있는 동안 메시지를 받지 않기 위해 "dnd"로 설정한다고 합시다. 그렇다면 다음과 같은 통신이 물밑에서 이루어 집니다.

로미오의 XMPP 클라이언트는 아래와 같은 프레전스 스탠자를 gmail.com 서버로 전송합니다.

<presence xml:lang="en"> 
  <show>dnd</show> 
  <status>Good night, good night! parting is such sweet sorrow, that I shall say good night till it be morrow.
  </status> 
</presence>

gmail.com 서버는 이 프레전스 스탠자를 받으면 로미오의 로스터에 있는 모든 상대방에 다음과 같은 프레전스 스탠자를 전역으로 전송합니다.

<presence from="romeo@gmail.com" to="juliet@gmail.com" xml:lang="en">   <show>dnd</show> 
  <status>Good night, good night! parting is such sweet sorrow, that I shall say good night till it be morrow.</status> 
</presence> 
<presence from="romeo@gmail.com" to="mercutio@gmail.com" xml:lang="en"> 
  <show>dnd</show> 
  <status>Good night, good night! parting is such sweet sorrow, that I shall say good night till it be morrow.</status> 
</presence> 
<presence from="romeo@gmail.com" to="benvolio@gmail.com" xml:lang="en"> 
  <show>dnd</show> 
  <status>Good night, good night! parting is such sweet sorrow, that I shall say good night till it be morrow.</status> 
</presence>

위의 프레전스 스탠자는 서버로부터 각 사용자의 연결 채널로 일일이 하나씩 보내집니다. 무슨 말이냐면 쥴리엣에게는 "to"에 쥴리엣의 JID를 넣어서 로미오의 프레전스를 보내고, 다른 친구에게는 그 친구의 JID를 "to" 속성에 넣어 보냅니다.

발행/구독 메시징이란?

이 절은 프레전스 스탠자가 어떻게 동작하는지 보충 설명을 하기 위해 준비했습니다. 이미 전역 발행/구독 메시징에 대해 잘 알고 있다면 건너 뛰어도 됩니다. 이후에 프레전스를 다루는 예제 프로그램을 소개할 거라 이 부분에 대해서 조금 자세히 설명 드리겠습니다.

가정해 봅시다. 당신은 뉴욕 어딘가에 살고 있고, 뉴욕 타임즈와 인디아 타임즈 같은 몇개의 일간지를 구독하고 있다고요. 당신은 뉴욕 증시 시황 등 뉴욕에서 일어나는 일에 대해서도 관심이 있고, 인도 영화계(Bollywood)의 가십거리도 좋아하고, Sanjeev Kapoor의 요리 컬럼에 대한 광팬이기도 합니다.

아마도 당신처럼 이것들과 다른 일간지에 대해서도 수백만의 구독자들이 있을 겁니다. 이 기사들을 만드는 기고가는 일간지에 기사를 싣고 이것이 원하는 독자들에게 전달되기를 원합니다. 당신은 주기적으로 당신이 구독한 일간지들을 받아 봅니다. 그리고 일간지에는 여러 기자들이 작성한 다양한 정보들이 포함되어 있기도 합니다.

이 예제에서 다른 기사를 썼던 그 기자들은 간접적으로 당신과 통신을 하고 있는 겁니다. 비록 단방향이기는 하지만요. 이것이 바로 발행/구독 메시징의 핵심입니다. 아래 그림을 보면 더 잘 이해가 되실 겁니다. 



전역 발행/구독 메시징에는 네개의 주체가 관여되어 있습니다.

발행자 (Publisher)

발행자는 특정한 독자들이나 모든 독자들을 위한 컨텐츠를 만듭니다. 발행자는 보통 구독자에 대해서 알지 못합니다. 발행자는 임의의 토픽(Topic)들을 발행할 수 있습니다. 위 그림에서 빨간 발행자는 두개의 토픽 T1, T2로 발행하였습니다.

토픽 (Topic)

이 게임에서 핵심은 토픽입니다. 토픽은 발행자와 구독자 모두가 접근해야 하는 객체입니다. 발행자는 토픽으로 발행합니다. 구독자는 특정 토픽을 구독합니다. 복수개의 발행자는 복수개의 토픽에 발행할 수 있습니다. 하나의 토픽은 여러개의 발행자와 엮어져 있을 수 있습니다. 위 그림에서 토픽 T1은 파랑과 빨간 발행자가 관여하고 있습니다. 하나의 토픽은 일간지에서의 하나의 뉴스 토픽이라고 비유할 수 있습니다.

토픽 서버 (Topic Server)

토픽 서버는 여러개의 다른 토픽들을 엮어서 하나의 토픽으로 만듭니다. 우리의 예제에서는 일간지라고 보면 됩니다.

구독자 (Subscriber)

구독자는 메시지의 최종 소비자입니다. 구독자는 토픽 서버를 통해 토픽을 구독합니다. 구독자는 보통 구독하는 토픽에 발행하는 발행자에 대해 알고 있습니다. 보통 해당 토픽에서 발행자의 수보다 구독자의 수가 훨씬 많습니다. 구독자는 발행자가 될 수도 있고, 그 반대도 가능합니다. 우리 예제에서는 당신은 구독자였지만, 당신의 직업이 언론사의 편집자일 수도 있는 겁니다. 그럼 당신은 발행자이면서 구독자인 겁니다.
(3편으로 이어집니다...)

댓글 없음:

댓글 쓰기