2012년 9월 26일 수요일

Meteor auth branch 변경 사항. 0.4.1 버전 9/22 기준

http://spectrumdig.blogspot.kr/2012/08/meteor-039.html 이후로 업데이트가 좀 되었다.

api key/secret 을 더 이상 소스 안에서 관리 안함.
accounts-ui 를 사용할 것을 권장하고 있다.

$ meteor mongo 로 접근
> db["accounts._loginServiceConfiguration"].find()
{ "service" : "facebook", "appId" : "appID", "secret" : "app Secret", "_id" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxx" }
이와 같은 형태로 설정.

재설정시 서비스 명을 기준으로 remove
> db["accounts._loginServiceConfiguration"].remove({service:"facebook"})

그룹스의 글을 일단 옮겨와 본다.
기존에 setSecret method 및 config method 는 전부 지우고 Meteor.accounts.configuration collection 에 넣으라는 소리를 하는데

그것만으론 안된다. appID하고 appSecret은 지우고 collection 에 넣든 accounts-ui 를 써서 다시 지정했다고 해도 OAuth 인증 후 callback 으로 돌아오는 URL이 무조건 localhost:3000으로 되는 오류가 있는데 ROOT_URL 환경변수를 어플리케이션의 URL로 설정하던가
__meteor_runtime_config__.ROOT_URL = "http://<callback 받을 어플리케이션 URL>" ;
로 서버쪽 startup function 안쪽에 넣던가 하라고 한다.
그룹스 없으면 어쩌려고 이놈들이;;

출처는 https://groups.google.com/d/msg/meteor-core/cVyomNZJzBg/3GdyEmkgtQgJ

이래저래 해봐도 다 안되서 그냥 환경변수 잡았다. 그리고 서버사이드에서 초기화 할때 collection을 잡았다.

결론은 아래 3단계 작업을 해주면 된다.

  1. meteor 기동전에 환경 변수를 잡는다.

    export ROOT_URL=http://<callback 받을 어플리케이션 URL>
  2. server쪽에 startup function 안에 텅빈 Meteor.accounts.configuration collection에 설정을 insert 한다.

    config = Meteor.accounts.configuration
    config.insert({ "service" : "facebook", "appId" : "appID", "secret" : "app Secret"});
  3. meteor 기동
기존에 Meteor.user() 를 사용했다면 오브젝트 구조가 바뀐 것에 주의하자.
Meteor.user().name 을 사용했다면 Meteor.user().profile.name 을 사용해야한다.

* PS : accounts-passwords 패키지가 accounts-password 로 단수형이 되었다 (....)

2012년 9월 18일 화요일

시간 종결자 moment.js


http://momentjs.com/
날짜 종결자라고 봐야겠다.
time ago 찾다가 발견. formatting에서부터 i18n까지 거의 대부분의 스펙을 제공함.

한가지 기능만 살펴보면
블로그나 SNS 등에서 쓰는 상대시간(humanize time이라고 하더라)도 지원.
http://momentjs.com/docs/#/displaying/from/ 의 내용을 참조하면
var start = moment(); // 현재시각 기준
혹은
var start = moment(1347961213698);
식으로 초기화하고

start.fromNow();
로 얼마나 시간이 지났는지를 humanize time 으로 반환한다.
a few second 대신 now 를 사용하고 싶다면
moment.relativeTime 를 수정하여

moment.relativeTime.s='now';
moment().fromNow(true);
이런식으로 true 옵션을 줘서 ago를 떼어내고 임의로 지정할 수 있다.

2012년 9월 17일 월요일

cloud9ide remote access tip

웹브라우저만 있으면 파일 업로드까지 가능한 막강 웹IDE인 cloud9ide 가 드디어 node 0.8.x 를 지원한다.
하지만 engine.io 탑재와 함께 내부기작이 바뀌었는지
외부에서 사용하기 위해 -l 옵션을 줘서 0.0.0.0 을 지정하면 클라이언트쪽 자바스크립트에서도 0.0.0.0을 참조하는 구조로 바뀌었다.
그래서 로컬로만 사용가능하고 외부에서 사용시 클라이언트쪽 통신이 끊어질 수 밖에 없는데
이래서야 원격 협업툴이 되지 않지.
그렇다고 직접 공인 IP를 찌르면 EADDRNOTAVAIL 오류가 난다.

$ node cloud9/server.js -w ~/workspace -p 8123 -l 123.123.123.123
path.existsSync is now called `fs.existsSync`.
connect plugin start

events.js:66
        throw arguments[1]; // Unhandled 'error' event
                       ^
Error: listen EADDRNOTAVAIL
    at errnoException (net.js:768:11)
    at HTTPServer.Server._listen2 (net.js:891:19)
    at listen (net.js:935:10)
    at Server.listen (net.js:992:9)
    at dns.js:71:18
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

엄청 오랫동안 이 문제로 헤맸는데
결국 클라이언트는 접속할 도메인을 사용하게 하고
서버 사이드에서만 0.0.0.0 을 사용하면 되는 문제이지 않을까 싶어서
/etc/hosts 를 열고

c9.mydomain.com    0.0.0.0

이부분을 추가. 물론 c9.mydomain.com 대신 자신이 사용하는 도메인을 쓰자.
다행스럽게도 잘 된다. 아아 다행이야 다행.

물론 기동은 -l 옵션 줄때 내 도메인을 주자

$ node cloud9/server.js -w ~/workspace -p 8123 -l c9.mydomain.com
이런 식.
-w 는 작업디렉토리
-p 는 포트번호
-l 이 접속 주소인데
/etc/hosts 설정에 따라 0.0.0.0 과 같은 것이 되어서 내부적으로는 -l 0.0.0.0 과 동일한 셈이다.
사악한 꼼수지만 크게 건들지 않아도 되는 간단한 해결책.

2012년 9월 9일 일요일

Meteor 테스트 전략

* package를 만들고 테스트 할 때
/usr/local/meteor/packages/livedata 참조
package.js 내용을 보면


Package.describe({
  summary: "Meteor's latency-compensated distributed data framework",
  internal: true
});

Package.on_use(function (api) {
  api.use(['stream', 'uuid']);
  api.use(['json', 'underscore', 'deps', 'logging'], ['client', 'server']);

  // livedata_connection.js uses a Minimongo collection internally to
  // manage the current set of subscriptions.
  api.use('minimongo', ['client', 'server']);

  api.add_files('writefence.js', 'server');
  api.add_files('crossbar.js', 'server');

  api.add_files('livedata_common.js', ['client', 'server']);

  api.add_files('livedata_connection.js', 'client');

  api.add_files('livedata_server.js', 'server');


  api.add_files('client_convenience.js', 'client');
  api.add_files('server_convenience.js', 'server');
});

Package.on_test(function (api) {
  api.use('livedata', ['client', 'server']);
  api.use('mongo-livedata', ['client', 'server']);
  api.use('test-helpers', ['client', 'server']);
  api.use('tinytest');

  api.add_files('livedata_connection_tests.js', ['client']);
  api.add_files('livedata_tests.js', ['client', 'server']);
  api.add_files('livedata_test_service.js', ['client', 'server']);
});

이런 식으로 정의해놓았다.
add_files 펑션에 각각 클라이언트/서버 사이드를 지정하고 해당 js 테스트를 돌릴 수 있도록 지정한다.

만일 livedata package 를 수정했고 테스트 여부를 확인하고 싶으면 단순하게

$ cd /usr/local/meteor/packages/livedata
$ meteor

[[[[[ /usr/local/meteor/packages/livedata ]]]]]

Running on: http://localhost:3000/


한뒤 localhost:3000 을 보면 테스트를 수행하는 것을 확인할 수 있다.

* application 에서 test 를 만들때
최상위 경로에 tests (test 가 아니다 복수형으로 써야함) 디렉토리를 만들면
meteor 에서 tests 경로 아래에 있는 것들은 가져오지 않는다. (중요!)
가끔 서버사이드에서 돌아가는 것들을 public 에 넣어서 돌리는 분들이 있는데
워험천만한 행동입니다.
tests에 넣고 하세요.

2012년 9월 8일 토요일

webstorm 에서 meteor Application 을 기동하는 방법

webstorm 은 훌륭한 web-IDE고
meteor도 훌륭한 javascript framework 인데

meteor의 경우 실제로 node.js 파일을 실행하는 부분이 없어서 어딜 진입점으로 해야할지 난감하게 느낄 수도 있다.
고민하지 말고 meteor 실행 파일을 까보자.

cat `which meteor`
해보니 맨 마지막 두줄에


export NODE_PATH="$DEV_BUNDLE/lib/node_modules"
exec "$DEV_BUNDLE/bin/node" "$METEOR" "$@"

이 두문장이 보인다.

$DEV_BUNDLE 이 어딘지 읽어보기 귀찮아서 찍어봤더니 meteor 설치 디렉토리더라
내 경우는 /usr/local/meteor
which meteor 에서 bin 상위 경로라고 보면 된다.
Edit Configuration 에서 Node.js 를 추가하고 아래와 같이 설정해보자.



이런식으로 주니 무리가 없더라. Environment variables 에
NODE_PATH 항목을 추가하고 meteor 를 설치한 경로 + /lib/node_modules 를 추가하는게 포인트.
눈치빠른 분이라면 meteor application 에 외부 package를 추가하고자 할때 어떻게 해야할지 감 잡으신 분도 있을거다

아무튼 저렇게 하고 가동하면 정상적으로 동작한다.
이제 터미널 옮겨다니 말고 편하게 콘솔창으로 서버 메시지를 보자.

2012년 9월 1일 토요일

node.js 에서 system call을 사용하는 방법


cloud9 을 사용해서 작업하고 있는데
업로드는 드래그&드랍으로 훌륭하게 지원하는 반면 다운로드를 지원하지 않아 조금 아쉽다.
child_process 패키지를 사용하여 폴더를 전체를 압축한 다음 내려주자.
http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
spawn을 사용해서 만들었다.

대충 express 를 통해 파일을 만들고 route/index.js 를 다음과 같이 해보았다.
/*
 * GET home page.
 */
var spawn=require('child_process').spawn;
var fs=require('fs');
exports.index = function(req, res) {
var zip=spawn('zip',['-r', 'archive.zip', '.', '-i', '*']);
zip.stdout.on('data', function (data) {
console.log('stderr: ' + data);
});
zip.stderr.on('data', function (data) {
console.log('stderr: ' + data);
});
zip.on('exit', function() {
res.setHeader('Content-type', 'application/octet-stream')
res.setHeader('Content-disposition', 'attachment; filename=archive.zip');
res.write(fs.readFileSync(__dirname+'/../archive.zip'));
res.end();
});
}

stdout.on -> 표준 출력 가져오기
stderr.on -> 오류 출력 가져오기

zip 파일로 내려주기 위해 Content-type 과 파일명을 setHeader 를 통해 붙여주자.
참 쉽죠?