Rath World Notes by Jang-Ho Hwang

15Sep/09Off

Spring framework을 공부하며: 무엇을 dependency로 규정해야 할까?

Posted by Jang-Ho Hwang

도대체 이제서야 Spring 을 공부하는 이유가 뭡니까?

런던에 도착한지 7주가 되어 갑니다. 아무리 본인의 취업의지가 희박하여 취업활동을 거의 진행하지 않았다 하더라도 7주 정도가 지나면, 월 고정 지출금액이 500만원이 넘는 런던 생활을 견딜 배짱이 조금씩 줄어듭니다. 리쿠르팅 사이트들을 면밀히 분석해본 결과 Spring이 너무 많더군요. 그래서 시작했습니다.

무슨 책으로 공부하고 있나요?

Amazon Kindle Store 에서 구입한 Spring Recipes를 보고 있습니다. 원래 Spring에는 전혀 관심이 없었는데 유독 이 책만은 제 흥미를 떨어트리지 않고 있습니다. 현재 16% 읽었습니다. (Kindle이 그렇게 표시해주네요)

소감 한 말씀

수년간 스프링을 건드리지 않았지만, 그래도 지인들께서는 스프링을 많이 쓰시기 때문에 주워들은 것들이 있었습니다. DI와 IoC container가 핵심이라는 말이였는데요. 스프링을 자세히 살펴보기전에는 Dependency Injection이 머 대단한 기술이라고 저렇게 호들갑인가.. 했었는데 Spring IoC container가 훌륭한 것이였더라고요. 기본 컨셉만을 지키는 IoC container를 만들라면 저 혼자서도 하루만에 만들어볼 수 있겠지만, Spring IoC container가 제공하는 feature set을 살펴보고 있노라니 엄두가 나지 않더라고요.

이녀석 어디에 써야 할까

IoC container의 basic feature들을 책을 통해 습득하고, 감 잡았다 싶어 기존 프로젝트에 spring을 적용하며 훈련해보자는 생각으로 얼마전에 만든 미투 구글리더 서버에 spring을 도입해보기로 결정했습니다.

그런데.. 막상 도입하려고 하니 무엇을 dependency로 규정해야할지가 문제였습니다. 미투 구글리더 서버는 POJO 소스코드까지 다 합쳐봐야 1,000라인이 안됩니다. 사용하는 library 들은  httpcomponents, commons-dbcp, slf4j-with-log4j, me2api-java 가 전부입니다.

이용중인 라이브러리들을 의존성이라고 규정해볼까요?

HttpComponents는 의존성이라고 규정하기 어렵습니다. 미투 구글리더 서버의 핵심이 http 요청을 최소의 cpu, i/o 부하로 *잘* 핸들링 하는 것이기 때문입니다. 수많은 사용자들의 구글리더 atompub url을 핸들링 하기 위해 HttpComponents에 최적화된 코드들이 몇 개 들어있는데.. 이것을 의존성으로 가정한다면 프로젝트 자체의 정체성이 모호해지기 때문입니다. 그래서 HttpComponents는 그대로 두기로 했습니다.

DBCP는 의존성이라고 규정하기 대단히 쉽습니다. 그저 빼버리면 그만입니다. 안그래도 commons-dbcp가 500KB가 넘는 commons-collections에 의존성이 걸려있어서 언제 퇴출시켜야할지 눈여겨 보고 있었던 놈이기 때문입니다.

slf4j는 많은 로깅 라이브러리들의 의존성을 날려버리기 위해  log4j, commons-logging, java.util.logging 을 facade로 추상화한 녀석입니다. 그런데 요녀석을 의존성으로 규정하자니.. 어불성설입니다. 한가지, slf4j ext의 Profiler 클래스를  쓰고 있는데, 이 Profiler는 dependency로 볼 수 있겠네요.

me2day-api. 세상에, 제품 이름이 미투 구글리더인데 여기서 미투가 의존성이라니.. 이게 말이 되는건가요? ㅋㅋㅋㅋㅋㅋ. 하지만 잠시 진지하게 생각해봅니다. 미투 구글리더 서버는 대부분의 cpu와 i/o 시간을 구글리더 피드 url을 긁고 분석하는데 사용합니다. 미투데이 API와 함께 하는 시간은 1%가 채 안됩니다. 아무리 제품의 정체성을 존중해준다고는 하지만 조금 아깝다는 생각이 듭니다. 미투 구글리더의 역할을 한줄로 요약하라고 하면, "신나게 지켜보다가 이벤트가 발생하면 sns에 쏜다" 입니다. 여기서 "sns에 쏜다"를 인터페이스로 규정짓고 미투데이를 의존성으로 가정한다면 실제 구현 인스턴스에는 twitter도 있을 수 있고 facebook도 있을 수 있습니다. 이것 참 해볼만한 일입니다.

구글리더. 이것 역시 제품 이름의 66.6%를 차지하는 무게 있는 녀석이긴 하지만, 구글리더와 함께 하는 작업을 인터페이스화 하면 "피딩 문서를 노려보고 있다가, 업데이트가 일어났음이 감지되면, SNS 사이트에 넘길 헤딩과 사용자의 의견을 뽑아낸다" 가 됩니다. 그것이 구글리더가 됐든, Facebook Share가 될지는 상관없습니다. 하지만 미투데이 api와 마찬가지로, 제품 스펙을 늘려서 DI를 적용하려니 적지 않게 찝찝한 기분이 느껴집니다.

configuration. 미투 구글리더의 config.properties 에는 3개의 정보가 있습니다. 피드 polling thread executor에게 줄 휴식시간 값, 미투데이 APP Key, 그리고 백엔드 RDBMS의 설정입니다. 이미 파일로 잘 떨어져 나와있어서 의존성이라 규정하기는 어렵습니다만, 훈련용으로 가볍게 시작하기에는 가장 적절한 부분이라고 생각합니다. Config bean을 정의하고 각 config item 들에 대해 적절한 setter를 만들어주면 바로 해결됩니다. 연습하기에는 아주 적절하지만 real world에는 적절치 못한 예로 보입니다.

마치며

레거시 앱을 보며 DI를 어디에 적용하는 게 좋을까.. 하는 접근은 단연 옳지 않습니다. 이것은 그저 훈련을 위한 것이지요. 그보다는 이 의존성을 어떻게 없애야할까- 할 때나, 개발 미팅 중 "그렇게 되면 어디어디에 의존성이 걸리잖아요?" 라고 말하게 되는 부분을 깔끔히 해결해주는 고마운 프레임워크라고 보는 것이 옳바른 듯 합니다. 조금 더 나아가 Spring IoC container의 feature를 십분 활용하여, 평소에는 그것이 의존성이라고 생각도 못한 부분들에 대해 다시 한 번 생각할 수 있는 기회를 주는 현자 역할도 해준다는 생각이 드네요.

위에서 살펴본 것처럼 DI를 마구 적용하려고 하면 한도 끝도 없는 것 같습니다. 그러나 개발자에게 큰 경각심을 주는 부분은 프로젝트에 Spring IoC container의 DI를 적용하기로 하면 프로젝트 디자인이 '개발자의 순수 의지와는 상관없이 깔끔해진다'는 장점은 무시하지 못할 부분이라고 생각합니다.

이제 글을 그만 쓰고 생각을 행동에 옮길 시간입니다. 안녕히!

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 였습니다.