본문 바로가기

Maker Movement

구글 클라우드 프린트(4) - 프린터 등록

구글에서 제공하는 페이지(https://developers.google.com/cloud-print/docs/privet)를 참조해서 시험삼아 가상의 클라우드 프린터용 Privet API 서비스를 구현하고 크롬 브라우저를 이용 등록해보았다. 몇몇 사항에 대해 자세히 설명이 되어 있지 않아서 여러번 시행 착오가 있었다. 다행스럽게 등록에 성공, 여기에 자세한 내용을 기술하고 작업한 소스코드를 공유한다.


사전에 준비할 것이 있는데, 프린터용으로 사용할 OAuth 2.0 client id가 없으면 구글페이지(https://developers.google.com/identity/protocols/OpenIDConnect#registeringyourapp)를 참조해서 만든다. 만약  https://console.developers.google.com 사이트 이용법을 잘 알고 있다면 해당 사이트에서 발급받으면 된다.


등록 시나리오

등록하는 전 과정은 아래처럼 겉보기에도 여러 단계를 거치는데, 각각의 단계마다 숨겨진 작업들이 있고 이러한 작업들이 정확히 이루어져야 한다. 

  1. 크롬 브라우저를 띄우고, 설정(고급) > 구글 클라우드 프린트 > 클라우드 프린트 기기 관리 페이지를 연다. (구글계정으로 로그인 되어 있어야 한다.)
  2. 프린터 프로그램 구동, 프린터는 mDNS규약에 따라 자신의 정보를 로컬망에 뿌린다.
  3. Privet REST API /privet/info 호출  
  4. 크롬 브라우저 화면에 프린터가 보여지고 '등록'버튼이 나타난다.
  5. 크롬 브라우저 화면에 표시된 등록 버튼을 클릭 
  6. Privet REST API /privet/register?action=start 호출
  7. Privet REST API /privet/register?action=getClaimToken 호출
  8. Privet REST API /privet/register?action=complete 호출
  9. Privet REST API /privet/info를 호출 (최종 확인작업)
이를 시퀀스다이어그램으로 자세히 그린 이미지는 다음과 같다.



단계 1~2, mDNS 

클라우드 프린터가 mDNS망에 뿌려야할 정보는 5개로 추릴 수 있다. 여기서 'AAAA' 레코드는 생략할 수 있다고 보는데, 확인한 것은 아니다.  'PTR', 'TXT' 레코드만 있으면 프린터 정보는 보여지나 '등록'버튼이 활성화 되지 않는다. 그 이유는 크롬 브라우저가 정보부족으로 프린터의 Privet API 서비스를 이용할 수 없기 때문이다.

Privet API 서비스에 대한 정보를 크롬브라우저에 알려주기 위해, 'SRV' 레코드에 해당 서비스 호스트:포트 정보를 담고 'A' 혹은 'AAAA'레코드에  호스트의 IP주소를 담아 mDNS 망에 뿌린다. 크롬 브라우저는 이 정보를 사용해서 프린터의 Privet API 서비스에 접근할 수 있는데, 아직 '등록' 버튼을 활성화 하려면 작업이 하나 더 수행되야 한다.

 Record Type

QNAME

 Data

 PTR

 _privet._tcp.local

 printer's name._privet._tcp.local

 TXT

 printer's name._privet._tcp.local

 txtvers=1 ...

 SRV

 printer's name._privet._tcp.local

 웹API  port, hostname

 A

 hostname

 IP address (IPv4)

 AAAA

 hostname

 IP address (IPv6)



단계 3~4, /info

크롬 브라우저는 mDNS로 얻어진 정보를 가지고 Privet API중 'privet/info'를 호출한다. 구글측 페이지에 따르면 API를 제공하는 쪽에서 'X-Privet-Token' 헤더 정보를 확인하도록 되어 있지만 이번 구현에는 생략했다. 


프린터는 JSON형식으로 프린터의 정보를 호출한 쪽(크롬브라우저)에 제공하는데, 이 때 중요한 것은 'api' 필드의 내용이다. 이 필드는 배열로 정의되는데 배열에 반드시 '/privet/register'항목이 포함되야 한다. 그렇지 않으면 '등록'버튼은 표시되지 않는다. 그리고 'id' 필드는 빈 문자열로 정의해도 된다.


단계 6, /privet/register?action=start

'등록'버튼이 눌려짐과 동시에 크롬브라우저는 등록이 시작되었음을 프린터에게 알려준다. 매개변수 'user'에 브라우저에서 구글계정으로 로그인한 사용자 아이디가 담겨 전달된다. 자세한 정보는 이 페이지 앞부분에 적혀진 구글 페이지를 참조하면 된다. 특별한 경우가 아니라면 구글 페이지에 정의된대로 응답한다. 


단계 7, /privet/register?action=getClaimToken

가장 중요한 단계이다. 단계 6의 응답을 받은 크롬브라우저는 자동으로 이 API를 호출한다. 그러면 프린터는 구글 클라우드 API '/cloudprint/register'를 호출하고 받은 응답을 가지고 크롬 브라우저로 응답한다. '/cloudprint/register' API를 호출할 때, 프린터는 아래의 값들을 제출한다.

  • proxy : 프린터측에서 정한 임의의 문자열을 넣는다. (자세한 내용을 확인 못함)
  • printer : 프린터 이름 (privet/info에서 제공한 이름과 동일)
  • capabilities : 프린터 사양 등인데 정확한 정보가 아니어도 된다.  (구글 페이지 참조)
  • use_cdd: true
참고로 작성한 코드의 일부를 옮겼다. http client로 'org.apache.httpcomponents' 라이브러리를 사용한 코드이다.
List<NameValuePair> params = new ArrayList(4);
params.add(new BasicNameValuePair("proxy", "ZZZ"));
params.add(new BasicNameValuePair("printer", this.printerName));
params.add(new BasicNameValuePair("capabilities", "{\"version\": \"1.0\"}"));
params.add(new BasicNameValuePair("use_cdd", "true"));


요청에 아무 오류가 없다면 등록과 관련된 많은 정보가 담긴 응답을 받게 된다. 테스트때 받았던 응답중 하나를 예로 옮긴다.

{

 "success": true,

 "request": {

  "time": "0",

  "params": {

   "proxy": [

    "ZZZ"

   ],

   "capabilities": [

    "{\"version\": \"1.0\"}"

   ],

   "printer": [

    "ZCUBEPRN"

   ],

   "printerid": [

    "1ddd6ccc5-3eee-bccc-7871-0001999abcce"

   ],

   "use_cdd": [

    "true"

   ]

  }

 },

 "printers": [

  {

   "isTosAccepted": false,

   "capabilities": {

    "version": "1.0"

   },

   "displayName": "ZCUBEPRN",

   "description": "",

   "capsHash": "",

   "updateTime": "...",

   "type": "GOOGLE",

   "notificationChannel": "XMPP_CHANNEL",

   "tags": [

    "__cp_printer_passes_..."

   ],

   "gcpVersion": "1.0",

   "proxy": "ZZZ",

   "createTime": "...",

   "defaultDisplayName": "",

   "name": "ZCUBEPRN",

   "id": "333d6cc66-3ee1-bcee-7871-0001999abcce",

   "status": "",

   "accessTime": "..."

  }

 ],

 "complete_invite_url": "https://goo.gl/printe...",

 "invite_url": "https://www.google.com/cloudprint/claimprinter.html",

 "oauth_scope": "https://www.googleapis.com/auth/cloudprint",

 "invite_page_url": "https://www.google.com/cloudprint/regtokenpage?...",

 "registration_token": "....",

 "token_duration": "....",

 "automated_invite_url": "https://www.google.com/cloudprint/conf...",

 "polling_url": "https://www.google.com/cloudprint/getauthcode?printerid..."

}


등록과정시 반드시 챙겨둘 항목 

  • printers[0].id : 프린터 아이디 (등록때마다 값이 달라진다.)
  • polling_url : 단계 8에서 사용될 URL (getauthcode URL로 직접 만들어도 된다.)

그리고 크롬브라우저로 보낼 정보들을 확인해서 응답한다.

  • registration_token ===> token
  • invite_url ===> claim_url


단계 8, /privet/register?action=complete

크롬 브라우저는 단계7의 응답을 받으면 자동으로 이 API를 호출한다. 프린터는 단계7에서 챙겨두었던 'polling_url'을 사용해서 구글 API를 호출한다. 이 URL은 printer id와 프린터용으로 사용할 oauth 2.0 client id를 가지고 직접 구성할 수도 있다.


'polling_url'은 대개 'https://...getauthcode?printerid=....&oauth_client_id='까지 구성되어 있다. 여기에 발급받은 client id를 덧붙여 호출하면 된다.오류가 없다면 아래와 같은 메시지를 받게 되며, 크롬 브라우저로  'device_id'에 프린터 아이디를 설정 해서 응답한다.

{

 "success": true,

 "xmpp_jid": "....@cloudprint.googleusercontent.com",

 "confirmation_page_url": "https://www.google.com/cloudprint/regc ...",

 "request": {

  "time": "0",

  "params": {

   "printerid": [

    "...."

   ],

   "oauth_client_id": [

    ".....apps.googleusercontent.com"

   ]

  }

 },

 "user_email": "user@gmail.com",

 "authorization_code": "...."

}



단계 9, 최종 확인작업

이 단계의 작업 또한 정확히 이루어져야 등록이 성공한다. 크롬 브라우저는 최종 확인작업을 위해 프린터의 '/privet/info'를 다시 호출하는데, 이 때 응답정보에는 아래의 사항이 지켜져야 한다.

  • 'id' 값은 앞단계에서 전달한 'device_id' 값과 같아야 한다.
  • 'api' 배열에 '/privet/regist' 항목이 없어야 한다.

마치며

다듬어야할 것들도 많고 더 구현해야 할 항목들도 있지만, 이 정도면 등록과정을 테스트 해볼 수 있다고 본다. 아울러 이번 테스트때 구현한 소스코드는 아래 GIT저장소에서 받을 수 있다.