Skip to main content

PHP 애플리케이션과 Google Contacts 통합하기

XML과 PHP를 사용하여 Google Contacts의 연락처 정보를 읽고 쓰기

Vikram Vaswani, Founder, Melonfire
Vikram Vaswani는 오픈 소스 도구와 기술을 전문으로 다루는 컨설팅 서비스 회사인 Melonfire의 창립자이자 CEO이며 PHP Programming SolutionsPHP: A Beginners Guide의 저자이기도 하다.

요약:  Google Contacts Data API는 사용자의 개인용 Gmail 연락처 정보를 읽고 수정할 수 있는 강력한 클라이언트 중립적 API를 제공합니다. 애플리케이션 컨텍스트에서 이 API와 함께 사용자 정의 PHP 애플리케이션을 통해 연락처를 검색, 추가, 삭제 및 수정하는 방법에 대해 설명합니다.

원문 게재일:  2009 년 9 월 22 일 번역 게재일:   2009 년 11 월 10 일
난이도 :  중급 영어로   보기 PDF:  A4 and Letter (338KB | 25 pages)Get Adobe® Reader®

소개

필자는 친구 및 동료들과 함께 이메일을 자주 주고 받기 때문에 주로 사용하는 이메일 클라이언트인 Mozilla Thunderbird에 포함된 주소록 애플리케이션에 연락처 정보를 저장해 두고 있었다. 하지만 지난 몇 달 동안 필자는 이 정보를 강력한 고유 주소록을 가지고 있는 Gmail로 점차적으로 옮겼다. Google Contacts라는 이 기능은 웹을 통해 쉽게 액세스할 수 있고, 많은 수의 정보 필드를 지원하며, 가져오기 및 내보내기 기능도 제공한다. 이 기능을 사용하면 연락처에 대한 포괄적인 데이터를 저장할 수 있을 뿐만 아니라 웹 액세스 기능을 갖춘 컴퓨터에서 액세스할 수 있기 때문에 어디에서나 자유롭게 연락처 정보를 "사용"할 수 있다.

자주 사용하는 약어

  • API: Application program interface
  • DOM: Document Object Model
  • HTTP: Hypertext Transfer Protocol
  • REST: Representational State Transfer
  • URL: Uniform Resource Locator
  • XML: Extensible Markup Language

개발 관점에서도 Google Contacts는 흥미로운 기능을 제공한다. 즉, 다른 많은 Google 제품과 마찬가지로 이 제품에도 Data API가 있으며, 이 API를 통해 개발자는 개인용 주소록에 저장된 데이터와 관련된 작업을 수행하는 새 애플리케이션을 쉽게 개발할 수 있다. REST 모델을 따르는 이 API는 XML이 지원되는 개발 툴킷을 통해 액세스할 수 있으며 필자가 주로 사용하는 PHP를 포함한 여러 가지 일반적인 프로그래밍 언어를 위한 클라이언트 라이브러리도 제공한다.

이 기사에서는 먼저 Google Contacts Data API에 대해 알아본 후 사용자 정의 PHP 애플리케이션을 사용하여 연락처 데이터를 통합하고 사용하는 방법에 대해 설명한다. 이 기사의 예제에서는 사용자의 주소록에서 연락처를 검색하고, 새 연락처를 추가하고, 기존 연락처를 수정 및 삭제하고, 연락처 피드에서 리턴된 데이터를 제어한다. 자 이제 시작해 보자.


Google Contacts API에 대한 이해

PHP 코드를 살펴보기 전에 Google Contacts Data API에 대한 몇 가지 용어를 먼저 살펴보자. 다른 Google Data API와 마찬가지로 시스템은 하나 이상의 XML 인코딩된 입력 인수가 들어 있는 HTTP 요청을 받은 후 요청된 정보가 포함된 Atom 피드를 리턴한다. 그런 다음 이러한 피드는 XML 인식 프로그래밍 언어에서 구문 분석될 수 있다. 또한 GET, POST, PUT, DELETE 등의 표준 HTTP 조작은 각기 레코드를 검색, 추가, 갱신 및 삭제하는 API 메소드에 맵핑된다.

일반적인 Google Contacts 피드에는 수많은 정보가 들어 있다. Listing 1의 예제를 살펴보자.


Listing 1: Google Contacts 피드 예제

<atom:feed xmlns:atom="http://www.w3.org/2005/Atom">
  <atom:author>
    <atom:name>John Doe</atom:name>
    <atom:email>user@gmail.com</atom:email>
  </atom:author>
  <atom:category term="http://schemas.google.com/contact/2008#contact" 
   scheme="http://schemas.google.com/g/2005#kind"/>
  <atom:id>user@gmail.com</atom:id>
  <atom:link href="http://www.google.com/" rel="alternate" type="text/html"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full" 
   rel="http://schemas.google.com/g/2005#feed" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full" 
   rel="http://schemas.google.com/g/2005#post" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full/
   batch" rel="http://schemas.google.com/g/2005#batch" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full?
   max-results=25" rel="self" type="application/atom+xml"/>
  <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full?
   start-index=26&max-results=25" rel="next" type="application/atom+xml"/>
  <atom:title type="text">John Doe's Contacts</atom:title>
  <atom:updated>2009-08-31T10:48:00.410Z</atom:updated>
  <atom:generator uri="http://www.google.com/m8/feeds" version="1.0">Contacts
  </atom:generator>
  <atom:entry xmlns:default="http://www.w3.org/2007/app" 
   xmlns:default1="http://schemas.google.com/g/2005" 
   xmlns:default2="http://schemas.google.com/contact/2008">
    <default:edited xmlns="http://www.w3.org/2007/app">2009-08-22T16:52:37.457Z
    </default:edited>
    <default1:name xmlns="http://schemas.google.com/g/2005">
      <default1:fullName>Vikram Vaswani</default1:fullName>
      <default1:givenName>Vikram</default1:givenName>
      <default1:familyName>Vaswani</default1:familyName>
    </default1:name>
    <default1:organization xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#work">
      <default1:orgName>Melonfire</default1:orgName>
      <default1:orgTitle>CEO</default1:orgTitle>
    </default1:organization>
    <default1:email xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#other" address="vikram@example.org" 
     primary="true"/>
    <default1:email xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#home" address="vikram@example.com"/>
    <default1:phoneNumber xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#mobile">0012345678901
     </default1:phoneNumber>
    <default1:phoneNumber xmlns="http://schemas.google.com/g/2005" 
     rel="http://schemas.google.com/g/2005#work_fax">0045678901234
     </default1:phoneNumber>
    <default2:website xmlns="http://schemas.google.com/contact/2008" 
     href="http://www.melonfire.com/" rel="home"/>
    <default2:website xmlns="http://schemas.google.com/contact/2008" 
     href="http://www.php-beginners-guide.com/" rel="blog"/>
    <default2:groupMembershipInfo xmlns="http://schemas.google.com/contact/2008" 
     deleted="false" 
     href="http://www.google.com/m8/feeds/groups/user%40gmail.com/base/6"/>
    <atom:category term="http://schemas.google.com/contact/2008#contact" 
     scheme="http://schemas.google.com/g/2005#kind"/>
    <atom:id>http://www.google.com/m8/feeds/contacts/user%40gmail.com/base/0
    </atom:id>
    <atom:link href="http://www.google.com/m8/feeds/photos/media/user%40gmail.com/0" 
     rel="http://schemas.google.com/contacts/2008/rel#photo" type="image/*"/>
    <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full/0" 
     rel="self" type="application/atom+xml"/>
    <atom:link href="http://www.google.com/m8/feeds/contacts/user%40gmail.com/full/0" 
     rel="edit" type="application/atom+xml"/>
    <atom:title type="text">Vikram Vaswani</atom:title>
    <atom:updated>2009-08-22T16:52:37.457Z</atom:updated>
    <atom:content type="text">PHP enthusiast</atom:content>
  </atom:entry>
  <atom:entry xmlns:default="http://www.w3.org/2007/app" 
   xmlns:default1="http://schemas.google.com/g/2005">
  </atom:entry>
  <openSearch:totalResults xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
  >153</openSearch:totalResults>
  <openSearch:startIndex xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
  >1</openSearch:startIndex>
  <openSearch:itemsPerPage xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/"
  >25</openSearch:itemsPerPage>
</atom:feed>

모든 Contacts 피드는 <feed> 요소를 루트 요소로 사용하여 열린다. <feed> 요소에는 다양한 버전의 피드에 대한 URL이 들어 있는 <link> 요소와 요약 통계가 들어 있는 <openSearch:> 요소가 포함되어 있다.

바깥쪽 <feed> 요소에는 요소당 단일 연락처를 나타내는 하나 이상의 <entry> 요소가 포함되어 있다. 각 <entry>에는 연락처 이름, 소속, 직위, 이메일 주소, 전화 및 팩스 번호, 웹 사이트, 사진 등을 포함한 세부 정보가 들어 있다. 그리고 각 <entry>에는 <link rel="self" ...><link rel="edit" ...>라는 두 가지 추가 <link> 요소가 들어 있으며 이들 요소에는 각기 전체 엔트리를 검색하고 엔트리를 편집할 수 있는 URL이 있다.


연락처 검색하기

Google Contacts 피드는 개인용이라는 점에 유의해야 한다. 이는 곧 Google에서 승인한 두 가지 인증 메소드인 AuthSub와 ClientLogin 중 하나를 사용하여 피드 소유자의 사용자 이름 및 암호를 통해 인증된 작업인 경우에만 피드에 포함된 데이터에 대한 작업(단순히 데이터를 보는 작업 포함)이 성공한다는 것을 의미한다.

이 유형의 인증을 수동으로 수행하는 작업은 상당히 까다로울 뿐만 아니라 일반적인 인증 트랜잭션 중에 발생할 수 있는 다양한 시나리오를 고려한 상당한 양의 코드도 필요하다. 다행히도 이에 대해서는 많이 걱정하지 않아도 된다. 왜냐하면 PHP 애플리케이션과 Google Data API를 통합하는 개발자를 위해 특별히 설계된 Zend® GData Client Library가 필요한 모든 세부 사항을 처리해 주기 때문이다. 별도로 다운로드(참고자료의 링크 참조)할 수 있는 이 라이브러리에는 Google Data API에 대한 편리한 객체 지향 인터페이스와 인증을 포함한 대부분의 일반적인 작업이 포함되어 있으므로 이 라이브러리를 사용하는 개발자는 애플리케이션의 핵심 기능에 집중할 수 있다. 이 기사의 나머지 예제에서 이 라이브러리를 사용할 것이므로 계속 진행하기 전에 이 라이브러리를 설치해야 한다.

Zend의 GData 라이브러리를 설치했으면 이제 PHP를 사용하여 Google Contacts Data API 피드를 처리하는 예제를 살펴보자. Listing 2에서는 Listing 1의 피드를 가져온 후 SimpleXML을 사용하여 관련 데이터 조각을 피드에서 추출한 다음 웹 페이지 형식을 데이터에 지정한다.


Listing 2: 연락처 검색 및 표시하기

<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing contacts</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }
    div.entry {
      display: inline;
      float: left;
      width: 400px;
      height: 150px;
      border: 2px solid; 
      margin: 10px;
      padding: 5px;
    }
    td {
      vertical-align: top;
    }
    </style>    
  </head>

  <body>
     
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Http_Client');
    Zend_Loader::loadClass('Zend_Gdata_Query');
    Zend_Loader::loadClass('Zend_Gdata_Feed');
    
    // set credentials for ClientLogin authentication
    $user = "user@gmail.com";
    $pass = "guessme";
    
    try {
      // perform login and set protocol version to 3.0
      $client = Zend_Gdata_ClientLogin::getHttpClient(
        $user, $pass, 'cp');
      $gdata = new Zend_Gdata($client);
      $gdata->setMajorProtocolVersion(3);
      
      // perform query and get result feed
      $query = new Zend_Gdata_Query(
        'http://www.google.com/m8/feeds/contacts/default/full');
      $feed = $gdata->getFeed($query);
      
      // display title and result count
      ?>
      
      <h2><?php echo $feed->title; ?></h2>
      <div>
      <?php echo $feed->totalResults; ?> contact(s) found.
      </div>
      
      <?php
      // parse feed and extract contact information
      // into simpler objects
      $results = array();
      foreach($feed as $entry){
        $xml = simplexml_load_string($entry->getXML());
        $obj = new stdClass;
        $obj->name = (string) $entry->title;
        $obj->orgName = (string) $xml->organization->orgName; 
        $obj->orgTitle = (string) $xml->organization->orgTitle; 
      
        foreach ($xml->email as $e) {
          $obj->emailAddress[] = (string) $e['address'];
        }
        
        foreach ($xml->phoneNumber as $p) {
          $obj->phoneNumber[] = (string) $p;
        }
        foreach ($xml->website as $w) {
          $obj->website[] = (string) $w['href'];
        }
        
        $results[] = $obj;  
      }
    } catch (Exception $e) {
      die('ERROR:' . $e->getMessage());  
    }
    ?>
    
    <?php
    // display results
    foreach ($results as $r) {
    ?>
    <div class="entry">
      <div class="name"><?php echo (!empty($r->name)) ? 
       $r->name : 'Name not available'; ?></div>
      <div class="data">
        <table>
          <tr>
            <td>Organization</td>
            <td><?php echo $r->orgName; ?></td>
          </tr>
          <tr>
            <td>Email</td>
            <td><?php echo @join(', ', $r->emailAddress); ?></td>
          </tr>
          <tr>
            <td>Phone</td>
            <td><?php echo @join(', ', $r->phoneNumber); ?></td>
          </tr>
          <tr>
            <td>Web</td>
            <td><?php echo @join(', ', $r->website); ?></td>
          </tr>
        </table>
      </div>
    </div>
    <?php
    }
    ?>

  </body>
</html>

그림 1에서는 사용자가 볼 수 있는 출력을 보여 준다. 이 화면 캡처를 보면 일부 개인 이메일 주소 정보가 마스킹 처리되어 있다.


그림 1. 연락처를 나열하는 웹 페이지
연락처를 나열하는 웹 페이지

먼저 Listing 2에서는 Zend 클래스 라이브러리를 로드한 다음 Zend_Gdata 서비스 클래스의 인스턴스를 초기화한다. 이 클래스는 Zend_Http_Client 오브젝트를 사용하며, 이 오브젝트는 필수 사용자 인증 정보와 함께 제공되며 Google Contacts 서비스에 대한 인증된 연결을 여는 데 사용된다. 인증된 연결이 열리면 getFeed() 메소드가 연락처 피드를 검색한다. 이 메소드는 Zend_Gdata_Query 오브젝트를 사용하여 피드 URL을 전달 받는다. 또한 Listing 2에서 setMajorProtocolVersion() 호출은 서버에게 v3.0의 Google Contacts Data API를 사용하도록 지시한다. 이는 필수 호출이므로 생략할 경우 예기치 않은 동작이 발생한다.

getFeed() API 호출에 대한 응답은 Listing 1과 비슷한 XML 피드이며, 이 피드는 구문 분석 후 PHP 오브젝트로 변환된다. 피드 내의 엔트리는 배열 요소로 표현되므로 피드를 반복하여 개별 연락처 엔트리를 검색할 수 있다. SimpleXML은 XML 트리에서 특정 정보를 쉽게 검색할 수 있는 방법을 제공하므로 simplexml_load_string() 메소드를 사용하여 각 엔트리의 XML을 SimpleXML 오브젝트로 변환한다. 그런 다음 쉽게 검색할 수 있도록 각 <entry>의 하위 노드를 검색하여 단일 배열로 변환한 후 각 결과 오브젝트를 $results 배열에 저장한다. 전체 피드를 처리한 후에는 foreach() 루프를 사용하여 $results 배열을 반복하면서 웹 브라우저에 표시할 컨텐츠의 형식을 쉽게 지정할 수 있다.

Zend_Gdata_Query 오브젝트에 전달되는 피드 URL은 표준 피드 URL인 http://www.google.com/m8/feeds/contacts/USERID/TYPE에 사용자 이름과 피드 유형을 추가하여 생성된다. 피드 유형에는 전체 피드와 부분 피드라는 두 가지 변형이 있으며 이 중에서 부분 피드는 전체 엔트리의 일부만 리턴한다. 그리고 Listing 2와 같이 사용자 이름 대신 특수 키워드 default를 사용하여 사용자의 기본 피드가 리턴된다.


연락처 추가하기

지금까지 연락처를 나열하는 작업을 살펴보았다. 이제 새 연락처를 추가하려면 어떻게 해야 할까?

이 작업은 실제로 복잡하지는 않다. 새 연락처를 추가하려면 XML로 인코딩된 새 <entry> 블록을 피드 URL에 POST하면 된다. Listing 3에서는 그러한 블록에 대한 예제를 보여 준다.


Listing 3: 예제 Google Contacts 엔트리

<?xml version="1.0"?>
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom" 
 xmlns:gd="http://schemas.google.com/g/2005">
  <gd:name>
    <gd:fullName>Jack Frost</gd:fullName>
  </gd:name>
  <gd:email address="jack.frost@example.com" 
   rel="http://schemas.google.com/g/2005#home"/>
  <gd:organization rel="http://schemas.google.com/g/2005#work">
    <gd:orgName>Winter Inc.</gd:orgName>
  </gd:organization>
</atom:entry>

Zend 라이브러리를 사용할 경우에는 훨씬 쉽게 이 작업을 수행할 수 있다. 즉, POST 요청을 작성하여 피드 URL에 데이터를 전송하는 insertEntry() 메소드만 호출하면 된다. Listing 4에서 그 예제를 볼 수 있다.


Listing 4: 새 연락처 추가하기

<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // create new entry
  $doc  = new DOMDocument();
  $doc->formatOutput = true;
  $entry = $doc->createElement('atom:entry');
  $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
   'xmlns:atom', 'http://www.w3.org/2005/Atom');
  $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
   'xmlns:gd', 'http://schemas.google.com/g/2005');
  $doc->appendChild($entry);
  
  // add name element
  $name = $doc->createElement('gd:name');
  $entry->appendChild($name);
  $fullName = $doc->createElement('gd:fullName', 'Jack Frost');
  $name->appendChild($fullName);
  
  // add email element
  $email = $doc->createElement('gd:email');
  $email->setAttribute('address' ,'jack.frost@example.com');
  $email->setAttribute('rel' ,'http://schemas.google.com/g/2005#home');
  $entry->appendChild($email);
  
  // add org name element
  $org = $doc->createElement('gd:organization');
  $org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
  $entry->appendChild($org);
  $orgName = $doc->createElement('gd:orgName', 'Winter Inc.');
  $org->appendChild($orgName);
  
  // insert entry
  $entryResult = $gdata->insertEntry($doc->saveXML(), 
   'http://www.google.com/m8/feeds/contacts/default/full');
  echo '<h2>Add Contact</h2>';
  echo 'The ID of the new entry is: ' . $entryResult->id;
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());
}
?>

Listing 4에서는 Listing 2에서 본 것처럼 Zend_Gdata 오브젝트를 사용하여 Google Contacts Data API에 대한 인증된 연결을 연다. 그런 다음 PHP의 DOM 확장이 Listing 3의 XML <entry>를 동적으로 생성하고 insertEntry() 메소드를 사용해서 결과 XML을 Google 서버에 실제로 저장한다. 추가된 엔트리는 Google Contacts 인터페이스에서 즉시 볼 수 있어야 한다.

<gd:organization rel="..." /> 요소에서 적절한 스키마를 지정하여 이메일 주소 및 전화 번호를 "home", "work" 또는 "mobile"에 속한 엔트리로 명시적으로 표시할 수 있다.

그림 2에서는 새 연락처 엔트리를 성공적으로 추가한 이후의 출력을 보여 준다.


그림 2. 새 연락처를 추가한 이후의 출력
새 연락처를 추가한 이후의 출력

그림 2의 화면 캡처를 보면 Google의 확인 메시지 중에서 일부 필드가 마스킹 처리되었다는 것을 알 수 있다. 이 메시지의 형식은 The ID of the new entry is http://www.google.com/m8/feeds/contacts/USER_ID/base/CONTACT_ID이다.

그리고 그림 3에서는 새로 추가된 연락처가 Google Contacts 인터페이스에 표시된 모습을 보여 준다.


그림 3. Gmail에 새로 추가된 연락처
Gmail에 새로 추가된 연락처, Jack Frost

연락처 삭제 및 갱신하기

Google Contacts Data API를 사용하면 엔트리를 편집하고 삭제할 수도 있다. 엔트리를 삭제하려면 DELETE 요청을 연락처의 편집 URL에 보낸다. 이 URL은 엔트리의 <link rel="edit" ...> 요소에 지정되어 있다. Zend 라이브러리 컨텍스트에서는 이 URL을 Zend_Gdata 오브젝트의 delete() 메소드에 쉽게 전달할 수 있다(Listing 5 참조).


Listing 5: 연락처 삭제하기

<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

// set ID of entry to delete
// from <link rel=edit>
$id = 'http://www.google.com/m8/feeds/contacts/default/base/29e98jf648c495c7b';

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $client->setHeaders('If-Match: *');
  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // delete entry
  $gdata->delete($id);
  echo '<h2>Delete Contact</h2>';
  echo 'Entry deleted';
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());
}
?>

엔트리를 편집하려면 getEntry() 메소드를 통해 엔트리의 고유 ID를 사용하여 엔트리를 검색한 다음 갱신하려는 값을 변경한다. 그런 다음 Zend_Gdata 오브젝트의 updateEntry() 메소드를 사용하여 엔트리를 서버에 다시 저장한다. 이 메소드는 엔트리의 <link rel="self" ...> 요소에 지정된 URL에 PUT 요청을 보내는 기능을 수행한다. Listing 6에서는 연락처 엔트리에 대한 새 이름과 이메일 주소를 설정하는 프로세스를 보여 준다.


Listing 6: 연락처 수정하기

<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

// set ID of entry to update
// from <link rel=self>
$id = 'http://www.google.com/m8/feeds/contacts/default/full/0';

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $client->setHeaders('If-Match: *');

  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // perform query and get entry
  $query = new Zend_Gdata_Query($id);
  $entry = $gdata->getEntry($query);
  $xml = simplexml_load_string($entry->getXML());

  // change name
  $xml->name->fullName = 'John Rabbit';
  
  // change primary email address  
  foreach ($xml->email as $email) {
    if (isset($email['primary'])) {
      $email['address'] = 'jr@example.com';  
    }  
  }
  
  // update entry
  $entryResult = $gdata->updateEntry($xml->saveXML(), 
   $entry->getEditLink()->href);
  echo 'Entry updated';
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());
}
?>

Listing 5Listing 6에서는 모두 추가 "If-Match" 헤더를 HTTP 요청의 일부로 추가했다. 이 헤더를 사용하면 마지막으로 읽은 이후로 변경되지 않은 엔트리만 삭제하거나 갱신할 수 있기 때문에 이 헤더는 멀티 클라이언트 환경에서 매우 유용하다. Google Contacts Data API Developers Guide(참고자료의 링크 참조)에서 이 헤더에 대한 자세한 정보를 볼 수 있다.


추가 매개변수 사용하기

Google Contacts Data API에는 결과 피드를 제어하는 데 사용할 수 있는 수많은 추가 매개변수가 있다. 다음은 이러한 매개변수를 보여 주는 목록이다.

  • start-index 매개변수는 피드의 시작 오프셋을 지정한다.
  • max-results 매개변수는 피드의 엔트리 수를 지정한다.
  • orderbysortorder 매개변수는 피드 엔트리의 정렬 방법을 지정한다.
  • showdeleted 매개변수에는 피드에서 지난 30일 동안 삭제된 엔트리가 들어 있다.

Zend 클라이언트 라이브러리를 사용할 경우에는 Zend_Gdata_Query 오브젝트의 해당 특성을 설정하여 이러한 여러 매개변수를 설정할 수 있다. 서비스와 관련된 매개변수는 Zend_Gdata_Query 오브젝트의 setParam() 메소드를 사용하여 설정할 수 있다. 이 두 가지 기술을 모두 보여 주는 Listing 7에서는 결과 세트의 엔트리 수를 10개로 제한한 후 마지막 수정 날짜를 기준으로 엔트리를 내림차순으로 정렬한다.


Listing 7: 추가 쿼리 매개변수 설정하기

<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');

// set credentials for ClientLogin authentication
$user = "user@gmail.com";
$pass = "guessme";

try {
  // perform login and set protocol version to 3.0
  $client = Zend_Gdata_ClientLogin::getHttpClient(
    $user, $pass, 'cp');
  $gdata = new Zend_Gdata($client);
  $gdata->setMajorProtocolVersion(3);
  
  // perform query and get result feed
  $query = new Zend_Gdata_Query(
    'http://www.google.com/m8/feeds/contacts/default/full');
  $query->maxResults = 10;
  $query->setParam('orderby', 'lastmodified');
  $query->setParam('sortorder', 'descending');
  $feed = $gdata->getFeed($query);

  // display title and result count
  // snip...
} catch (Exception $e) {
  die('ERROR:' . $e->getMessage());  
}
?>


간단한 애플리케이션

지금까지 Google Contacts Data API의 작동 방법을 살펴보았으므로 이제 연락처 엔트리를 관리하고 조작하는 간단한 PHP 애플리케이션을 개발할 수 있다. 이 프로토타입 애플리케이션에는 3개의 스크립트가 있다(다운로드의 압축된 파일 참조).

  • contacts-index.php는 연락처 엔트리를 나열하고 엔트리를 추가 및 삭제할 수 있는 링크를 제공한다.
  • contacts-save.php는 새 엔트리를 추가하는 양식을 표시하고 제출을 처리한다.
  • contacts-delete.php는 지정된 엔트리를 삭제한다.

먼저 첫 번째 스크립트에서는 추가 링크를 사용하여 Listing 1을 갱신한다(Listing 8 참조).


Listing 8: 연락처 검색 및 표시하기

<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing contacts</title>
    <style>
    body {
      font-family: Verdana;      
    }
    div.name {
      color: red; 
      text-decoration: none;
      font-weight: bolder;  
    }
    div.entry {
      display: inline;
      float: left;
      width: 450px;
      height: 150px;
      border: 2px solid; 
      margin: 10px;
      padding: 5px;
    }
    td {
      vertical-align: top;
    }
    span.links {
      float: right;  
    }
    </style>    
  </head>
  <body>
    <h2>Contacts</h2>
    
    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Http_Client');
    Zend_Loader::loadClass('Zend_Gdata_Query');
    Zend_Loader::loadClass('Zend_Gdata_Feed');
    
    // set credentials for ClientLogin authentication
    $user = "user@gmail.com";
    $pass = "guessme";
    
    try {
      // perform login and set protocol version to 3.0
      $client = Zend_Gdata_ClientLogin::getHttpClient(
        $user, $pass, 'cp');
      $gdata = new Zend_Gdata($client);
      $gdata->setMajorProtocolVersion(3);
      
      // perform query and get feed of all results
      $query = new Zend_Gdata_Query(
        'http://www.google.com/m8/feeds/contacts/default/full');
      $query->maxResults = 0;
      $query->setParam('orderby', 'lastmodified');
      $query->setParam('sortorder', 'descending');
      $feed = $gdata->getFeed($query);
      
      // display number of results
      ?>
      
      <h2><?php echo $feed->title; ?></h2>
      <div>
      <?php echo $feed->totalResults; ?> contact(s) found.
      </div>
            
      <?php
      // parse feed and extract contact information
      // into simpler objects
      $results = array();
      foreach($feed as $entry){
        $obj = new stdClass;
        $obj->edit = $entry->getEditLink()->href;
        $xml = simplexml_load_string($entry->getXML());
        $obj->name = (string) $entry->title;
        $obj->orgName = (string) $xml->organization->orgName; 
        $obj->orgTitle = (string) $xml->organization->orgTitle; 
      
        foreach ($xml->email as $e) {
          $obj->emailAddress[] = (string) $e['address'];
        }
        
        foreach ($xml->phoneNumber as $p) {
          $obj->phoneNumber[] = (string) $p;
        }
        foreach ($xml->website as $w) {
          $obj->website[] = (string) $w['href'];
        }
        
        $results[] = $obj;  
      }
    } catch (Exception $e) {
      die('ERROR:' . $e->getMessage());  
    }
    ?>
    
    <div>
    <a href="contacts-save.php">Add a new contact</a>
    </div>    
    
    <?php    
    // display results
    foreach ($results as $r) {
    ?>
    <div class="entry">
      <div class="name"><?php echo (!empty($r->name)) 
       ? $r->name : 'Name not available'; ?> 
        <span class="links"><a href="contacts-delete.php?
         id=<?php echo $r->edit; ?>">Delete</a></span>
      </div>
      <div class="data">
        <table>
          <tr>
            <td>Organization:</td>
            <td><?php echo $r->orgName; ?></td>
          </tr>
          <tr>
            <td>Email:</td>
            <td><?php echo @join(', ', $r->emailAddress); ?></td>
          </tr>
          <tr>
            <td>Phone:</td>
            <td><?php echo @join(', ', $r->phoneNumber); ?></td>
          </tr>
          <tr>
            <td>Web:</td>
            <td><?php echo @join(', ', $r->website); ?></td>
          </tr>
        </table>
      </div>
    </div>
    <?php
    }
    ?>

  </body>
</html>

그림 4에서는 수정된 출력을 보여 준다.


그림 4. 추가 및 삭제 기능이 포함된 연락처를 나열하는 웹 페이지
추가 및 삭제 기능이 포함된 연락처를 나열하는 웹 페이지

그림 4를 보면 각 엔트리 옆에 Delete 링크가 추가되었다는 것을 알 수 있다. 이 링크에는 엔트리 ID가 포함되어 있으며 이 ID는 링크 대상에 GET 매개변수로 전달된다. 대상 스크립트에서는 이 ID를 읽고 Listing 5에서 설명한 기술을 사용하여 ID를 삭제한다. Listing 9에서 이 작업을 수행하는 코드를 볼 수 있다.


Listing 9: 연락처 삭제하기

<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Deleting contacts</title>
  </head>
  <body>
    <h2>Delete Contact</h2>    

    <?php
    // load Zend Gdata libraries
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Http_Client');
    Zend_Loader::loadClass('Zend_Gdata_Query');
    Zend_Loader::loadClass('Zend_Gdata_Feed');
    
    // set credentials for ClientLogin authentication
    $user = "user@gmail.com";
    $pass = "guessme";
    
    if (empty($_GET['id'])) {
      die('ERROR: Missing ID');
    } 

    try {
      // perform login and set protocol version to 3.0
      $client = Zend_Gdata_ClientLogin::getHttpClient(
        $user, $pass, 'cp');
      // set GData delete headers
      $client->setHeaders('If-Match: *');
      $gdata = new Zend_Gdata($client);
      $gdata->setMajorProtocolVersion(3);
      
      // delete entry
      $gdata->delete($_GET['id']);
      echo 'Entry deleted';
    } catch (Exception $e) {
      die('ERROR:' . $e->getMessage());
    }
    ?>
  </body>
</html>  

엔트리를 나열하고 제거하는 작업을 살펴보았다. 그렇다면 엔트리를 추가하려면 어떻게 해야 할까? Listing 10의 스크립트에서는 웹 양식을 통해 새 연락처를 추가한 후 Zend 라이브러리를 사용하여 해당 POST 패킷을 생성한 다음 데이터를 서버에 저장하는 작업을 수행한다.


Listing 10: 새 연락처 추가하기

<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Adding new contacts</title>
    <style>
    body {
      font-family: Verdana;      
    }
    </style>
  </head>
  <body>
    <h2>Add Contact</h2>    
    
    <?php if (!isset($_POST['submit'])) { ?>    
    <form method="post" 
     action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>">
      Name: <br/>
      <input name="name" type="text" size="15" /><p/>
      Email address(es): <br/>
      <textarea name="email" type="text"></textarea><p/>
      Organization: <br/>
      <input name="org" type="text" size="15" /><p/>
      <input name="submit" type="submit" value="Save" />      
    </form>    
    
    <?php 
    } else { 

      // check for required input      
      if (empty($_POST['name'])) {
        die('ERROR: Missing name');
      } 
      
      if (empty($_POST['email'])) {
        die('ERROR: Missing email address');
      } 
      
      if (empty($_POST['org'])) {
        die('ERROR: Missing organization');
      } 
      
      // sanitize input and save to array
      $inputData['name'] = htmlentities($_POST['name']);
      $inputData['email'] = htmlentities($_POST['email']);
      $inputData['org'] = htmlentities($_POST['org']);
      
      // load Zend Gdata libraries
      require_once 'Zend/Loader.php';
      Zend_Loader::loadClass('Zend_Gdata');
      Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
      Zend_Loader::loadClass('Zend_Http_Client');
      Zend_Loader::loadClass('Zend_Gdata_Query');
      Zend_Loader::loadClass('Zend_Gdata_Feed');
      
      // set credentials for ClientLogin authentication
      $user = "user@gmail.com";
      $pass = "guessme";
      
      try {
        // perform login and set protocol version to 3.0
        $client = Zend_Gdata_ClientLogin::getHttpClient(
          $user, $pass, 'cp');
        $gdata = new Zend_Gdata($client);
        $gdata->setMajorProtocolVersion(3);
        
        // create new entry
        $doc  = new DOMDocument();
        $doc->formatOutput = true;
        $entry = $doc->createElement('atom:entry');
        $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
         'xmlns:atom', 'http://www.w3.org/2005/Atom');
        $entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
         'xmlns:gd', 'http://schemas.google.com/g/2005');
        $doc->appendChild($entry);
        
        // add name element
        $name = $doc->createElement('gd:name');
        $entry->appendChild($name);
        $fullName = $doc->createElement('gd:fullName', $inputData['name']);
        $name->appendChild($fullName);
        
        // add email elements
        $arr = explode(',', $inputData['email']);
        foreach ($arr as $a) {
          $email = $doc->createElement('gd:email'); 
          $email->setAttribute('address', $a);
          $email->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
          $entry->appendChild($email);          
        }
        
        // add org name element
        $org = $doc->createElement('gd:organization');
        $org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
        $entry->appendChild($org);
        $orgName = $doc->createElement('gd:orgName', $inputData['org']);
        $org->appendChild($orgName);
        
        // insert entry
        $entryResult = $gdata->insertEntry($doc->saveXML(), 
         'http://www.google.com/m8/feeds/contacts/default/full');
        echo 'The ID of the new entry is: ' . $entryResult->id;
      } catch (Exception $e) {
        die('ERROR:' . $e->getMessage());
      }
    }
    ?>
  </body>
</html>

Listing 10은 실제로 두 부분으로 구성되어 있는데 그 중 하나는 웹 양식이고 다른 하나는 양식을 통해 전달된 입력을 처리하는 PHP 코드이다. 그림 5에서는 이 양식을 보여 준다.


그림 5. 새 연락처를 추가하는 웹 양식
새 연락처를 추가하기 위한 양식 필드가 포함된 웹 페이지

사용자가 연락처의 이름, 이메일 주소 및 소속을 이 양식에 입력하고 제출하면 스크립트의 두 번째 부분이 작동한다. 먼저 HTTP 클라이언트가 초기화되면서 Google Contacts Data API에 대한 인증된 연결을 여는 데 사용된다. 그런 다음 웹 양식에 입력된 입력에 대한 유효성 검증이 수행된 후 새 엔트리에 대한 데이터를 보유할 새 DOMDocument 오브젝트가 초기화된다.

그런 다음 PHP의 DOM 메소드가 새 <entry>를 동적으로 생성하고 insertEntry() 메소드가 엔트리를 Google 서버에 실제로 저장한다. 엔트리가 추가된 후 API가 엔트리의 고유 ID를 리턴하면 작업 성공을 알리기 위해 이 ID가 사용자에게 표시된다.

그림 6에서는 새 엔트리가 성공적으로 추가된 이후의 출력을 보여 준다.


그림 6. 새 연락처를 추가한 이후의 출력
새 연락처를 추가한 이후의 출력

그림 6의 화면 캡처를 보면 Google의 확인 메시지 중에서 일부 필드가 마스킹 처리되었다는 것을 알 수 있다. 이 메시지의 형식은 The ID of the new entry is http://www.google.com/m8/feeds/contacts/USER_ID/base/CONTACT_ID이다.


결론

이 기사에서는 SimpleXML 및 Zend 클라이언트 라이브러리를 함께 사용하여 Google Contacts Data API의 데이터를 PHP 애플리케이션으로 통합하는 방법을 집중적으로 살펴보았다. 이 기사의 예제에서는 Google Contacts 피드 형식을 소개한 후 연락처 목록을 검색하고, 연락처를 추가, 수정 및 삭제하고, 사용자의 Google 계정에 저장된 연락처 정보에 대한 사용자 정의 인터페이스를 개발하는 방법을 살펴보았다.

이러한 예제를 통해 살펴본 대로 Google Contacts Data API는 연락처 관리를 위한 창의적인 새 애플리케이션을 개발하려는 개발자에게 유용한 강력하고 유연한 도구이다. 이 도구를 통해 자신의 아이디어를 실제로 구현해 보기 바란다.



다운로드 하십시오

설명이름크기다운로드 방식
Archive of the final code examplecontacts-example-app.zip4KBHTTP

다운로드 방식에 대한 정보


참고자료

교육

제품 및 기술 얻기

토론

필자소개

Vikram Vaswani는 오픈 소스 도구와 기술을 전문으로 다루는 컨설팅 서비스 회사인 Melonfire의 창립자이자 CEO이며 PHP Programming SolutionsPHP: A Beginners Guide의 저자이기도 하다.



등록 상표  |  My developerWorks 이용약관

이 문서 북마킹 하기

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=20
Zone=XML, 오픈 소스
ArticleID=446096
ArticleTitle=PHP 애플리케이션과 Google Contacts 통합하기
publish-date=09222009
author1-email=vikram.melonfire@gmail.com
author1-email-cc=