Rev. 2.73

역시나, 기업 요구사항은 파일 교환보다는 문서 관리 쪽으로 많이 치우쳐져 있더군요. 그래서 문서관련 기능을 강화하기 위해 매시업할 수 있는 서비스들을 찾아보던 중 Scribd라는 서비스를 알게 되었습니다. 이 서비스는 업로드한 문서를 HTML5(또는 플래시)에서 출력할 수 있도록 컨버전해주는 서비스였습니다. 변환된 문서는 원본 문서와 다른 점을 찾아보기 어려울 정도로 품질이 좋더군요. 그리고 자신들이 가진 기술을 공유에 목적을 두고 마음껏 사용해 주기를 바라는 착한 회사였습니다.

Scribd의 API는 문서를 웹페이지에 임베드하기 위한 자바스크립트 API와 문서를 등록하거나 메타데이터를 조회하거나 검색 등을 할 수 있는 플랫폼 API로 나뉩니다. 플랫폼 API에는 Ruby, Java, PHP, Python 등의 라이브러리가 있었지만 아쉽게도 Node.JS용 라이브러리만 쏙 빠져있어서 래핑 모듈을 만들고 배포합니다. 아래는 GitHub에 공개한 scribd 모듈의 API 문서입니다.

Scribd Platform API binding for Node.JS

Installing:

$ npm install scribd

Usage:

var Scribd = require('scribd');

var key = "ENTER-YOUR-API-KEY-HERE"
  , secret = "ENTER-YOUR-API-SECRET-HERE"; 

var scribd = new Scribd(key, secret);

// or (>= 0.1.5)

var scribd = new Scribd({
    apikey: key
  , secret: secret
});


/**
 * docs.upload
 */

scribd.upload(function(err, res) {
  console.log("\n scribd.upload", err, res);
}, "./my.docx", "doc", "private");

// or (>= 0.1.5)

scribd.upload({
    file: "./my.docx"
  , docType: "doc"
  , access: "private"
}, function(err, res) {
  console.log("\n scribd.upload", err, res);
});

Methods:

/**
 * Docs Method
 */

// docs.upload (callback, file, [docType], [access], [revId])
scribd.upload(function(err, res) {
  console.log('\n scribd.upload', err, res);
}, './document.path');

// docs.uploadFromUrl (callback, url, [docType], [access], [revId])
scribd.uploadFromUrl(function(err, res) {
  console.log('\n scribd.uploadFromUrl', err, res);
}, 'url');

// docs.getList (callback)
scribd.getList(function(err, res) {
  console.log('\n scribd.getList', err, res);
});

// docs.getConversionStatus (callback, docId)
scribd.getConversionStatus(function(err, res) {
  console.log('\n scribd.getConversionStatus', err, res);
}, 'docId');

// docs.getSettings (callback, docId)
scribd.getSettings(function(err, res) {
  console.log('\n scribd.getSettings', err, res);
}, 'docId');

// docs.changeSettings (callback, docId, [title], [description], [access], [license], [showAds], [tags])
scribd.changeSettings(function(err, res) {
  console.log('\n scribd.changeSettings', err, res);
}, 'docId', 'title');

// docs.getDownloadUrl (callback, docId)
scribd.getDownloadUrl(function(err, res) {
  console.log('\n scribd.getDownloadUrl', err, res);
}, 'docId');

// docs.getStats (callback, docId)
scribd.getStats(function(err, res) {
  console.log('\n scribd.getStats', err, res);
}, 'docId');

// docs.delete (callback, docId)
scribd.delete(function(err, res) {
  console.log('\n scribd.remove', err, res);
}, 'docId');

// docs.search (callback, query, [numResults], [numStart], [scope])
scribd.search(function(err, res) {
  console.log('\n scribd.search', err, res);
}, 'Node.JS', 1);

// docs.getCategories (callback, docId)
scribd.getCategories(function(err, res) {
  console.log('\n scribd.getCategories', err, res);
}, 'docId');

// docs.featured (callback, [limit], [offset], [scope])
scribd.featured(function(err, res) {
  console.log('\n scribd.featured', err, res);
});

// docs.browse (callback, [limit], [offset], [categoryId], [sort])
scribd.browse(function(err, res) {
  console.log('\n scribd.browse', err, res);
});

// docs.uploadThumb (callback, file, docId)
scribd.uploadThumb(function(err, res) {
  console.log('\n scribd.uploadThumb', err, res);
}, 'thumbnail.path', 'docId');


/**
 * Thumbnail Method
 */

// thumbnail.get (callback, docId, [width], [height])
scribd.getThumbnail(function(err, res) {
  console.log('\n scribd.getThumbnail', err, res);
}, 'docId', 256);


/**
 * User Method
 */

// user.login (callback, username, password)
scribd.login(function(err, res) {
  console.log('\n scribd.login', err, res);
}, 'username', 'password');

// user.signup (callback, username, password, email, [name])
scribd.signup(function(err, res) {
  console.log('\n scribd.signup', err, res);
}, 'username', 'password', 'email', 'name');

// user.getAutoSigninUrl (callback, [nextUrl])
scribd.getAutoSigninUrl(function(err, res) {
  console.log('\n scribd.getAutoSigninUrl', err, res);
}, '/');

Have fun!

License

MIT <3

Comments

얼마 전(지난 오월 초) 웹 기반 IDE인 클라우드9(개인 사용자용)의 코드베이스의 아키텍처가 업그레이드되는 대대적인 마이너 업데이트가 있었습니다. 설치방법과 실행방법 그리고 소소하게 UI 등에 변동사항이 있어 주요한 내용을 기록합니다.

0.7.x부터는 자체적으로 빌드해서 사용해 오던 node-builds-v4를 더이상 사용하지 않습니다. 이것은 실행 환경에 문제가 발생하면 대처하기 어려운 부분을 해결한 것입니다. 그래서 Node.JS 0.6.15 이상, NPM 1.1.16 이상을 필요로 합니다. 그리고 sm(Sourcemint)를 이용하여 소스상태를 관리하고 설치 및 갱신 하도록 변경되었습니다. 참고로 Sourcemint는 테스트 및 배포를 이롭게 하기 위해 프로젝트를 빌드하는데 필요한 소스와 패키지를 자체적으로 관리하며, 소스 리포지토리에서 설치, 변동 사항 반영 등이 처음부터 끝까지 완전히 자동화된 소프트웨어 구축 환경을 제공합니다.(참 멋진 녀석이군요!)

다음과 같이 간단하게 설치할 수 있습니다:

# npm install -g sm
# sm clone --dev https://github.com/ajaxorg/cloud9/tree/master cloud9

또는

# git clone https://github.com/ajaxorg/cloud9.git cloud9
# cd cloud9
# sm install

그리고 실행합니다:

# cd cloud9
# bin/cloud9.sh

프로젝트 폴더를 지정할 수도 있습니다:

# bin/cloud9.sh -w ~/git/myproject

물론 포트를 변경할 수도 있습니다(기본 포트는 3131):

# bin/cloud9.sh -p 80

아쉽게도 호스트네임(Hostname)은 변경을 할 수 없게 되어있지만 configs/default.js 파일의 20라인부터 다음과 같이 수정해 주면 -h 옵션으로 호스트네임을 변경할 수 있습니다.

변경 전:

var projectDir = (argv.w && path.resolve(process.cwd(), argv.w)) || process.cwd();
var fsUrl = "/workspace";

var port = argv.p || process.env.PORT || 3131;

var config = {
    name: "Cloud9",
    tmpdir: __dirname + "/../.architect",
    basePath: __dirname + "/../plugins-server",
    containers: {
        master: {
            title: "Cloud9",
            plugins: [{
                packagePath: "./cloud9.connect",
                port: port,
                host: "localhost"
            }, {
                packagePath: "./cloud9.sourcemint",

변경후:

var projectDir = (argv.w && path.resolve(process.cwd(), argv.w)) || process.cwd();
var fsUrl = "/workspace";

var port = argv.p || process.env.PORT || 3131;
var host = argv.h || process.env.HOST || "localhost";

var config = {
    name: "Cloud9",
    tmpdir: __dirname + "/../.architect",
    basePath: __dirname + "/../plugins-server",
    containers: {
        master: {
            title: "Cloud9",
            plugins: [{
                packagePath: "./cloud9.connect",
                port: port,
                host: host
            }, {
                packagePath: "./cloud9.sourcemint",

이제 호스트네임 옵션이 활성화 되었습니다:

# bin/cloud9.sh -h firejune.io

업데이트는 더욱 간단합니다:

# git pull
# sm update

만약 npm에서 디펜던시(Dependency)가 발생하면 이렇게 해결합니다:

# sm install

아래 보이는 화면은 크롬에서 애플리케이션 모드로 작동하는 Cloud9의 모습입니다. 내비게이션 사이드바가 사라진 대신 상단의 도구영역이 활성화되었으며, 메뉴를 자동 숨김 하는 기능 등이 추가되었습니다.

스크린샷 2012-05-14 오후 2.02.11.png

이번 회사 프로젝트에는 GitHub와 Cloud9 IDE을 도입해서 협업하기로 결정했습니다. Cloud9을 맥OS에 로컬로 설치한 후 백그라운드에서 실행합니다. 그리고 크롬의 애플리케이션 바로가기 기능을 이용해서 Dock에 등록하고 실행하면 브라우저 UI가 모두 사라져 마치 레알(?) 애플리케이션을 실행하는 느낌으로 사용할 수 있습니다. 하지만 아쉽게도 맥OS용 크롬에는 이 기능이 비활성화되어 있기 때문에 Mait Vilbiks씨가 만든 createGcApp.dmg를 이용하여 바로 가기를 생성할 수 있습니다. :)

덧 2012-5-17:

맥OSX에서 탭/윈도 닫기의 단축키는 Command + W 이고 컨벤션인데 클라우드9의 "커런트 탭 클로즈" 단축키는 Option(alt) + W로 되어있어 실제 윈도를 닫아버리는 실수를 자주 합니다. 이 단축키를 변경하려면 plugins-client/ext.tabbehaviors/tabbehaviors.js 파일을 열고 35라인을 아래와 같이 수정하면 Command + W 키를 이용하여 현재 탭을 닫을 수 있습니다. 물론 다른 단축키들도 입맛에 맛게 고칠 수 있습니다.

변경 전:

        ["closetab", "Option-W", "Ctrl-W", "close the tab that is currently active", "Closing active tab.", function(){ return ide.onLine && tabEditors.activepage; }],
        ["closealltabs", "Option-Shift-W", "Ctrl-Shift-W", "close all opened tabs", "Closing all tabs.", function(){ return ide.onLine && tabEditors.activepage; }],
        ["closeallbutme", "Option-Ctrl-W", "Ctrl-Alt-W", "close all opened tabs, but the tab that is currently active", "Closing tabs.", function(){ return ide.onLine && tabEditors.length > 1 }],

변경 후:

        ["closetab", "Command-W", "Ctrl-W", "close the tab that is currently active", "Closing active tab.", function(){ return ide.onLine && tabEditors.activepage; }],
        ["closealltabs", "Command-Shift-W", "Ctrl-Shift-W", "close all opened tabs", "Closing all tabs.", function(){ return ide.onLine && tabEditors.activepage; }],
        ["closeallbutme", "Command-Ctrl-W", "Ctrl-Alt-W", "close all opened tabs, but the tab that is currently active", "Closing tabs.", function(){ return ide.onLine && tabEditors.length > 1 }],

Comments

클로즈 베타를 앞두고 있는 코드명 벨록스 프로젝트에 킬러 피쳐를 추가하는 과제를 두고 고민하다가 개발자 답게 드랍박스와 연동하여 기존 사용자들을 흡수하는 방향으로 진행하기로 했습니다. 테스터들에게는 빠른 속도와 용량 제한 없는 공간을 무료로 제공할 수 있게 되고, uCloudDaum클라우드, NDrive 등의 유사 서비스도 오픈 API가 있는 지는 모르겠습니다만 있는 족족 흡수할 생각에서 였습니다. Node.JS와 드랍박스를 연동하는 구현 사례나 관련 문서가 전무하여 며칠동안 삽질하면서 만들어야 했는데, 그 구현 과정을 예방 차원에서 기록합니다.

# npm search dropbox

NAME              DESCRIPTION
arca              little tool to download files from a public dropbox folder
dbox              NodeJS SDK for the Dropbox API
dropbox           A node.js client module for the official Dropbox API
dropbox-sdk       An alternative Dropbox SDK for nodejs
everyauth         Auth solution (password, facebook, & more) for your node.js Conn
passport-dropbox  Dropbox authentication strategy for Passport.
symbox            Simplifies symlinking folders in Dropbox

Node.JS에서 사용할 수 있는 드랍박스 클라이언트가 있나 찾아봤더니 여러개가 있더군요. 이 중에도 dropbox-node 모듈이 간결하면서도 인지도가 있어 보여 이것을 선택했습니다만, 초장부터 화근이었습니다. 드랍박스의 API는 크게 두가지로 나눌 수 있는데 모바일 디바이스를 위한 SDK와 그리고 웹앱을 위한 OAuth v1의 인증 명세를 따르는 SDK입니다. 이 둘 모두 OAuth 방식 인증을 사용합니다만, 차이점은 사용자 인증할 때 모바일용 SDK에서는 드랍박스 사용자 아이디와 암호를 직접 요구하며 웹용 SDK에서는 OAuth기반의 웹 인증(트위터와 같은)을 수행한다는 것입니다. 또한 개발하려는 애플리케이션의 용도에 따라 드랍박스 전체 파일에 접근하게 할 것인지 기존 파일에 접근하지 않고 클라우드로 사용할 것인지에 따라서도 사용법이 달라집니다.(검증필요)

벨록스는 당연히 드랍박스에 존제하는 파일에 흥미가 있고 웹용이기 때문에 웹용 SDK를 선택해야 했지만 dropbox-node 모듈은 사용자의 아이디와 암호를 요구하는 인증방식이었습니다.(헐~) 그래서 웹인증 부분을 해결하려고 passport-dropbox모듈을 설치했는데 이녀석 때문에 본격적인 삽질이 시작될 줄은 꿈에도 몰랐던 거죠...

주말에 마음을 비우고 출근해서 드랍박스의 개발자 문서를 차근차근 살피다 보니 Node.JS용 공식 서드파티 SDK는 dbox였더군요! 개삽질한 코드들은 모두 지우고 metadata를 긁어와서 폴더 단위로 파일을 정렬하는 것 까지 단숨에 해결했습니다. 개발인란게 참 허무하네요.

Comments