BLOG main image
분류 전체보기 (239)
Rails (65)
Ruby (34)
이야기 (40)
스토리큐 (61)
그 밖에.. (30)
C# (6)
드리밍 인 코드
The note of Legendre
작은아이의 생각
agiletalk's me2DAY
[rails] Growl4Rails
美소년 ㅇㅅㅇ씨의 一日
마사키군의 생각
ayukawa's me2DAY
작은아이의 생각
agiletalk's me2DAY
63,407 Visitors up to today!
Today 12 hit, Yesterday 22 hit

 SUBSCRIBE

'2009/03'에 해당되는 글 9건
2009/03/29 21:36

보통 주중에는 아침 일찍 집을 나섰다가 밤 늦게 집에 들어간다. 그러다 보니 진이는 외롭다.
요즘에는 주말에도 집을 비우는 경우가 많아서, 진이는 더 외롭다.

P3290036

집에 들어가면 귀여운 척, 친한 척이다.

P3290018

사냥놀이라도 많이 해주어야 하는데, 그게 또 잘 안된다.

P3290019

이번 주말에는 대전에 갔다 왔다.

P3290009

'이야기' 카테고리의 다른 글

제 7회 LIG 코리아 오픈 마라톤 대회 참가 후기  (0) 2009/04/05
백만불짜리 습관  (0) 2009/04/01
진이는 외롭다.  (0) 2009/03/29
데일 카네기(자기 관리론)  (0) 2009/03/17
제 7회 LIG코리아 오픈 마라톤 대회  (0) 2009/03/15
실행에 집중하라.  (0) 2009/03/04
2009/03/29 10:37

지난 글에는 MySpace에서 애플리케이션을 생성하는 것에 대해 알아봤다. 이번 글은 opensocial API의 기초라고 할 수 있는 사용자 정보를 가져오는 방법에 대해 설명한다.

opensocail API 0.8을 기준으로 설명을 한다. opensocial 홈에서 제공하는 Developer's GuildAPI Reference, 그리고 myspace에서 제공하는 opensocial 0.8에서 달라진 것 문서가 도움이 된다.

VIEWER 정보 가져오기

VIEWER는 지금 애플리케이션을 보고 있는 사용자다. OWNER는 VIEWER가 보고 있는 애플리케이션을 인스톨한 있는 사용자다. canvas 에서는 OWNER와 VIEWER의 구분이 모호하다. 하지만 profile이나 home에서는 VIEWER와 OWNER의 구분이 명확하다. VIEWER의 정보를 가져오는 코드는 다음과 같다.

  1. var os = opensocial.Container.get();
  2. var req = os.newDataRequest();
  3. var params = {};
  4. params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] = [
  5.   opensocial.Person.Field.ID,
  6.   opensocial.Person.Field.NAME,
  7.   opensocial.Person.Field.THUMBNAIL_URL,
  8.   opensocial.Person.Field.ABOUT_ME,
  9.   MyOpenSpace.Person.Field.LARGE_IMAGE
  10. ]
  11. req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER, params), "viewer");
  12. req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), "owner");
  13. req.send(loadUserCallback);
  14. function loadUserCallback(resp) {
  15.   if (resp.hadError()) {
  16.     var viewer = response.get("viewer");
  17.     if(viewer.hadError()) {
  18.       // handle viewer error
  19.       console.debug(viewer.getErrorMessage() + " : " + viewer.getErrorCode());
  20.     }
  21.   }
  22.   else {
  23.     var viewer = resp.get("viewer").getData();
  24.     var name = viewer.getDisplayName();
  25.     var thumb = viewer.getFiled(opensocial.Person.Field.THUMBNAIL_URL);
  26.   }
  27. }

이 코드 중 핵심은 newFetchPersonRequest이다. 이 함수의 프로토 타입은 다음과 같다.

  1. Object newFetchPersonRequest(id, opt_params)

id는 string으로 사용자의 ID다.  이미 정의되어 있는 opensocial.IdSpec.PersonId.VIEWER 혹은 opensocial.IdSpec.PersonId.OWNER를 이용해서 VIEWER 혹은 OWNER의 ID를 가져올 수 있다. 다른 사용자의 정보를 원한다면 그 사용자의 ID를 적어주면 된다. 참고로, opensocial 0.8에서 myspace 사용자의 id는 'myspace:43234' 이런 식으로 ID 앞에 'myspace:'가 붙는다.

opt_params을 이용해서 가져올 사용자 정보의 필드들을 지정할 수 있다. 어떤 필드들이 있는 지는 opensocial.Person.Field 에서 확인할 수 있다. 특별히 필드를 지정하지 않으면 기본값(id, profile URL, thumbnail URL, displayName) 만 가져온다. myspace에서는 opensocial 표준에 추가로 MyOpenSpace.Person.Field 도 제공한다. 자세한 내용은 MyOpenSpace Version 0.8을 참조하자.

req 에 여러개의 newFetchPersonRequest()를 넣어서, 한번에 여러 개의 요청을 보낼 수 있다. 그럼으로써 코드를 효율적으로 작성할 수 있다. 실제 ajax 요청은 여러개 발생한다.

Ajax 요청이기 때문에 결과를 처리하기 위해서는 콜백함수를 달아주어야 한다. 콜백함수의 파라미터(resp)는 opensocial.DataResponse 객체이다. 여러 개의 요청을 보낸 경우,  resp 안에 각각의 요청에 대한 응답이 opensocial.ResponseItem 객체로 들어 있다. 이 객체는 resp.get("viewer") 이런 식으로 가져올 수 있다. opensocial.ResponseItem 안에 요청했던 정보가 들어 있다. 이 정보는 getData() 함수를 통해 가져올 수 있다. opensocail.Person 객체인 viewer를 가져오기 위해서는 resp.get("viewer").getData() 를 이용한다. viewer의 각 필드들은 viewer.getFiled() 함수를 이용해 가져올 수 있다.

  1. viewer.getFiled(opensocial.Person.Field.THUMBNAIL_URL);

field 의 값은 opensocial.Person.Field 에서 확인할 수 있다. nickname의 경우는 단축메소드가 있는데, viewer.getDisplayName() 이 그것이다.

요청에 대한 응답에 에러가 발생했는지는 resp.hadError() 로 확인할 수 있다.  한개의 요청이라도 에러가 발생하면 resp.hadError()가 true 이다. 어느 요청이 에러가 발생했는지는 resp.get("viewer").hadError() 와 같은 식으로 각각의 응답을 확인해야 한다.

사용자의 Data 정보 다루기

애플리케이션에서 사용할 목적으로 사용자의 설정같은 정보를 MySpace안에 저장할 수 있다. 이 data를 Persistent Data라고 한다. Persistent Data는 사용자가 애플리케이션을 제거하지 않는 이상 없어지지 않는다. 다음은 Persistent Data를 만들거나 수정하는 코드다.

  1. var os = opensocial.Container.get();
  2. var req = os.newDataRequest();
  3. var value = {
  4.   period : 4,
  5.   shot : "big"
  6. }
  7. req.add(req.newUpdatePersonAppDataRequest("VIEWER", "APP_KEY", value));
  8. req.send(updateUserCallback);
  9. function updateUserCallback(resp) {
  10.   if(resp.hadError()) {
  11.     // handle error
  12.   }
  13.   else {
  14.     // success
  15.   }
  16. }

여기서 중요한 함수는 newUpdatePersonAppDataRequest() 이다. 이 함수의 프로토타입은 다음과 같다.

  1. Object  newUpdatePersonAppDataRequest(id, key, value)

id는 String으로 사용자의 ID이다. 현재는 "VIEWER" 만 사용할 수 있다. key는 말 그대로 키다. value 역시 말 그대로 key와 연결되어 있는 값이다.

value는 "valid json" 이어야 한다. valid json은 javascript 객체, 혹은 eval()로 평가되어지는 string을 말한다. 잘못된 값이 넘겨지면 아무 일도 일어나지 않는다. opensocial 0.7에서는 이 value가 String이었다. 0.7 에서는 저장한 String은 0.8에서는 동작하지 않을 수 있다. 0.8의 방법이 0.7과 호환은 안되지만 깔끔하기는 하다.

다음은 사용자의 Persistent Data를 가져오는 코드다.

  1. var os = opensocial.Container.get();
  2. var req = os.newDataRequest();
  3. var fields = [ "APP_KEY", "APP_KEY_2" ];
  4. var params = {};
  5. params[opensocial.IdSpec.Field.USER_ID] = opensocial.IdSpec.PersonId.VIEWER;
  6. params[opensocial.IdSpec.Field.NETWORK_DISTANCE] = 0;
  7. var idspec = opensocial.newIdSpec(params);
  8. req.add(req.newFetchPersonAppDataRequest(idspec, fields), "viewer_data");
  9. req.send(function(resp) {
  10. if (resp.hadError()) {
  11.   // handle error
  12. }
  13. else {
  14.   var viewerId = gadgets.views.getParams().viewerId;
  15.   var viewerData = resp.get("viewer_data").getData()[viewerId];
  16.   var value = viewerData["APP_KEY"];
  17.   console.debug(value.shot);
  18. }

여기서 눈여겨 볼 함수는 newFetchPersonAppDataRequest()이다.

  1. Object newFetchPersonAppDataRequest(idSpec, keys, opt_params)

첫번째 파라미터는 idSpec이다. opensocial.newIdspec()을 이용해서 idspec 객체를 생성할 수 있다. 이 때, USER_ID 사용자의 ID string이다. NETWORK_DISTANCE 0은 자기 자신, 1은 자신의 친구, 2는 친구의 친구를 나타낸다. keys는 가져오고 싶은 key들의 배열이다.

응답에서 값을 가져오기 위해서 요청한 사용자의 ID가 필요한데, VIEWER의 ID는 gadgets.views.getParams().viewerId 를 이용해서 가져올 수 있다.

저장되어 있는 value가 valid json이 아닌 경우 응답에는 null 이 들어가 있게 된다. 0.7에서 0.8로 업그레이드 할 때 이런 일이 발생할 수 있다.

'그 밖에.. > javascript' 카테고리의 다른 글

MySpace 애플리케이션 개발하기 #4  (2) 2009/04/03
MySpace 애플리케이션 개발하기 #3  (0) 2009/04/01
MySpace 애플리케이션 개발하기 #2  (0) 2009/03/29
MySpace 애플리케이션 개발하기 #1  (0) 2009/03/26
Paginator 3000  (0) 2009/03/20
jQuery 사용하기  (0) 2008/03/17
2009/03/26 00:17

목표

MySpace 애플리케이션에 대해 알아보고, 간단한 프로그램 만들어보기

애플리케이션의 생성

Myspace developer Platform에 접속해서 개발자로 등록한다. My Apps 탭 버튼을 누르면 개발자로 등록할 수 있는 폼이 보인다. 등록 후 하루 정도 지나면 승인 메일이 온다.

등록이 완료되면 Build 탭을 눌러서 Application 을 하나 생성하자. Application을 생성하고 "Hello World" 를 보여주는 프로그램에 대해서는 MySpace의 예제 문서 페이지 에 자세히 나와 있다.

여기서는 몇 가지 설명만 추가한다.

MySpace는 opensocial API를 제공하고 있다. 0.7 버전과 0.8 버전을 모두 지원하는데, 최신 버전인 0.8 로 선택하도록 하자. 0.7과 0.8 규격(혹은 구현)에 차이가 있고, 따라서 0.7 에서 동작하는 것이 0.8에서도 동작할 것이라고 기대할 수 없다.

myspace

OpenSocial 규격을 보면 오픈소셜 컨테이너가 참조하는 애플리케이션의 정보는 xml 파일 안에 있다. 보통 이 xml 파일이 외부에 있고, 이것의 URL을 컨테이너가 참조하게 되는데, MySpace는 이 xml 파일이 MySpace 안에 있다. Uploading Application XML 을 이용해 xml 파일을 올릴 수도 있고, Edit App Source 을 이용해 HTML 부분면 직접 입력할 수도 있다. 개발의 편이를 위해 HTML 소스에는 포함되는 javascript 와 css 만 써 놓고, 로직은 모두 포함된 javascript 파일에 넣어 두기로 하자.

javascirpt 와 css 파일을 올려놓을 웹서버가 필요하다. 이 파일을 MySpace에서 접근할 수 있어야 한다. 개발하는 동안에는 웹서버를 작업하는 PC에 실행시키고, javascirpt와 css 파일을 올려놓자. 이렇게 함으로써 파일들을 손쉽게 수정할 수 있고, 변경된 내용이 곧바로 애플리케이션에 반영될 수 있다. 만약 PC의 웹서버 포트가 막혀 있다면 SSH turneling을 사용할 수도 있다.

MySpace 애플리케이션을 하나 생성하면 애플리케이션 계정도 하나 생성된다. 그 계정으로 로그인할 수도 있고, 프로파일을 꾸밀 수도 있다.

MySpace내에서 애플리케이션이 보여지는 공간은 Canvas, Profile, Home 이렇게 세 종류가 있다. MySpace Application에 대한 설명에 각각의 공간에 대한 설명이 잘 나와 있다. Profile과 Home에서는 외부에 있는 javascript와 css 를 포함할 수 없으니, 이 점을 유의하자. Profile은 width가 고정되어 있다고 가정해서는 안된다. 사용자는 애플리케이션의 폭을 여러 가지로 조절할 수 있다.

opensocial API에 대해 웹을 검색하다 보면 0.7 버전에 대한 예제가 많이 있다. 이들은 0.8에서 동작하지 않는 경우가 많다. MySpace의 개발자 페이지에도 0.7 버전의 예제와 0.8 버전의 예제가 공존하고 있다. 혼란스러울 수 있다.

이제 소셜 애플리케이션에 빠져 보자.

2009/03/24 22:20

요즘에는 스토리큐마이스페이스 애플리케이션을 개발하고 있습니다. 개발하는 동안에는 개발의 편리를 위해 마이스페이스에서 접근하는 javascript 파일이 내 PC 안에 있었으면 좋겠다고 생각을 했었습니다. 그러면 손쉽게 수정이 가능하고, 수정한 내용들이 애플리케이션에 바로 반영될 수 있을테니까요. 가장 간단한 방법은 내 PC에 웹서버를 설치하고, 마이스페이스에서 내 PC에 있는 파일들의 URL을 알려주는 것입니다. 그러나 불행히도 내가 일하는 곳이 사설 네트웍을 이용하고 있습니다. 외부에서는 80포트로 접근을 할 수 없도록 설정되어 있습니다. 이 때 사용할 수 있는 좋은 방법이 있습니다. SSH Turneling입니다.

우선 퍼블릭 네트웍 위에서 동작하는 SSH 서버가 하나 필요합니다. 그리고 사설 네트웍 위에서는 SSH 서버와 접속할 수 있는 SSH Client가 필요합니다. 하려는 것은 다음과 같습니다.

  1. 마이스페이스(HTTP Client)의 요청을 SSH server가 받도록 합니다.
  2. SSH Server에서는 80 포트로 들어오는 트래픽을 SSH 포트로 포워딩합니다.
  3. 이 트래픽을 SSH Client 가 받아서 내 PC(HTTP ServeR) 의 80포트로 포워딩합니다.

ssh

이 과정들이 SSH client 에서 다음 명령 단 하나로 이루어집니다.

  1. ssh -R 0.0.0.0:80:192.168.1.5:80 root@10.5.253.20 -N

간단하고, 강력합니다.

SSH Turneling에 대한 자세한 설명은 참고 링크로 대신합니다.

참고 링크

SSH Port Forwading

SSH Turneling

SSH Turneling 사용하기

2009/03/20 14:13

  페이지가 많아질 때 페이지를 이동하기 위해 사용하는 방법은 페이지네이션 입니다. 보통 다음과 같은 식으로 UI가 구현이 됩니다.

programmableweb

많은 페이지네이션 UI들을 Pagination Gallery에서 확인하실 수 있습니다.

Paginator 3000

Pagiation 3000은 창의적인 방법으로 페이지네이션 UI를 구현한 javascript 입니다. 밑에 슬라이더가 있어서 페이지가 많더라도 한번에 원하는 페이지로 이동할 수 있습니다.

clip_image001

Paginator 3000 홈페이지에서 데모를 확인해 볼 수 있습니다. 러시아 개발자 karaboz에 의해 개발되었는데, 아주 멋집니다.

pagiator 3000은 여기에서 다운로드 받을 수 있습니다.

사용하기

사용방법에 대해 간단히 알아보겠습니다. Paginator 3000을 이용하기 위해서는 총 페이지수와 현재 페이지만 알면 됩니다. 다음과 같은 식으로 사용할 수 있습니다.

  1. <link rel="stylesheet" type="text/css" href="paginator3000.css" />
  2. <script type="text/javascript" src="paginator3000.js"></script>
  3. <div class="paginator" id="paginator_example"></div> // body 안에 페이지네이터를 위해
  4. <script type="text/javascript">
  5.   paginator_example = new Paginator(
  6.     "paginator_example", // 페이지네이터가 들어갈 id 
  7.     2048, //  총 페이지 수
  8.     10, //  한 화면에 보여질 페이지 수 
  9.     300, //  현재 페이지 
  10.     "http://www.yourwebsite.com/pages/" // 기본 URL
  11.   );
  12. </script>

페이지의 URL은 ‘기본 URL + 페이지 번호’ 로 들어갑니다. 예를 들어 90번 페이지의 링크는 “http://www.yourwebsite.com/pages/90” 이 됩니다. 이 규칙이 잘 안 맞는다면 Paginator.prototype.drawPages 를 적당히 수정하면 됩니다.

플러그인

WordPress에서는 WordPress 플러그인 으로 이 페이지네이터를 볼 수 있습니다. 티스토리 플러그인 혹은 jQuery 플러그인으로 변환할 수 있으면 좋겠다는 생각을 해 봅니다.

2009/03/17 17:12

개인적으로 자기 계발 서적을 좋아하는 편이 아닙니다. 책마다 내용이 비슷비슷하다는 생각을 가지고 있지요. 때로는 내용이 추상적이기도 하고, 때로는 복잡하기도 해서 현실과 동떨어진다는 느낌을 받기도 합니다. 이 책도 jake가 제 책상 위에 올려놓지 않았다면 아마 읽지 않았을 것입니다. 읽고 난 후 느낌은 '책이 잘 쓰여져 있어서 읽을만한 가치가 충분히 있다' 입니다. 마음에 드는 점을 몇 가지 꼽자면, 우선 책이 쉽습니다. 한 주제를 뒷받침하기 위해 많은 이야기들을 예로 들고 있습니다. 그래서 추상적이기 쉬운 내용을 현실적으로 표현해 냈습니다. 또 한 가지는 책의 목표가 단순하고 확실합니다. 책은 처음부터 끝까지 “걱정을 줄이고 삶을 살아갈 수 있는 방법”에 대해 설명하고 있습니다. 걱정 없는 삶을 열심히 살아간다면 그것이 성공이라고 말하고 있습니다.

카네기 본인도 말하고 있듯이 책의 내용은 특별할 것이 없습니다. 강연이나 경험, 혹은 다른 책을 통해서 모두 한 번쯤은 들어봤을 만한 내용들입니다. 그럼에도 책을 읽는다면 자신의 상황에 따라 좋은 생각을 몇 개 얻어갈 수 있을 것입니다. 사람은 보고 싶은 것만 보니까요. 다음은 제가 책을 통해 얻은 생각들입니다.

오늘에 충실하라.

지난 일들이 내 일부인 것임에는 틀림없지만 그 일들을 후회한다고 해도 아무 것도 달라지지 않습니다. 내 몸엔 리셋 스위치가 없습니다. 미래를 기대하고 희망을 가져야 하지만 생각만으로는 아무것도 바뀌지 않습니다. 앞날에 대한 걱정은 건강만 해칠 뿐입니다. 중요한 것은 오늘, 바로 지금입니다.

현명한 사람에게는 매일매일이 새로운 삶이다.

걱정을 글로 표현하라.

걱정은 때때로 사람을 패닉 상태로 몰아 넣습니다. 걱정에 대해 알게 되는 것만으로 그 혼란함을 벗어날 수 있습니다. 머리 속이 복잡할 때는 종이를 한 장 꺼내서 다음과 같은 과정을 따릅니다.

  1. 내가 걱정하고 있는 것을 정확하게 적는다.
  2. 그에 대해 내가 할 수 있는 것을 적는다.
  3. 어떻게 할 지 결정한다.
  4. 결정을 즉각적으로 실행에 옮긴다.

내게 일어난 모든 일들을 받아들여라.

후회를 멈추어야 합니다. 그리고 어쩔 수 없는 일은 받아들여야 합니다. 쇼펜하우어는 다음과 같이 말했습니다.

인생이란 항해를 떠나는 데 있어 가장 중요한 준비는 어떤 어려움이든 감수하겠다는 마음가짐이다.

나폴레옹은 다음과 같이 말했습니다.

내 몰락은 다른 누구도 아닌 나 자신 때문이다. 내 자신이야말로 내 최대의 적이었으며, 내 비참한 운명의 원인이다.

그리고 다음과 같은 말도 있습니다.

언제나 웃어라, 그리고 무슨 일이든 남자답게 받아들여라.

쏟아진 우유 때문에 울지 마라.

지금의 상황을 아쉬움이 아닌 유쾌함으로 대처해야 합니다.

유쾌하게 생각하고 행동하라. 그러면 유쾌해진다.

어쩌면 지금이 기회일 수도 있습니다.

운명이 신 레몬을 건네면 레모네이드를 만들어라.

해리 에머슨 포스딕은 다음과 같이 말했습니다.

이런 게 인생이다. 줄 하나가 끊어지면 세 줄로 연주를 마쳐야 하는 게 인생이다.

행복은 대게 즐거움이 아니다. 행복은 대개 승리감이다.

비난을 멈추어라.

보통 다른 사람을 비난해서 얻을 수 있는 것은 크지 않습니다. 아이젠하워는 그것을 낭비라고 생각했습니다.

마음에 들지 않는 사람들 생각으로 1분이라도 낭비하지 말자.

디즈레일리는 다음과 같이 말했습니다.

사소한 일에 시간을 낭비하지 마라. 인생은 사소한 일에 신경쓰기에는 너무 짧다.

비판보다는 다른 사람을 기쁘게 하는 일에 더 신경을 써야 합니다.

선행이란 다른 사람의 얼굴에 기쁜 미소가 생기게 하는 것이다. – 마호메트

선행을 베푼 후에는 감사를 기대하지 말아야 합니다. 기대를 하고 본 영화는 재미가 없듯이, 감사를 기대한다면 그 기대를 만족하는 감사를 받을 수 없을 것입니다.

우을증에 대해서

아들러 박사의 ‘우리에게 인생이란 무엇인가’ 라는 책을 인용해 우을증을 설명한 구절입니다.

우울증은 일종의 다른 사람들에 대한 만성적인 분노와 비난이다. 환자는 관심과 동정, 도움을 얻고자 하지만 그는 바로 그 자신의 잘못으로 인해 낙담을 맛볼 뿐이다.

피곤함은 우울함을 만듭니다. 틈틈이 휴식해서 피곤을 쌓아두지 않도록 해야 합니다.

필요한 것은 실천하기

왜 jake가 이 책을 내게 추천했을까 하는 생각을 해 봅니다. 내 모습이 걱정에 찌들어 살고 있는 듯해서 안쓰러워 보였을 지도 모릅니다. 책을 다 읽은 지금 내 걱정들이 모두 사라졌다고 하면 그것은 거짓말입니다. 4주만에 몸짱되기 책을 읽는다고 몸짱이 되지 않듯이, 이 책을 읽는다고 해서 걱정이 순식간에 사라지지는 않습니다. 이 책은 걱정 없이 살아갈 수 있는 방법에 대해 설명하고 있을 뿐입니다. 깨달은 몇 가지 생각들을 실행으로 옮기기 위한 노력이 필요합니다. 일 년 뒤에는 좀 더 편안한 얼굴이 되어 있기를 희망합니다.

'이야기' 카테고리의 다른 글

백만불짜리 습관  (0) 2009/04/01
진이는 외롭다.  (0) 2009/03/29
데일 카네기(자기 관리론)  (0) 2009/03/17
제 7회 LIG코리아 오픈 마라톤 대회  (0) 2009/03/15
실행에 집중하라.  (0) 2009/03/04
2009 매시업 경진대회 본선 심사 참관 후기  (0) 2009/02/28
2009/03/15 22:43

lig 2009년 4월 5일에 열리는 제 7회 LIG 코리아 오픈 마라톤 대회에 참가신청을 했습니다. 작년에는 bs와 같이 뛰었는데, 올해는 혼자 뛰게 되었습니다. 혹시 참가하시는 분 있으시면 인사했으면 합니다.^^

10km를 신청했는데, 50분 이내로 들어왔으면 하는 것이 바램입니다.

'이야기' 카테고리의 다른 글

진이는 외롭다.  (0) 2009/03/29
데일 카네기(자기 관리론)  (0) 2009/03/17
제 7회 LIG코리아 오픈 마라톤 대회  (0) 2009/03/15
실행에 집중하라.  (0) 2009/03/04
2009 매시업 경진대회 본선 심사 참관 후기  (0) 2009/02/28
아웃라이어  (0) 2009/02/22
2009/03/07 14:05

회사가 서울대 안에 있다보니 점심 때가 되면 서울대 식당으로 식사를 하러 가곤 한다. 그 때마다 드는 생각은 어디로 먹으러 가지? 와 오늘의 메뉴는 무엇이지? 이다. 어느 식당에서 무슨 메뉴를 제공하고 있는지에 대해서는 서울대 생활협동조합 홈페이지에 가면 볼 수 있다. 즐겨찾기에 등록해 놓고 사용하다 보니, 이번에는 이 메뉴들을 RSS로 받아봤으면 좋겠다는 생각이 들었다. 웹페이지를 가져와서 메뉴만 쏙 빼내서 RSS로 보내면 되니 아주 간단할 것 같다. 그런데 막상 해 보려고 아주 간단하지만은 않다.  프로그램을 만들어서 호스팅 할 수 있는 웹서버에 올려야 하고, 도메인도 구해야 한다. 이런 것들이 구글 앱 엔진을 이용하면 아주 간단해진다는 이야기를 들었다.

appengine 구글 앱 엔진을 이용하면 도메인과 호스팅을 걱정하지 않아도 된다. 프로그램을 개발하는데만 신경을 쓰면 된다. 개발 환경도 잘 갖추어 놓고 있어서 개발하기도 쉽다. 개발한 내용을 배포하는 것도 아주 쉽다. 게다가 구글에서 제공하는 인프라스트럭쳐를 사용할 수도 있다. 다만 현재는 파이선만을 사용해야 하고, 미국에 호스팅되는 관계로 그다지 빠르지는 않다. 그래도 서울대 식당 메뉴를 RSS로 받아보기 서비스를 만드는 데는 딱 좋을 것 같다.

우선 구글 앱 엔진에 로그인을 하고 애플리케인션을 하나 신청한다. 첫 번째 애플리케이션을 만들 때 SMS를 이용해서 인증을 하는데 전화번호가 010-222-3333 이면 82102223333으로 입력하면 문자가 날라온다. 파이선 2.6을 설치하고 Google App Engine SDK 도 설치한다. Getting Started:Python 문서를 따라하다 보면 아주 손쉽게 helloWorld 서비스를 만들 수 있다.

개발하기

적당한 위치에 디렉토리를 만들고 애플리케이션 이름을 붙여준다. 애플리 케이션 이름을 snulunch라고 하자.  그리고 그 디렉토리에 snulunch.py 와 app.yaml 파일을 만든 후 서버를 실행시킨다.

  1. dev_appserver.py snulunch

서버를 실행시킨 후 localhost:8080 에 접속하면 결과를 확인할 수 있다. snulunch.py 파일을 수정하면 수정된 내용이 서버를 재시작하지 않아도 곧바로 반영된다. 개발을 마친 후에는 서버에 올리기 위해 다음 명령을 수행한다.

  1. appcfg.py update snulunch

첫 번째 실행할 때만 계정정보를 물어본다. 계정정보가 맞다면 업로드를 수행한다. 이것으로 끝이다. 인상적으로 쉽다. http://snulunch.appspot.com/ 에서 결과를 확인할 수 있다.

The Webapp Framework

이제 sunlunch.py를 만드는 일만 남았다. 웹서비스를 개발에 있어서 프레임웍을 이용하는 것이 굉장히 효과적이다. 구글 앱 앤진은 Djano같은 파이선을 이용하는 모든 웹 프레임웍을 지원한다고 한다. 그리고 자체적으로 webapp framework도 지원한다. 여기서는 간단하게 webapp 프레임웍을 이용하면 충분할 것 같다.

  1. from google.appengine.ext import webapp
  2. import wsgiref.handlers
  3. import urllib
  4. from BeautifulSoup import BeautifulSoup
  5. from time import strftime
  6. class MenuRss(webapp.RequestHandler):
  7.   def get(self):
  8.     self.response.headers['Content-Type'] = 'application/rss+xml; charset=utf-8'
  9.     date = self.request.get('date').encode("utf-8")
  10.     if(not date) : date = strftime("%Y-%m-%d")
  11.     url = "http://snulunch.appspot.com/menu?date=" + date
  12.     body = urllib.urlopen("http://www.snuco.com/Comuser/restaurant/menu_01.asp?date=" + date).read()
  13.     soup = BeautifulSoup(body)
  14.     menus = str(soup.findAll(cellspacing="1")[0])
  15.     body = urllib.urlopen("http://www.snuco.com/Comuser/restaurant/menu_02.asp?date=" + date).read()
  16.     soup = BeautifulSoup(body)
  17.     menus += str(soup.findAll(cellspacing="1")[0])
  18.     xml = """<?xml version="1.0" encoding="UTF-8"?>
  19. <rss version="2.0">
  20.   <channel>
  21.     <language>ko-KR</language>
  22.     <title>[SNU] Today's Menu</title>
  23.     <description>menus of restaurants in Seoul National University</description>
  24.     <link>http://snulunch.appspot.com</link>
  25.     <item>
  26.       <title>Menus on %s</title>
  27.       <description><![CDATA[
  28.     """  % (date)
  29.     xml += menus
  30.     xml += """]]>
  31.       </description>
  32.       <pubDate>%s</pubDate>
  33.       <link>%s</link>
  34.       <guid>%s</guid>
  35.     </item>
  36.   </channel>
  37. </rss>
  38.     """ % (strftime("%Y-%m-%d %H:%M:%S"), url, url)
  39.     self.response.out.write(xml)
  40. class Menu(webapp.RequestHandler):
  41.   def get(self):
  42.     date = self.request.get('date')
  43.     if(not date) : date = strftime("%Y-%m-%d")
  44.     self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
  45.     body = urllib.urlopen("http://www.snuco.com/Comuser/restaurant/menu_01.asp?date=" + date).read()
  46.     soup = BeautifulSoup(body)
  47.     menus = str(soup.findAll(cellspacing="1")[0])
  48.     body = urllib.urlopen("http://www.snuco.com/Comuser/restaurant/menu_02.asp?date=" + date).read()
  49.     soup = BeautifulSoup(body)
  50.     menus += str(soup.findAll(cellspacing="1")[0])
  51.     self.response.out.write(menus)
  52. application = webapp.WSGIApplication([
  53.   ('/', Menu),
  54.   ('/menu.rss', MenuRss),
  55.   ('/menu', Menu)
  56. ], debug=True)
  57. def main():
  58.   wsgiref.handlers.CGIHandler().run(application)
  59. if __name__ == '__main__':
  60.   main()

프레임웍이 직관적이고 간단해서 그다지 어렵지 않다. 간단한 설명을 하자면 webapp.WSGIApplication 를 생성하면서 라우팅에 대해서 적어준다. "/"요청은 는 Menu 클래스에 맵핑된다. 그리고 HTTP method가 Menu 클래스의 method와 연결된다. 그래서 GET "/" 요청은 Menu.get 메소드를 호출하게 된다.
HTTP 요청의 파라미터는 self.request.get('PARAM_NAME') 를 통해서 가져올 수 있다.
HTTP header 를 self.response.headers["HEADER_NAME"] = VALUE 를 이용해 설정할 수 있다.
HTTP body 를 self.response.out.write() 를 이용해 채울 수 있다.

이 작업을 하면서 덤으로 파이선과 RSS에 대해서 몇 가지 알게 됬다.

  • urllib 를 이용해서 간단하게 url에 해당하는 HTML 문서를 가져올 수 있다.
  • BeautifulSoup를 이용해서 손쉽게 HTML 문서를 파싱할 수 있다.
  • RSS 문서의 description 안의 HTML 코드는 <![CDATA[ ... ]]> 로 감싸야 제대로 표현된다.

이제 다 됬다. 오늘의 서울대 식당 메뉴를 RSS로 받고 싶다면 http://snulunch.appspot.com/menu.rss 를 리더에 추가하면 된다.

사용해 보니

어려운 점은 역시 파이선이 익숙하지 않다는 것이다.  그것만 제외한다면 개발환경은 충분히 좋았고, 배포하기도 편했다. 덤으로 훌륭한 통계도 보여준다.

2009/03/04 23:11

얼마 전에 생활이 끔찍해지고, 도무지 잘 해낼 수 없을 것만 같던 때가 있었다. 그 때 "왜 이렇게 되었을까? 무엇이 부족한 것일까?" 같은 질문들이 머리 속을 가득 채웠다. 그 때 생각한 여러 가지 문제들 중에서 가장 중요하다고 생각하는 것 두 가지를 뽑이라고 한다면, 하나는 꾸준함이 없다는 것이고, 다른 하나는 떠오르는 생각들을 실행하지 못한다는 것이다.

얼마나 많은 생각들이 머리속에서 조용히 사라지는지 모른다. 얼마나 많은 계획들이 종이에 적힌 채 먼지에 덮혀가는 지 모른다. 수영을 배우자는 생각, 마라톤을 완주하자는 계획, 담배를 끊자는 결심, 새로운 웹서비스에 대한 생각, 자유의지에 대한 글을 써야겠다는 생각, 청소를 해야한다는 생각.
시간이 부족하다고 투정을 하기도 하지만, 그렇지 않다. 다음 질문에 대답할 수 없기 때문이다. "난 위 계획들보다 중요한 일을 하면서 살아왔던가?"

실행에 집중하라.

0100005249098_03 행동하는 것이 중요하다는 생각을 가지고 있었기 때문일 것이다. genture에서 실행하는 독서통신교육의 책목록을 둘러보다 "실행에 집중하라"라는 책이 눈에 들어왔다. 그리고 주저없이 선택했다.

책은 기대와는 다르게 조직에서의 실행력에 대해 설명하고 있다. 꼭 실행력이라기 보다는 훌륭한 리더가 되기위해 가져야할 요건들에 대해 설명을 하고 있다. 좋은 내용을 담고 있을 테지만, 내 현실이 책의 예들과 동떨어져 있기에 지금의 나에게는 좋은 책이라고 할 수 없다. 언젠가 내가 조직을 이끌게 되고, 그 조직이 커가게 되면 아마 이 책을 다시 꺼내들고 교훈을 얻을 수 있을 것이다. 하지만 지금은 마음에 와닿지는 않는다. 그럼에도 실행에 대한 몇 가지 좋은 아이디어를 얻을 수 있었다.

"생각을 행동으로 옮기기 위해서 어떻게 해야 할까?"

현실적이 되어라.

무리한 계획을 세워서는 안 된다. 할 수 있을만큼만 계획해야 한다. 조금 더 구체적으로 이야기 하면, 한번에 한가지만 시도하는 것이 좋다. 여러 가지를 한 번에 시도하는 것은 실패할 확률을 높인다. 우선 아침에 일찍 일어나기를 하고, 일어나는 일에 충분히 익숙해지면, 아침에 신문을 읽기 시작하고, 그리고 여유가 있으면 밥도 먹고, 운동도 하고 이런식으로 천천히 늘려가는 것이 좋다. 그러다가 다시 아침이 너무 빡빡하다고 생각되면 운동하는 시간을 줄일 수도 있다. 요점은 피곤하지 않은 하루가 될 정도의 계획을 가지는 것이다.

할일 목록을 만들고 우선순위를 정하라.

하고 싶은 일은 이많큼 많이 있는데, 해야할 시간은 부족하다. 이 때 필요한 것은 해야 할 일들을 나열해 보고, 가장 중요한 일을 선택하는 것이다. "중요한 일을 먼저하기". 이 당연한 듯이 보이는 원칙이 지키기는 참 어렵다. 중요한 일이 익숙하지 않을 때는 특히 그러하다. 한 예를 들면, 내 중요한 일 목록에는 글쓰기가 항상 높은 순위에 있는데, 내게 글쓰는 일은 시간이 많이 드는 일이고, 힘든 일이다. 그래서 "다음에 해야지" 하고 미루게 되면 어느 순간 글을 써야겠다는 주제들이 열 개도 넘게 된다. 그러면 그 목록들은 그저 휴지통에 들어가게 된다. 이 주일 이상 미루어진 일은 다시 실행하기 어렵다. 미루어야 한다면 조만간 그 미룬 일들 목록을 다시 평가해 보는 일이 꼭 필요하다. 많이 미루게 되는 일에 대해서는 데드라인을 정하는 것도 효과가 좋아 보인다. 숙제처럼 하는 것이다.

기록하라.

할일 목록을 나열하고 관리하기 위해서는 기록하는 것이 꼭 필요하다. 해야할 일을 기록하고, 중요한 일을 표시하고, 그일의 수행계획을 세운다. 기록을 하다보면 내가 얼마나 잘하고 있는지 알 수 있다. 내 하루가 여유가 있는지 빡빡한지도 알 수 있다.

의사소통하라.

보통 사람들에게 혼자서 할 수 있는 일은 그렇게 많지 않다. 많은 일을 수행하기 위해서는 다른 사람들의 도움을 받아야 한다. 내가 필요로 하는 것을 이야기하고 도움을 받을 수 있을만큼 받아라. 도움을 받으면 그 사람과의 관계도 개선된다고 한다. 일석 이조다.
책에서 가장 많이 말하고 있는 것이 의사소통이다. 현실을 파악하기 위해서는 현장에서 일하는 사람들과 소통해야 한다. 일이 잘 진행되고 있는지 확인하기 위해 사람들과 이야기 해야 한다. 더 좋은 작업환경을 만들기 위해서도 대화야해 한다. 무엇이 중요한지도 역시 의견교환이 있어야 한다. 책 제목을 "커뮤니케이션" 이라고 해도 될 듯 싶다.

실행한 것에 대해 보상하라.

스티븐 래빗은 프리코노믹스라는 책을 통해 세상은 인센티브에 의해서 돌아간다고 말했다. 제대로 실행하고 있다면 보상해주어야 한다. 스스로 하는 보상은 쇼핑일 수도 있고, 휴가일 수도 있다.

주위 사람들에게 도움을 청하라.

주변에 따라하고 싶은 사람이 있다는 것은 좋은 일이다. 현명한 사람이 주위에 있다면 그 사람의 지혜를 빌려라.

단호한 의지

이렇게 목록을 나열해 보니 자기개발서에서 제시하는 성공하기 위해서 해야할 것들 목록같아 보인다. 위 항목들도 중요하지만, 실행을 하기 위해 가장 중요한 것은 아마 단호한 의지가 아닐까 싶다. 매일 저녁 달리기를 하기로 마음먹었다면 비가오나 눈이오나 달리기를 한다. 오늘은 우울해서 못뛰겠다라는 생각을 갖지 않는다. 이런 단호한 의지. 다만 어떻게 그 단호한 의지가 생겨나게 되는 것인지를 모르겠다. 습관인가?

'이야기' 카테고리의 다른 글

데일 카네기(자기 관리론)  (0) 2009/03/17
제 7회 LIG코리아 오픈 마라톤 대회  (0) 2009/03/15
실행에 집중하라.  (0) 2009/03/04
2009 매시업 경진대회 본선 심사 참관 후기  (0) 2009/02/28
아웃라이어  (0) 2009/02/22
많은 글을 쓰도록 하자  (0) 2009/02/01
prev"" #1 next