Google Data API 클라이언트 디버깅: 프로그램 내에서 트래픽 탐색

제프리 스커더, Google Data API팀
2007년 6월

소개

때로는 전송되는 내용을 직접 확인하는 것보다 나은 방법이 없습니다. 특히 Google Data API와 같은 웹 서비스를 사용하는 소프트웨어를 작성할 때 많은 작업에 HTTP 요청이 포함됩니다. 다른 방법이 모두 실패하면 실제로 전송되고 수신된 바이트를 확인하여 프로그램이 예상대로 작동하는지 확인할 수 있습니다. Google Data API용 클라이언트 라이브러리에는 HTTP 트래픽을 표시하는 디버깅 모드가 있습니다. 이는 WireShark 또는 Fiddler와 같은 패킷 스니퍼에 액세스할 수 없는 경우에 특히 유용합니다.

프로그램이 올바르다고 확신했지만 패킷 추적을 검사한 결과 추가 줄바꿈 문자가 있거나 HTTP 헤더의 이름이 잘못 지정된 것을 발견한 적이 한두 번이 아닙니다. HTTP 트래픽을 확인하지 않고 웹 서비스에 대해 프로그래밍하는 것은 눈을 감고 바늘에 실을 꿰는 것과 같습니다.

하지만 패킷 스니퍼를 사용할 수 없거나 암호화된 패킷을 처리하기에 적합하지 않은 상황이 발생할 수 있습니다. 걱정하지 마세요. 프로그램 내 로깅 메커니즘을 활용하여 이 제한을 해결할 수 있습니다. 이러한 로깅 기능을 활용하면 암호화된 HTTPS 데이터나 원격으로 실행되는 코드의 경우에도 교환된 데이터의 일부 또는 전부를 확인할 수 있습니다.

이 도움말에서는 Java, .NET, Python용 Google Data API 클라이언트 라이브러리를 사용하여 3개 언어로 샘플 진단 코드를 작성했습니다. 각 예에서 로깅 또는 디버깅을 사용 설정하고, 클라이언트 로그인을 사용하여 인증한 다음, Google 스프레드시트 목록을 가져와 제목을 출력합니다.

자바

java.util.logging 클래스를 사용하여 클라이언트 라이브러리의 몇 가지 주요 객체의 로깅 수준을 설정할 수 있습니다 (결과적으로 트래픽 데이터가 노출됨). 아래 예에서는 HTTP 헤더와 XML 파서의 활동을 살펴보고 전송되는 내용을 완전히 파악했습니다.

Google Data Java 클라이언트 라이브러리에는 HTTP 요청과 XML 파싱을 처리하는 별도의 클래스가 있으므로 각 클래스에 대해 하나의 로거 객체를 만들어야 합니다. com.google.gdata.client.http.HttpGDataRequest는 HTTP 트래픽을 처리하고 com.google.gdata.util.XmlParser는 XML 파싱을 담당합니다.

로거 인스턴스는 HttpGDataRequestXmlParser의 활동을 기록하며 각 로거의 출력 세부정보 수준을 제어할 수 있습니다. 이 데모에서는 HttpGDataRequestXmlParser 객체에서 생성된 모든 이벤트를 보기로 선택했습니다.

로거를 만들고 구성한 후에는 클래스에서 이벤트를 수신할 때 수행할 작업을 알려야 합니다. 지금은 모든 로깅 정보를 콘솔에 작성하고 싶으므로 ConsoleHandler를 만들어 두 로거에 모두 추가합니다.

샘플 코드는 다음과 같습니다.

import com.google.gdata.client.spreadsheet.*;
import com.google.gdata.data.spreadsheet.*;
import com.google.gdata.util.*;
import java.io.*;
import java.net.URL;
import java.util.*;
import java.util.logging.*;

public class PrintSpreadsheetsWithLogging {
   
   
public static void main(String [] args) throws AuthenticationException,
                                                   
ServiceException, IOException {
       
// Configure the logging mechanisms.
       
Logger httpLogger = Logger.getLogger("com.google.gdata.client.http.HttpGDataRequest");
        httpLogger
.setLevel(Level.ALL);
       
Logger xmlLogger = Logger.getLogger("com.google.gdata.util.XmlParser");
        xmlLogger
.setLevel(Level.ALL);
       
// Create a log handler which prints all log events to the console.
       
ConsoleHandler logHandler = new ConsoleHandler();
        logHandler
.setLevel(Level.ALL);
        httpLogger
.addHandler(logHandler);
        xmlLogger
.addHandler (logHandler);
       
       
SpreadsheetService service = new SpreadsheetService("testing-loggingExampleApp-1");
        service
.setUserCredentials(email, password);
     
       
// Get a list of your spreadsheets.
        URL metafeedUrl
= new URL("http://spreadsheets.google.com/feeds/spreadsheets/private/full ");
       
SpreadsheetFeed feed = service.getFeed(metafeedUrl, SpreadsheetFeed.class);
     
       
// Print the title of each spreadsheet.
       
List spreadsheets = feed.getEntries();
       
for (int i = 0; i < spreadsheets.size(); i++) {
         
SpreadsheetEntry entry = (SpreadsheetEntry)spreadsheets.get(i);
         
System.out.println("\t" + entry.getTitle().getPlainText());
       
}
   
}
}

이 프로그램을 실행하면 콘솔에 다음과 같은 내용이 표시됩니다 (덜 흥미로운 부분은 잘라냈습니다).

Jun 7, 2007 10:24:50 AM ...HttpGDataRequest setPrivateHeader
FINER: Authorization: <Not Logged>
Jun 7, 2007 10:24:50 AM ...HttpGDataRequest setHeader
FINER: User-Agent: ...
...
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINE: 200 OK
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Date: Thu, 07 Jun 2007 17:25:24 GMT
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: null: HTTP/1.1 200 OK
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Content-Type: application/atom+xml; charset=UTF-8
Jun 7, 2007 10:25:20 AM ...HttpGDataRequest execute
FINER: Last-Modified: Thu, 07 Jun 2007 17:25:22 GMT
...
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINE: Start element id
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element id
...
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINE: Start element title
Jun 7, 2007 10:25:20 AM ...XmlParser startElement
FINER: Attribute type='text'
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element title
...
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element entry
...
Jun 7, 2007 10:25:20 AM ...XmlParser endElement
FINE: End element feed

이러한 로그는 상당히 커질 수 있으므로 로거의 수준을 설정할 때 더 신중해야 할 수 있습니다. 나중에 사용할 로그 데이터를 저장할 수 있도록 ConsoleHandler 대신 FileHandler을 만들 수도 있습니다.

Java가 적합하지 않다면 .NET을 사용해 볼 수도 있습니다.

.NET

.NET 클라이언트 라이브러리에서 HTTP 트래픽을 캡처하려면 클라이언트의 기본 요청 팩토리를 GDataLoggingRequestFactory로 바꾸면 됩니다.

.NET 라이브러리의 HTTP 요청은 각 서비스 객체 내부에 있는 GDataRequestFactory에 의해 생성됩니다. 일반 요청 팩터리는 로깅을 실행하지 않지만 GDataRequestFactory의 하위 클래스인 GDataLoggingRequestFactory에는 로깅이 내장되어 있습니다. CombinedFileName를 설정하여 로그 파일의 전체 경로를 지정할 수 있습니다.

요청 팩토리를 설정한 후 서비스 객체의 RequestFactory를 설정하여 서비스 객체의 요청 팩토리를 바꿔야 합니다. 코드는 다음과 같이 표시될 수 있습니다.

using System;
using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Spreadsheets;

namespace LogginTest
{
   
class Program
   
{
       
static void Main(string[] args)
       
{
           
SpreadsheetsService service = new SpreadsheetsService("-exampleApp-1");
            service
.setUserCredentials(email, password);

           
Google.GData.Client.GDataLoggingRequestFactory factory = new GDataLoggingRequestFactory("wise", "SpreadsheetsLoggingTest");
            factory
.MethodOverride = true;
            factory
.CombinedLogFileName = "c:\\temp\\xmllog.log";
           
Console.WriteLine("Log file name:" + factory.CombinedLogFileName);
           
            service
.RequestFactory = factory;

           
SpreadsheetQuery query = new SpreadsheetQuery();
           
SpreadsheetFeed feed = service.Query(query);

           
Console.WriteLine("Your spreadsheets:");
           
foreach (SpreadsheetEntry entry in feed.Entries)
           
{
               
Console.WriteLine(entry.Title.Text);
           
}

           
Console.ReadKey();
       
}
   
}
}

결과 로그 파일에는 XML 요청과 응답이 포함됩니다. 다음은 tidy를 사용하여 서식을 지정한 축약된 예입니다.

<?xml version='1.0' encoding='utf-8'?>

<feed xmlns='http://www.w3.org/2005/Atom'
xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'>
  <id>
  http://spreadsheets.google.com/feeds/spreadsheets/private/full</id>
  <updated>2007-06-07T22:05: 02.674Z</updated>
  <link rel='self' type='application/atom+xml'
  href='http://spreadsheets.google.com/feeds/spreadsheets/private/full'>

  </link>
  ...
  <entry>
    <updated>2007-03-28T17:28:57.250Z</updated>
    <category scheme=' http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#spreadsheet'>
    <title type='text'>events</title>

    <content type='text'>events</content>
    ...
  </entry>
  <entry>
    <updated>2007-05-25T22:11:08.200Z</updated>

    <category scheme=' http://schemas.google.com/spreadsheets/2006'
    term='http://schemas.google.com/spreadsheets/2006#spreadsheet'>
    </category>
    <title type='text'>UnitTest</title>
    <content type='text'>UnitTest</content>
    ...
  </entry>

  ...
</feed>

하지만 스크립트 언어에 관심이 많고 Python을 사용하는 것을 선호할 수도 있습니다.

Python

Python 클라이언트 라이브러리에서 HTTP 트래픽을 캡처하려면 HTTP 클라이언트에서 디버그 모드를 사용 설정하여 HTTP 헤더 트래픽을 콘솔에 에코하면 됩니다. 서비스 객체에는 True로 설정할 수 있는 디버그 멤버가 있습니다.

디버그를 true로 설정하면 서비스 객체에 포함된 기본 HTTPRequest 객체에서 디버그 플래그가 설정됩니다.

다음은 스프레드시트 목록을 요청할 때 스프레드시트 서버에서 전송된 HTTP 헤더를 에코하는 예입니다.

#!/usr/bin/python

import gdata.spreadsheet.service

client
= gdata.spreadsheet.service.SpreadsheetsService()
client
.debug = True

client
.ClientLogin(email, password)

feed
= client.GetSpreadsheetsFeed()

for entry in feed.entry:
 
print entry.title.text

콘솔에 다음과 같은 내용이 표시됩니다.

reply: 'HTTP/1.1 200 OK\r\n'
header: Content-Type: application/atom+xml; charset=UTF-8
header: Last-Modified: Thu, 07 Jun 2007 18:22:35 GMT
header: Cache-Control: max-age=0, must-revalidate, private
header: Transfer-Encoding: chunked
...
header: Date: Thu, 07 Jun 2007 18:22:35 GMT
header: Server: GFE/1.3

삽입 또는 업데이트와 같은 추가 작업을 실행하면 해당 요청 데이터가 콘솔에 에코됩니다.

결론

이 간단한 튜토리얼에서는 Google Data API 클라이언트 라이브러리를 사용하는 Java, .NET 또는 Python 프로그램에 기본 로깅 기능을 추가하는 방법을 설명했습니다. 이러한 기술은 HTTP 교환을 디버그해야 하지만 패킷 스니퍼에 액세스할 수 없는 경우에 유용합니다. 이 예시에서는 일부만 다루었습니다. 이러한 언어에 있는 로깅 메커니즘은 여기에 표시된 것보다 훨씬 강력합니다. 로깅 또는 Google Data API에 대한 자세한 내용은 아래 리소스 목록을 참고하세요.

이 도움말에서 다루는 클라이언트 라이브러리는 다음 페이지에서 확인할 수 있습니다.

관련 기술 자료 항목:

토론 그룹: Google 데이터 API가 더 많이 출시됨에 따라 토론 그룹이 상당히 많아지고 있습니다. Google에서는 그룹을 적극적으로 모니터링합니다.

궁금한 점이나 제안사항이 있으시면 언제든지 알려 주세요. 토론 그룹에 참여하여 게시를 시작하세요.