スプーキーズのちょっとTech。

SPOOKIES社内のより技工的な、専門的なブログページです。

Node.jsについて(紹介・導入編)

f:id:spookies_kuriyama:20160415094819p:plain

前置き(Node.jsの前に)

プログラミング言語について

エンジニアの皆さんは、どのプログラミング言語をお使いでしょうか?

よく挙げられるのがNativeコードを吐き出すCPP、Objective-C、バイナリコンパイル型のC#、Java、 LLのPython、PHP、Ruby、Perlあたりだと思います。 最近だと、SwiftやGo lang等も候補にあがりそうです。

それでは、JavaScriptはどうでしょうか?
がちがちのサーバサイドアプリケーションエンジニアや、 インフラ・ネットワーク・データベース・セキュリティ専業エンジニア以外のアプリケーションエンジニアの大多数の方は、この言語を使って、コードを書いた事があると思います。

「JavaScriptはプログラミング言語じゃないよ」という方もいらっしゃるかもしれませんが、 私は、JavaScriptは立派なプログラミング言語であり、非常に汎用的、かつ将来性のある言語だと思いますし、 一番好きなプログラミング言語でもあります。

最近では、JavaScriptを中心に組むWeb Front-end Engineerという職種もあり、 もりもりJavaScriptを駆使して、Webのフロント側を作っている専業の方もいる程です。

JavaScriptについて

元々のJavaScript

そもそもJavaScriptは、かなり昔にNetscape社がクライアントのWebブラウザ内で、 決められた動的な制御を実行するために、策定された言語です。

当初はクライアントマシンも非力で、実行エンジンも良いものではなく、 APIも少なく、使いにくかったので、かなり限定的な使い方しか出来ませんでした。

しかし、ブラウザ内で動くというのはかなり画期的で、Netscapeの動きを見て、 ライバルのMicrosoftがInternet Explorer(IE)に慌てて同様の機能を盛り込んでリリースしました。
※ 余談ですが、IEに盛り込んだJavaScriptは、正式名称はJavaScriptという名称ではなくJScriptという、JavaScriptの方言の1つです

しかし、IEのJScriptが独自拡張や、互換性のない記述をしていたり、 言語仕様が一致しない等、利用する上で、かなりの苦労がありました。

Mozilla主導による言語仕様の統一化の動き

そのようなカオスなJavaSciprtの世界に統一化の動きを見せたのが、Firefoxの開発元Mozillaです。 Mozillaは仕様化されていなかったJavaScriptの統一化を目指し、 JavaScriptの統一仕様として、GoogleやAdobe等とともにECMAScriptを主導していきました。

今日提供されているブラウザでは、ECMAScriptに準拠して作られており、 JavaScriptの記述方法やAPIにもかなりの互換性があります。
※採用しているECMAScriptのVersionの違いはあります

また、クライアントマシンのマシンパワー向上、JavaScript実行エンジンの最適化、 AJAXによる、非同期通信を用いた動的なページの更新も、JavaSciprtの地位を押し上げました。

Node.jsへの流れ

このようにJavaScriptは使いやすくなり、非常に汎用的、将来性のある言語となりました。

汎用的や将来性につきましては、別に機会がありましたら記載しようと思いますが、 今回は、クライアントサイドで動くJavaScriptではなく サーバサイドあるいは、スタンドアローンアプリとしても動くNode.jsについて、ご紹介しようと思います。

Node.js

Node.jsとは何か

https://nodejs.org/en/

Node.jsを一言で言うと、JavaSciprt言語を駆使してコードを動作させるスクリプティングエンジン環境です。要は、LLと非常に似ています。というよりも、ほぼLLです。
Node.jsならではのメリットを挙げますと、

  • 記述言語が、JavaScriptなので、Web Front-end EngineerやDesignerの人も修得が用意
  • 既存のJavaScriptが流用可能
    • 但し、条件ありで、元々の記述次第で、流用度がかなり変化します
    • デフォルトのAPIがかなり豊富
      http://nodejs.jp/nodejs.org_ja/api/
      ※ 暗号化(Crypto)、ネットワーク(Net)、圧縮伸長(Zlib)等、他のLLではオプションや拡張扱いのものもデフォルトで利用可能
      ※ Web APIの待ち受けも、Apache・IIS・nginx等のWebサーバなしで、簡単に作成可能です
  • サーバサイドでも動かせるが、クライアントサイドで、スタンドアローンアプリやスクリプトツールとしても動かせる
  • 公式純正のパッケージマネージャーがデフォルトで提供(Node Package Manager[npm])されていて、かなり高機能かつ使いやすい
    • 他のLLには公式純正かつデフォルトで付いてくるパッケージマネージャーは存在しない
      (デファクトスタンダードならある。Python pip、PHP Composer、Ruby Rubygems、Perl CPAN)
  • 豊富なライブラリやフレームワークがある
    • Electron(旧ATOM Shell)、nw.js(旧Node-Webkit)、Express、Meteor、moment.js(これはNode.js専用という訳ではない)、node-fs-extra(スタンドアローンアプリ作成に非常に使える)等、他多数

ライブラリやフレームワークについては、機会があれば、またご紹介したいと思います。

Node.jsダウンロード

以下の公式サイトにアクセスして下さい。

Download | Node.js

https://nodejs.org/en/download/stable/

Windows・Macなら公式バイナリやインストールファイルを落として、実行するだけで簡単に動作可能です。
Linuxのバイナリは、最近のディストリビューションなら、大抵展開して、パスを通せば実行可能です。
ソースコードも提供されていますので、Mac以外のUnix環境、FreeBSDやNetBSD等でも、ビルドできます。
※ 公式からのダウンロード以外にも、Homebrew、yum、aptget、Nuget経由でのインストールする方法もあります

Node.jsインストール

前提

ここからは、Mac OS X環境前提で説明していきます。

インストール

上記でダウンロードしたMac用のインストールファイルをクリックして、インストール

https://nodejs.org/en/download/stable/

Mac OS X Installer (.pkg) 64-bit
node-v5.10.1.pkg

長期運用に使う場合は、LTS版がおすすめですが、大抵は、最新のStable版で良いと思います。今回も最新のStable版 v5.10.1で試します。

後は、以下の順に画面を進めていけば導入可能です。

f:id:spookies_kuriyama:20160413095710p:plainf:id:spookies_kuriyama:20160413095715p:plainf:id:spookies_kuriyama:20160413095720p:plainf:id:spookies_kuriyama:20160413095724p:plainf:id:spookies_kuriyama:20160413095728p:plain

最近のバージョンは、インストール時に以下の場所にPATHを通してくれるので、そのままターミナルで実行できます。

Node.js was installed at

   /usr/local/bin/node

npm was installed at

   /usr/local/bin/npm

Make sure that /usr/local/bin is in your $PATH.

ターミナルで、以下コマンドを入力すると、バージョン情報が表示されると正しく導入されています。  

MacBook-Pro:~ spookies$ node -v
v5.10.1
MacBook-Pro:~ spookies$ npm -v
3.8.3

次に、http簡易サーバを作ってみます。

MacBook-Pro:~ spookies$ mkdir -p src/nodejs/
MacBook-Pro:~ spookies$ cd src/nodejs
MacBook-Pro:~ spookies$ vim http.js
MacBook-Pro:~ spookies$ cat http.js

const http = require('http’);

const hostname = '127.0.0.1’;
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hoge Nyanko\n’);
  console.log('Client request!');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

MacBook-Pro:~ spookies$ node http.js 
Server running at http://127.0.0.1:3000/

最後のコマンドで、作成したソースをNode.jsで実行すると、
「Server running at http://127.0.0.1:3000/」と表示されます。
これは、127.0.0.1のホスト名と、ポート3000番でサーバを待ち受けており(待機状態で)、
クライアント側(ブラウザ)からアクセスすると、server側の処理が実行されます。

f:id:spookies_kuriyama:20160413095732p:plain

クライアント側(ブラウザ側)には、 200番ステータス(リクエスト成功)+テキストのコンテンツ属性+「Hoge Nyanko」 という文字列が返されます。

また、今回は、ターミナルから、直に実行しましたが、Service化やDaemon化、 nginx等のWebサーバと連携し、 ポートやServer側での処理を駆使すると、同時に幾つものリクエストを捌く事も出来、
Node.jsのサーバから、Node.jsのサーバも初期で提供されているAPIでリクエスト・レスポンスが可能ですので、 まさしく名前の通り、ノードとして振る舞わせる事も出来ます。

サンプルソース2(スタンドアローンスクリプト)

上記では、サーバサイドの例を記載しましたが、次はターミナルでコマンドを実行すると、 クライアント内で、特定の処理を実行するコードを例としてあげてみます。

以下は、コマンドライン引数で、バックアップ元のディレクトリ・バックアップ先のディレクトリ・ファイル名の3つを指定して、zip圧縮する例です。

サードパーティ製のモジュールとして、zip圧縮するモジュールもありますが、 ここでは、簡略化の為、MacのOSコマンドzipで代用します。

ソースコードは以下となります。

$ cat backup.js

const fs = require('fs');
const path = require('path');

const process = require('process')
const child_process = require('child_process');

function isDirectory(pathName) {
    // ファイル存在チェック
    var isExist = fs.existsSync(pathName);
    if(isExist) {
        // ディレクトリかどうか
        var isDir = fs.statSync(pathName).isDirectory();
        if(isDir) {
            return true;
        } else {
            console.log(pathName + ' is not directory');
            return false;
        }
    } else {
        console.log(pathName +' is not directory or file.');
        return false;
    }
}

// from -> to/zipにzip圧縮する
function backup(from, to, zip){
    var fromDir = path.dirname(from);
    var fromBase= path.basename(from);
    console.log('cd ' + fromDir);
    process.chdir(fromDir);
    // MacでのOSコマンドzipのコマンド文を生成(多分、最近のLinuxでも使える)
    var exeCom = 'zip -r ' + to + '/' + zip + ' ' + fromBase;
    console.log('[Exec] ' + exeCom);
    var result = require('child_process').exec(exeCom, function(err, stdout, stderr) {
        if (!err) {
            console.log(stdout);
        } else {
            console.log(err);
        }
    });
    return true;
}

// 引数チェック
if (process.argv.length != 5) {
    console.log('Usage: ' + process.argv[0] + ' ' + process.argv[1] + ' from_directory_name to_directory_name zip_file_name');
    console.log('For example: node ' + process.argv[1] + ' ~/dat/from =/dat/to backup.zip');
    return;
}

// 引数の内容を受け取る
var from = process.argv[2];
var to = process.argv[3];
var zipName = process.argv[4];

if(isDirectory(from) && isDirectory(to)){
    backup(from, to, zipName);
} else {
    console.log('Error!');
}

実行は、以下のコマンドで実行して下さい。

MacBook-Pro:~ spookies$ node backup.js from_directory_name to_directory_name zip_file_name

実行すると、「from_directory_name」ディレクトリをzip圧縮し、 「to_directory_name」ディレクトリに「zip_file_name」の名称でzip圧縮します。

まとめ

このようにNode.jsは、今後も将来性があり、サーバサイド・クライアントサイド問わずプログラムが作れますので、 皆様も是非活用してみてください。
Let's enjoy Node.js!