생각대로 되는 nodejs, 또 잊어먹기 전에 메모!

지난달부터 재미삼에 패치팡이란 애니팡 아류작을 하나 맹글어보고 있는데, 부득이(?)하게 랭킹서버까지 만들어야했다. 그리고 지난주였나? 페북과 연동해서 친구들과 경쟁구도를 만들기 위해 랭킹서버를 약간 수정했다. 물론 데이터 모델도 달라졌고, API도 약간의 수정이 있었다. 귀차니즘에 수정을 최소화하다보니 많은 기능을 넣지는 않았음에도 불구하고 삽질의 연속이었다. 그때 정리했어야하는데 일단 잊어먹기 전에 생각나는대로 정리해보자.

1. 청크(Chank)데이터 이어붙이기 일단

페이스북 Graph API를 이용해 친구목록을 가져와야했다. 페이스북의 모든 API는 https로 호출한다. (혹시 http로 호출하는 API가 있을찌도 모르겠으나,.. 일단 문서에는 없는듯..) 뭐 https도 암호화와 패킷 검증을 제외하면 프로토콜은 http와 거의 대동소이하다.  여튼 nodejs를 이용해 API 호출후 응답을 받으려면 다음과 같이 ‘data’ 이벤트를 통해 데이터를 수신받는다.

var https = require('https');
var url = 'https://graph.facebook.com/me/friends?fields=id&access_token='+ token;
https.get(url, function(response) {
    response.on('data', function(chunk) {
        console.log('receive.. chunk data...', chunk);
        var friends = JSON.parse(buffer.join(''));
    });
});

HTTP와 HTTPS 객체에 대한 보다 자세한 설명은 nodejs API 문서 를 참고하자. 위와 같이 코드를 작성하고 실행해보면 간간히 JSON.parse 중에 에러가 난다. 아! 왜 자꾸 에러나는거야? 짱내고 있었는데.. 생각해보니 data 이벤트로 받는 녀석은 chunk 단위다! 그럼 당연히 데이터가 짤릴수도 있는거지! 아하~~ 😀 물론 한번에 담겨서 넘어올때도 있지만ㅋㅋㅋ 여튼 콘솔에 값을 출력해보면,.. [Buffer 0x45…..] 버퍼 어쩌구저쩌구가 찍힌다 그래서 버퍼에 담았다가 이어붙이면 되겠다 싶었다. 그런데!!! 어떻게하지? 어떻게 붙이지? Buffer 객체를 만들어야하나? Stream 객체로 해야하나? 서버개발에 익숙치 않으니 Java에서 쓰던 방법들을 동원해서 별에별 방법을 다 써봣다. 너무 Java틱했나? 결론은 다 잘 안됐다. OTL… 도대체 왜 안되는거야? 라고 씩씩대면서 4시간을 날렸다. 그런데 해결책이 아~ 허무해~~! 너무나 단순했다. 그냥 배열에 담았다가 join만 하면 되는거였어!! 정말 javascirpt 다운 해결책!~ ㅜㅜ 아~ 내 아까운 시간~ 그래도 뭐 4시간동안 API문서보고 이거저거 해봤다는거에 위안을 삼으며 다음과 같이 수정!

var https = require('https');
var url = 'https://graph.facebook.com/me/friends?fields=id&access_token='+ token;
https.get(url, function(response) {
    var buffer = [];
    response.on('data', function(chunk) {
        console.log('receive.. chunk data...', chunk);
        buffer.push(chunk);
    });
    response.on('end', function() {
        var friends = JSON.parse(buffer.join(''));
    });
});

위와 같이 수정하면 데이터 짤림 없이 온전한 데이터를 파싱하는데 성공!!

2. Express 모듈에 미들웨어 장착하기

이번에도 다소간의 삽질이 있었지만, 이번엔 지극히 자바스크립트 다운 생각으로 임했더니 매우 쉽게 해결된 문제다. 먼저 nodejs 로 서버를 만들때 가장 많이 쓰는 모듈이 바로 Express 모듈이다. 이젠 거의 socket.io와 더불이 이바닥 표준이 아닌가 싶다. 여튼 GET 방식이든 POST 방식이든 요청 URL에 한글이 들어갈 경우 반드시 인코딩해서 넘겨야하고 서버에선 디코딩 과정이 필요하다.  아니면 서버에서 무조건 URL을 자동 인코딩해서 처리해야한다. 그런데 난 이 모든 것들을 Express가 알아서 해주는줄만 알았다. 그런데 그게 아니었다. ㅜㅜ 너무 맹신했나? 그래서 데이터를 넘길때 서버에서 인코딩하고 다시 응답을 만들때 디코딩하는 생쑈를 했다. 그런데 말이지.. 자꾸 이상한 생각이 든다,.. 이거 너무 귀찮은데,…-_-;;.. 왜 이걸 Express에서 안해주는거지?… API를 뒤졌다.. 역시!!.. 저 밑에 있네.. 바로

Middleware 라는 항목에.. 빙고~!!

app.use(express.bodyParser());

// is equivalent to:
app.use(express.json());
app.use(express.urlencoded());
app.use(express.multipart());

바디파서 한줄이면 인코딩은 알아서 척척척 해준다. -_-;… 역시 자바스크립트는 생각대로 하면된다. 느무 조아요!! 일단 여기까지 또 생각나면… 이어서..

원 데이, 2012년 최고의 영화

2012년 마지막날 본 이 영화 올해의 My Best로 선정하고 싶다.
그동안 영화를 봐도 글 한번 안남겼는데…
이 영화의 감동은 꼭 남기고 싶다.

일단 포스터 이미지 한장 박아주시고,… 앤 해서웨이 눈부시게 아름답구나..

스토리 구성이 독특하다라는 얘기만 듣고 정말 보고 싶었는데..
오늘에서야 봤다. 물론 사전 지식은 전혀 없었다.  
독특한 스토리 구성때문인지 초반 이야기는 따라가기 버거웠다.
초중반을 넘어가면서 이야기의 살이 채워지니까 집중하게 되더라..

그리고 클라이막스 뚜둥~
“잊은줄 알았는데… ” 이거 맞나?
“지웠는지 알았는데.. ” 이거였나?
여튼간에.. 가슴을 후펴파는 대사… 왜 이렇게 슬프지?

사랑하고 싶어지는 날이다.
용기가 샘솟는 날이다.
DVD판을 하나 사야겠다.

별점은 얘기하지 않아도 5개 만점!!

자바스트립트 MVC 모델편

글을 쓰다보니 길어져 부득히 시리즈로 나눴다. 항상 이렇게 시리즈로 나누면 중간에 쓰다 마는데.. ㅇㅎㅎ 여튼 뭐 언젠가는 마무리하겠지.. 사실 이글에 앞서 “UML로 코딩하기라는 글”이 있는데 글이 마무리가 안되서, 마무리 되면 공개하는 걸로 하고 그냥 시이작~!

자바스트립트를 보다 구조화해서 코딩 할 수 없을까?

MVC패턴은 요즘 나의 최대 관심사다. 최근 몇년간 TDD니 BDD니 하면서 날코딩과 테스트를 중심으로 작성했던 코드들도 수두룩하다. 하지만 이 코드들은 전부 유지보수에 실패했다! 물론 실패할수밖에 없었던 구구절절한 사연들이 많지만 그건 중요한게 아니므로 패스~ 여튼 결국 난 테스트 주도 프로그래밍에 대해 다시 한번 생각해볼 수 밖에 없었다. 물론 UI 개발에 한정적인 얘기지만 TDD는 뭔가 UI 개발과는 어울리지 않는다는 결론에 이르렀다. 그게 벌써 2년전이다. 그렇게 해서 찾기 시작한 대안이 바로 설계를 바탕으로 하는 프로그래밍이다. 사실 설계라는 것이 스펙에 따라 계속 변경된다고 믿었기에 초기 설계에 그렇게 큰 의미를 부여하진 않았다. 하지만 최근 1년간 MVC 패턴을 이곳저곳에 적용하면서 이른 나의 결론은 초기설계가 중요하고, 잘못된 설계는 나중에 영영 되돌릴수 없다라는 결론도 얻었다. 여기도 사실 현실적인 구구절절한 사연이 있지만.. 패스~ 하지만 확실한 것은 MVC 패턴을 이용한 프로그래밍이 가장 만족스러웠다는 사실이다. 여튼 현실 얘기는 짚어치우고 오늘은 자바스크립트를 이용해 MVC 코드를 어떻게 작성할수있을까? 만을 고민해보자. 사실 나도 MVC를 제대로 이해하고 쓰고 있는건가? 라는 생각을 종종한다. 그만큼 생각의 변화가 많았다. 아마도 공력이 더 쌓이면 또 생각이 바뀌지싶다. 여튼 각설~

자바스크립트로 MVC 구현하기

MVC를 그림으로 그려보면 그 중심에는 당연히 컨트롤러가 있다. 그래서 컨트롤러가 가장 중요한게 아닐까? 라는 생각을 하게 되는데.. 사실 그 말이 맞기도하고 그렇치 않기도하다. 내가 생각할때 MVC는 모델과 뷰 그리고 컨트롤러가 모두 중요하다. 그리고 그 각자의 역할이 매우 명확한데 이것을 최근에야 깨달았다.

MVC 패턴, from 구글 이미지 검색

모델 설계

모델은 매우 중요하다. 특히 모델 구현에 있어서 가장 중요한 것은 Observer 라고 얘기하는 관찰자를 만드는 것이다. 흔히들 MVC 패턴으로 설계된 UML들을 보면 옵저버패턴이 등장하고, 아래 그림과 같이Observer 라는 이름의 클래스도 등장한다. 참고로 난 패턴 용어를 잘 모른다. 어쩌다보니 자연스럽게 알게된 경우가 대부분이다. 혹시 이 글을 읽고 있는 분들도 책으로만 패턴을 공부하지 않기를 바란다. 여튼 훈수는 여기까지… ㅋ

Observer Pattern
일반적인 옵저버 패턴 from wikipidia http://en.wikipedia.org/wiki/Observer_pattern

여기서 Observer 라는 단어의 뜻을 한번 생각해보자. “관찰” 이라는 뜻인데,.. 관찰하다라는 말이 무슨 말일까? 스타크래프트의 옵저버를 연상해도 좋다!. 여튼 옵저버는 현재 상황을 유심히 살펴보다가 누군가에게 알려주는 역할을 한다. 이때 누군가에게 알려주려면 +notify() 메소드를 사용한다.

그럼, Subject는 무슨 뜻일까? 이건 좀 번역하기 애매하다. 여튼 UML에 그려진 대로만 해석하면 Subject 클래스는 옵저버 객체를 관리하는 녀석이다. 관리할 옵저버는 +registerObserver() 메소드를 이용해 등록한다. 그리고 이렇게 등록된 옵저버는 +observerCollection 이라는 변수에 담아 관리한다는 사실도 위 설계도를 보면 알 수 있다.

옵저버 객체 구현

여기까지가 일반적인 옵저버 패턴이고 저 설계대로 구현하려면, 저 설계의 의미를 곱씹어 봐야한다. 일단 Observer와  ConcreteObserverA와의 관계는 상속관계다. 의심할 여지없이 명확하다. 구현도 어렵지 않으므로 패스~!

다음으로 Observer와 Subject와의 관계를 살펴보자. 먼저 Subject 클래스쪽에 빈 마름모가 등장했다. 빈 마름모의 의미는 Subject 객체가 어디선가 생성된 Observer 인스턴스(혹은 객체)를 인자로 받는다는 의미다. 그리고 여기서 또하나 주목해야 할 것이 있다. 바로 의존관계다!. 보통 의존관계는 화살표로 표시하는데, 이 두관계는 눈을 씻고 찾아봐도 화살표가 없다! 그럼 서로 의존성이 없다는 얘긴가? 땡~~!! 틀렸다! 선이 연결되어 있으므로 서로가 서로를 의존한다고 봐야한다.

여튼, Observer와 Subject는 화살표 없이 실선으로 연결되어 있으므로 서로가 서로를 알고 있고, 서로 간의 호출 할수있는 참조를 가지고 있다는 의미가 된다. 그리고 보통 이런 경우는 두 객체를 하나로 합체할 수있다! 뚜둥~!  그래서 그림을 다시 그려보았다.

옵저버 객체
내멋대로 합체한 옵저버 객체

이제 이 설계를 기반으로 구현해보자. 다른 언어로 구현해도 마찬가지겠지만,  클래스가 많아지면 각 클래스간의 역할관계를 정의해야하기 때문에 복잡해진다. 그래서 일단 편한대로 내식대로 옵저버를 설계해봤다.

+ observe( 메시지, 행동 ) : 관찰할 메시지를 등록하고, 메시지를 받으면 행동을 정의한다.

+ notify(메시지, 정보) : 관찰하고 있던 상태가 변경하면 누군가에게 알린다.

옵저버 패턴을 구현할때 핵심은 이벤트를 어떻게 전달하냐인데, 이벤트를 전발하는 방식은 대부분 콜백으로 이루어진다. 즉 콜백함수를 등록해놓고 호출되도록 만든다. 여기서 문제는 자바스크립트가 싱글 스레드라는 사실이다. 싱글 스레드이기 때문에 함수 호출 시점에 따른 의존성도 생길수 있다. 예를 들면 아래와 같다.

notify 메시지가 동기식일 경우 생기는 문제
notify 메시지가 동기식일 경우 생기는 문제

옵저버 객체에서 MethodA() 호출후, notify() 함수를 통해 외부에 MessageC를 날렸다. 그리고 MethodB()를 호출한 상황인데, MessageC 를 받는 객체에서 어떤일이 행해진다면, MethodB()의 호출 시점은 MessageC 를 구현한 객체에 따라 달라진다.

MethodA -> MessageC ( 여러 메소드가 중간에 낑겨들어갈수있음…) -> MethodB

위와 같은 흐름이 보장 된다면, MessageC에서 데이터를 저장하고, MethodB에서 저장한 데이터를 전제로 구현을 해도 큰 문제가 발생하지 않는다.

하지만,  MessageC에서 Ajax 호출이 발생하면 동기식 상황들은 깨지고 만다. 따라서 완간하면 notify() 함수 구현은 반드시 동기식 호출만 하거나 혹은 아예 비동기를 전제로 작성하는게 맘 편할수있다.

나는 그냥 비동기를 가정하고 notify() 호출은 setTimeout을 이용해 메시지 큐에 담아 버렸다. 따라서 위와 같이 호출시 메소드 호출순서는 아래와 같다.

MethodA -> MethodB -> MessageC

여튼 실제 구현한 Observable 객체 다음 링크를 참고한다.

옵저버 객체를 이용한 모델 구현

이제 앞에서 작성한 옵저버 객체를 이용해 모델 객체를 구현해보자. 일단 모델은 Observable 객체를 상속받는다. 자바스크립트는 프로토타입을 이용해 상속받는다. 프로토타입 상속에 관련된 내용은 자바스크립트 가든 번역본을 참고한다.

var SampleModel = function(){};
SampleModel.prototype = new Observable();

다음으로 모델에 저장할 데이터를 정의한다. 데이터를 정의할때는 public으로 정의하고 싶다면 this 객체를 이용해 정의하고, private 으로 정의하고 싶다면 var 문을 이용해 정의한다.

var SampleModel = function(){
    this.publicData = {};
    var privateData = {};
    
    this.getPrivateData = function(){
        return privateData;
    }
};

this.getPublicData = function(){
    return this.publicData;
};

공개 데이터는 아래와 같이 프로토타입 공간에 메소드를 정의해 가져올수도 있다. 하지만 아래 방법보다는 공개 데이터이므로 프로퍼티로 직접 접근해 사용하는 것이 보다 효율적이다.

SampleModel.prototype.getPublicData2 = function(){
    return this.publicData;
}

옵저버 객체에 감지할 이벤트 등록

자, 그럼 옵저버 객체를 이용해 감시할 이벤트를 정의해보자.

var model = new SampleModel();
model.observe({ "CHANGE_STATE" : function(e){
    console.log(e.data) }
});

위 코드는 model에서 발생하는 CHANGE_STATE 메시지를 감지한다.

모델에서 이벤트 발생

자 그럼 CHANGE_STATE 메시지를 직접 발생시켜보자. 메시지는 모델에서 상태가 변경됐을때 발생함을 전제하므로 setData 함수를 만들어 정의한다.

SampleModel.prototype.setData = function(data){
    this.publicData = data;
    this.notify("CHANGE_STATE", {data: this.publicData});
}

여기까지 모델을 살펴봤다. 사실 글쓰다 귀찮아서 마지막은 후다닥 코드만 썼는데.. 나중에 View편을 작성할때 좀더 보강하기로 하고, 오늘은 여기까지… 혹시 질문있으면 댓글 고고씽~