Rath World Notes by Jang-Ho Hwang

13Nov/09Off

Google이 발표한 새로운 시스템 프로그래밍 언어 Go 써보기

Posted by Jang-Ho Hwang

구글이 새로운 시스템 프로그래밍 언어인 Go를 발표했습니다.

이것이 무엇인가.. 에 대한 글은 인터넷에 마구마구 퍼져있으니 찾아보시면 되고요, 어제 Google Go를 보고 제가 미투데이에 남겼던 기록들은 여기에서 볼 수 있습니다.

안타깝게도 release revision의 json 모듈에 버그가 있어서 2시간정도 삽질을 했습니다. 그것이 버그인지 감별하는데 대부분의 시간을 보냈고, 실제로 버그를 고치는데는 30분 정도가 걸린 듯 하네요.

$GOROOT/src/pkg/json/parse.go 에서 유니코드 문자열 처리를 위해 \u를 만났을때 다음 4바이트를 읽어 int로 변환하는 _UnHex 펑션에서 start, end offset을 받게 해놓고, caller는 start, length를 불러서 생긴 어처구니 없는 버그였습니다. 영광스럽게 패치를 보내려고 하니.. current revision에는 이미 고쳐있지 뭐에요. 별 수 있나요. 울었습니다.

문법에 익숙해지는데 꽤 시간이 걸렸지만, 깔끔합니다.

시험삼아 미투데이에서 내게 달린 최근 댓글을 확인하는 콘솔 프로그램을 만들어보며 학습을 했는데, make 파일과 me2comment.go 파일을 한 번 둘러보시지요.

* Makefile

1 include $(GOROOT)/src/Make.$(GOARCH)
2
3 TARG=me2comment
4 GOFILES=\
5     me2comment.go\
6
7 include $(GOROOT)/src/Make.cmd

* me2comment.go

 1 package main
 2
 3 import (
 4     "io";
 5     "http";
 6     "json";
 7     "fmt";
 8     "regexp";
 9     "flag";
10 )
11
12
13 func filter_html(str string) string {
14     remove_anchor := regexp.MustCompile("<[^>]+>");
15     var s string = remove_anchor.ReplaceAllString(str, "");
16     s = regexp.MustCompile("&lt;").ReplaceAllString(s, "<");
17     s = regexp.MustCompile("&gt;").ReplaceAllString(s, ">");
18     return s;
19 }
20
21 func main() {
22     var username string;
23     flag.StringVar(&username, "username", "rath", "Your ID of the me2DAY.");
24     flag.Parse();
25
26     url := fmt.Sprintf("http://me2day.net/api/track_comments/%s.json", username);
27     r, _, err := http.Get(url);
28     if err!=nil {
29         fmt.Printf("Error: %v\n", err);
30         return;
31     }
32
33     buf, err := io.ReadAll(r.Body);
34     ret, ok, errtok := json.StringToJson(string(buf));  
35     if !ok {
36         fmt.Printf("Error: %v\n", errtok);
37     } else {
38         to_me := ret.Get("comment_to_mes");
39         var last_post string = "";
40         for i:=0; i<to_me.Len(); i++ {
41             e := to_me.Elem(i);
42
43             nickname := json.Walk(e, "comment/author/nickname");
44             body := filter_html(json.Walk(e, "comment/body").String());
45             post_body := filter_html(json.Walk(e, "post/body").String());
46
47             if post_body!=last_post {
48                 var to_print string = post_body;
49                 if len(post_body)>50 {
50                     to_print = post_body[0:50]
51                 }
52                 fmt.Printf("* %s\n", to_print);
53             }
54             fmt.Printf("   ㄴ%s: %s\n", nickname, body);
55             last_post = post_body;
56         }
57     }
58
59     r.Body.Close();
60 }

실행시키면 ..


Jang-Ho-Hwangs-iMac:tmp rath$ ./me2comment
* 그녀의 요리 실력은 나날이 업그레이?
   ㄴ2Z[이지]: 영국이라는 게 믿기지 않아요! @.@
   ㄴJB♥: 와웅 보쌈코기 같이생겼어용ㅋㅋ뭔가맛나보이는 덮밥입니다ㅋㅋ
   ㄴ바람달: 하앍
   ㄴK-Dog: 크아!!
   ㄴtomato: 소스는 없이 먹는건가요?
* $GOROOT/src/pkg/json/parse.go 에서 \u를 만났?
   ㄴ솥아: 난 왜 최신 언어들에는 관심이 없는걸까 -.-  [글보러가기]
* 그녀의 요리 실력은 나날이 업그레이?
   ㄴ_wonhui: 맛있어 보입니다 -ㅁ-
* $GOROOT/src/pkg/json/parse.go 에서 \u를 만났?
   ㄴK-Dog: 꺅 더 좋은 세상을 위해 애쓰시는군요
   ㄴ다즐링: 이 님하 고고씽 삼매경.. ㅠㅠ 이러지마셈 흑흑..
* 버그 찾았다 >_< 2글자만 더 입력하면 ?
   ㄴgyedo: 역시 고수 ^^b
* $GOROOT/src/pkg/json/parse.go 에서 \u를 만났?
   ㄴgyedo: 보통 버그들이 ^^ 어처구니 없죠, 근데 current에는 고쳤다니 기특한걸요
* 밤에 집중이 잘 되는 나. 런던은 요새
   ㄴ늘보: 헉 ㅋㅋ 전 생산성이 떨어져요. 해가 지고나면 졸려서 ㅋㅋ
   ㄴ루치아: 컨디션 안 좋으면 쉬면서 해야죠 ㅎㅎ

이렇게 최근 내게 달린 댓글을 출력해주는 간단한 프로그램입니다.

go가 의존성을 모두 날려버리고 하나의 binary로 만들어주는 기특한 녀석이기 때문에, 이런 간단한 프로그램의 바이너리가 1.2MB나 된답니다. (darwin/amd64 빌드임)

만만한게 미투데이라 ^_^ 시스템 프로그래밍과 전혀 무관한 미투데이 클라이언트를 만들어보았는데요, 다음에 기회가 되면 go의 자랑 기능인 동시성 지원부분을 써보고 싶네요.

Tagged as: , , 7 Comments
10Sep/09Off

미투 구글리더 개발 후기 : Ext GWT

Posted by Jang-Ho Hwang

미투 구글리더 개발 후기입니다.

미투 구글리더는 3개월전에 개발되어 계속 혼자 써오던 것이였는데요. 런던에 온지 6주가 다 되어 가는데, 아무런 준비도 없이 온지라 딱히 직업도 없고 -_- 와이프는 학교 왔다갔다 하느라 혼자 있는 시간이 많아서 평소에 미뤄왔던 일들을 하나 둘씩 하다보니 이녀석을 개발하게 되었답니다.

미투 구글리더의 프론트엔드는 Ext GWT를 이용해 만들었습니다. 3개월동안에는 허접한 HTML만으로 만든 페이지로 운영되었지만 뭐 혼자 쓰는 것이니 운영이랄 것도 없었지요. 아무래도 java awt/swing 으로 개발을 시작해서 그런지 swing 개발 스타일로 웹을 만들 수 있는 GWT가 많이 끌렸습니다. 그런데 제가 비주얼 디자인에 약하다보니.. 자연스럽게 GXT로 눈을 돌리게 되었지요. $329 라는 가격이 그리 만만한 가격은 아니지만 런던에서 첫 수입생기면 바로 지를 생각하고 ^^ 일단 개발을 시작했습니다.

프론트 엔드에서는 미투데이 아이콘을 가져오기 위한 me2DAY API get_person 을 이용하고, 구글리더 피드 주소를 쉽게 가져올 수 있게 하도록 GetGoogleReaderGRLD 란 클래스를 쓰고 있습니다. GetGoogleReaderGRLD가 뭐냐면, 구글리더 공유 피드 URL은

http://www.google.com/reader/public/atom/user/12212200999016560865/state/com.google/broadcast

이런 형식으로 구성되어 있습니다. 다른 부분은 다 똑같은데.. /user/ 뒤의 숫자가 핵심인거지요. 이 숫자만 알면 사용자로 하여금 미투 구글리더를 쓰기 위해

1) 구글리더에 접속
2) 공유 피드 URL 주소가 어디에 있는지 뒤적뒤적
3) 이 URL이 맞는거 같지만 확신이 안서서 갈등
4) 복사해서 붙이기

이런 귀찮은 과정을 줄여줄 수 있습니다. 그래서 GetGoogleReaderGRLD 클래스가 하는 일은 HttpClient를 이용하여 GRLD (/user/ 뒤의 숫자를 GRLD라고 지칭하더군요) 를 긁어오는 것입니다.

어쩔 수 없이 자신의 구글 아이디와 패스워드를 입력해야하는데요, 물론 저장은 하지 않습니다. 원래는 무조건 이 방법만을 강요하게 할까 하다가 아무래도 신뢰 문제가 걸려서 자신이 직접 공유피드URL를 입력할 수도 있게 해두었지요.

GXT와 함께 하는 프론트엔드 개발에서 가장 애를 많이 먹었던 것은 미투데이 아이콘을 표시해주는 ListView 를 개발하는 것이였는데요, 미투 아이콘은 44x44 라 아이콘 하나를 한줄에 표시하기에는 공간 낭비가 심해서 이를 가로로 나열하고 자동으로 wrap 되게 하고 싶었는데.. 생각대로 되지 않아서 애를 많이 먹었습니다. 결국 IE6 에서는 제대로 렌더링 되지 않는 display: inline-block 를 쓰고야 말았습니다. 구글링을 하다보니 inline-block 효과를 모든 브라우저에서 가능하게 하는 법들이 종종 보였는데요, 공간 낭비가 심해지는 디스플레이가 치명적인 결함도 아니고 해서 ^_^ 그냥 inline-block을 써버리고 말았지요.

프론트엔드에서 사용되는 RPC 콜은 총 5개 입니다. Eclipse의 Google plugin 덕분에 RPC 부분에서는 조금도 삽질을 하지 않았습니다.

public interface UtilityServiceAsync {
void setAccountEnabled(String me2id, boolean enable, AsyncCallback<Void> callback);
void getMe2dayProfile(String userid, AsyncCallback<Person> callback);
void getMe2dayIcons(String userid, AsyncCallback<List<Icon>> callback);
void getGoogleReaderAtomUrl(String email, String password, AsyncCallback<String> callback);
void updateAccount(String me2id, String atomURL, int me2dayIconIndex, boolean forwardWhenNote, boolean forwardWhenMe2day,
AsyncCallback<String> callback);
}

이 요청들을 처리하는 GWT의 RemoteServiceServlet 에서는 인증체크를 위한 세션과 쿠키 체크가 들어가있는 것 말고는 별 게 없습니다. 다만 여기서 파라미터로 me2day-api java 의 Person 클래스와 Icon 클래스가 왔다갔다 하는 것을 보실 수 있는데요. 이것을 위해 me2day-api java에 GWT 지원을 넣어 0.6 릴리즈를 하기도 했습니다.

GWT에서 legacy POJO를 RPC의 파라미터나 리턴값으로 쓰려면.. 일단 해당 클래스들이 GWT에서 제공하는 클래스 외의 것들을 쓰고 있는지 확인해야 합니다. me2day-api 의 경우에 가장 골치아팠던 점은 java.net.URL 클래스였지요. GWT 에서는 java.net.URL 클래스를 지원하지 않습니다. 그래서 URL 필드들을 모두 String 으로 바꿔야 되는 지경에 이르렀는데요, 그렇다고 GWT 와 상관없이 개발하는 사람들도 다 String을 써야한다는 것은 웃기는 일이지요. 그래서 기존 클래스들은 그대로 두고 각 클래스에 toGWT() 란 녀석을 하나 심어서 GWT 호환 객체로 clone 해주는 인터페이스를 만들었습니다. 이렇게 되면 하위호환성도 지키면서 GWT 지원 엔티티 클래스들도 확보할 수 있게 되지요.

하지만 이렇게 GWT 호환 클래스를 만드는 것만으로는 부족했습니다. GXT 에서는 MVC 모델을 적용하기 적합한 List, Tree, Table 에 대해서는 RpcProxy 란 것을 제공합니다. 이녀석이 뭔가 하니 각 컴포넌트의 모델 부분에 대해 네트워크 관련 코딩을 직접하지 않고 rpc 이름만 지정해주면 지가 혼자 알아서 요청도 날리고 데이터가 오면 뷰에 뿌려주기까지.. 하는 녀석이지요.

RpcProxy<List<Icon>> proxy = new RpcProxy<List<Icon>>() {
@Override
protected void load(Object loadConfig, AsyncCallback<List<Icon>> callback) {
service.getMe2dayIcons(me2dayId, callback);
}
};
ListLoader<ListLoadResult<Icon>> loader = new BaseListLoader<ListLoadResult<Icon>>(proxy, new BeanModelReader());
ListStore<BeanModel> store = new ListStore<BeanModel>(loader);
ListView<BeanModel> view = new ListView<BeanModel>();
view.setStore(store);

아이고 이렇게 예쁠 수가요. 그렇지만 이렇게 편한 방법을 쓰려면 POJO에 약간을 수정을 가해줘야 합니다. 물론 이점은 GWT 가 아니라 GXT 에서 제공하고 (강요하는) 것입니다.

1) POJO를 건드리지 않고, GXT 앱 프로젝트 위에 썰렁한 BeanModel 클래스를 지정하기

import com.extjs.gxt.ui.client.data.BeanModelMarker;
import com.extjs.gxt.ui.client.data.BeanModelMarker.BEAN;

@BEAN(net.me2day.gwt.client.Icon.class)
public class IconBeanModel implements BeanModelMarker {
}

이렇게 어떤 POJO의 BeanModel 이다.. 라고 마커 클래스를 만들어두면 GWT compiler가 알아서 매핑해줍니다. 또 다른 방법은,

2) POJO를 건드리기

그저 POJO 에 메소드가 하나 없는 BeanModelTag 인터페이스를 하나 달아두면 됩니다.  이 부분에 대해서는 Ext JS Blog의 Java Bean support with Ext GXT 를 참조하시면 됩니다.

프론트엔드 부분은 이정도로 설명을 마치겠습니다.

아, 하나 더. 한창 jQuery를 써서 만들고 싶었는데 GWT 프로젝트를 하게 되서 아쉬움이 컸었는데요, 한을 풀었습니다. gwtquery란 녀석이 있더군요. GWT 자바 소스코드에서 DOM 접근과 조작을 jQuery 처럼 할 수 있게 해줍니다. static으로 $란 이름을 가진 메서드를 제공해서 이 모든 것을 해결합니다. 하하 -_-

VerticalPanel panel = new VerticalPanel() {
protected void onRender( Element parent, int pos )
{
super.onRender(parent, pos);
$("#profile-picture").load(new Function() {
public boolean f(Event e)
{
$("#welcome-message-1").css("display", "block");
$("#welcome-message-2").css("display", "block");
return true;
}
});
}
};

멋집니다. 자알 동작하네요! 이게 다 import static 덕분입니다. 하하.

프론트엔드는 이 정도로 줄이겠습니다. 백엔드는 다음 기회에 ^^

10Sep/09Off

미투 구글리더를 소개합니다.

Posted by Jang-Ho Hwang

미투 구글리더를 소개합니다.

미투 구글리더는, 구글리더에서 마음에 드는 피드를 공유했을 때 이를 미투데이에 포스팅해주는 매쉬업입니다. 물론 노트와 함께 공유했을 경우 노트 내용이 함께 포스팅 되어 딱딱하지 않은 포스트를 만들어낼 수가 있지요.

미투 구글리더

미투 구글리더

미투데이에 무슨 글을 올려야할지 마땅치 않아 고민하지 않으셨었나요? 그렇다면 구글리더에서 친구들과 함께 보고 싶은 피드 항목을 '공유' 하여 미친들과 함께 볼 수 있습니다.

친구들과 함께 하는 미투데이에.. 딱딱한 피드 제목만 나오는 게 싫다고요? 걱정하지 마세요. 구글리더에서 '메모와 함께 공유'를 하시면 메모 내용이 미투데이에 함께 포스팅 되어 딱딱하지 않은 포스트를 발행할 수 있답니다.

그럼 어떻게 쓰는지 함께 보도록 하지요. 일단 미투 구글리더 서비스에 접근합니다.

미투 구글리더는 구글리더에서 공유한 항목을 자동으로 미투데이에 포스트해주는 매쉬업이기 때문에 미투데이 사용자키를 필요로 합니다. 그러므로 인증을 해주셔야 ^^ 쓸 수 있습니다.

아래와 같은 화면이 나오면 '인증하기' 버튼을 눌러주세요.

인증하기 버튼을 누르면, 미투데이로 넘어가서 여러분에게 미투 구글리더 서비스가 신뢰할 수 있는 서비스인지 확인하는 페이지가 아래와 같이 표시됩니다.

미투데이 API 인증

미투데이 API 인증

수락하시고 나면, 아래처럼 미투구글리더 설정 화면이 나옵니다.

미투 구글리더 설정화면

미투 구글리더 설정화면

무엇보다 먼저, 자신의 구글리더 공유 피드 URL를 입력해주셔야 합니다. 위의 스크린샷에 써있는 것은 제 구글리더 공유 피드 주소인데요, 물론 자신의 것을 입력해야겠죠 ^^? 구글리더에 들어가서 공유 설정에 가서.. 상세보기를 눌러 피드주소를 복사해서 붙여주시면 되는데요, 이게 꽤나 귀찮으실 겁니다. 그럴 경우 하단의 "피드 주소를 입력하기 귀찮으시면 눌러주세요" 버튼을 눌러 손쉽게 해결할 수 있는데요, 자신의 구글 아이디와 비밀번호를 입력하면 제 프로그램이 열심히 크롤링하여 피드 주소를 자동으로 채워줍니다. 물론 아이디와 패스워드는 제 서버에 저장해두지 않습니다. 🙂

하단의 미투 아이콘은 미투 구글리더가 포스팅 해줄 때 사용할 아이콘을 의미합니다. 아무거나 ^^ 선택하셔도 되고요.

마지막 필터! 구글리더의 공유 기능을 자주 쓰지 않으셨던 분이라면 아마 쓸모가 없을테지만, 하루에 20개도 넘게 공유를 하시던 분이라면, 공유하는 모든 항목이 자신의 미투데이에 포스팅되는 것이 꽤나 부담스러울 것입니다. 이런 경우 필터를 활성화하여 원하는 피드만 미투데이로 보내게 할 수 있습니다.

공유항목에 노트를 썼을 때만 포스트 - 구글리더에는 '공유' 와 '메모와 함께 공유' 를 할 수 있습니다. 이 필터가 활성화 되어있을 경우 그냥 '공유' 한 것은 미투데이로 보내지지 않고 '메모와 함께 공유' 한 항목만 미투데이에 보내지게 됩니다.

노트 내용에 #me2day를 썼을 때만 포스트 - 위의 필터만으로는 부족하시다면 ^^ 이 필터를 사용하여 거의 완벽히 미투데이로 포스트되는 피드를 걸러낼 수 있습니다. 공유하고자 하는 항목의 노트 내용 맨 앞이나 맨 뒤에 #me2day 라고 쓴 포스트만 미투데이에 포스트해주는 필터입니다. 물론 실제 포스트 내용에는 #me2day 가 빠져서 등록됩니다. 예를 들어 노트 내용에 '허허 이게 참 말이 되는 얘기인지.. #me2day' 라고 쓰셨을 경우 '허허 이게 참 말이 되는 얘기인지..' 만 들어가게 됩니다.

이렇게 설정을 다 하셨으면, 하단의 '설정저장' 버튼을 눌러주셔야 실제로 계정이 활성화가 됩니다. 만약 미투 구글리더가 마음에 안드신다면 ^^ 그 옆 버튼인 '연동끊기'를 눌러 미투 구글리더가 미투데이에 어떤 포스트도 보내지 않도록 막아두실 수 있습니다.

그럼 미투 구글리더와 함께 재미난 미투데이 하시길 바랄께요!

여기까지 rath 였습니다.