概要

前回のつづき。

 

Node.js って何? に答えるシリーズ

このシリーズは "ざっくり読み感想文" 3分の2までに終えているぜ。

 

Node.js で Web アプリ作るってなったとき知っていたらよさげなコトたち

サーバサイドでもクライアントサイドでも同じ言語を使えるってヤバくね?

  • 言われてみればヤバくね? 複数の言語を勉強する必要がないというのもあるし、以下の条件を満たすと "ユニバーサル Web アプリケーション" を実現できる。
    • クライアントサイドが Single Page Application (以下 SPA) であり、
    • クライアントサイドが HTML のレンダリングをする Client Side Rendering (CSR) …… ではなく、
    • サーバサイドがレンダリングをする Server Side Rendering (SSR) であり、
    • そのレンダリングの結果を Static Site Generation (以下 SSG) の仕組みを使ってキャッシュしておき、
    • かといってクライアントサイドの仕事がなくなるわけではなくてこちら側では HTML と JavaScript オブジェクトを紐付ける処理を行う (ハイドレーションという)、
  • ……という条件を満たすことで "ユニバーサル Web アプリケーション" となる。は……? なんか超大変そう。
  • なんか超大変そうだが、ちゃんとフレームワークがある。 React.js でユニバを作るなら Next.js を使えばいいし、 Vue.js でユニバを作るなら Nuxt.js を使おう。
  • 大袈裟杉内? と思ったときは http モジュールとか Express モジュール? フレームワーク? を使うこと。
  • http は低レベルなので普通は Express を使いましょう。こんな感じ↓
'use strict'
const express = require('express');
const router = express.Router();
const app = express();

// Express の機能を拡張するためにはミドルウェアを使う。
// 静的ファイルの配信は static ミドルウェア関数を使う。
app.use(express.static('public'));

// リクエストボディをパースするためのミドルウェア。
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Cookie の取得にはこのミドルウェア。
app.use(require('cookie-parser'));

function expressMiddleware (req, res, next) {
    // ... (ミドルウェアの処理)
    next(); // 後続のミドルウェアに処理を受け渡す場合はこれが必要。
}

// こんなふうに使う。
app.get(
    '/api/todos',
    expressMiddleware,  // <- これは汎用的なミドルウェアと呼ぶ。
    (req, res) => {  // <- これも実はミドルウェア。だけど汎用的ではない。ルートハンドラという。
        // ...
    }
);
  • ああ、 Django ではやたらと「この middleware はこの middleware の後に定義……」とか決まりがある。それは、 middleware は next によって次の middleware へ処理を委譲していくものだから、だったのか。

 

時代はリアルタイム Web じゃね?

  • だって、ウェブページを開いたまま10分経過したら、その情報はもう古くなってるかもしれないじゃん? 新しい情報が投稿されたら、それをすぐにウェブページに表示してほしいよね?
  • それを実装するためのもっともシンプルな手段がポーリング。定期的にサーバにリクエストする。いやいや……こんなの、あるか分からない振り込みを確認するためにいちいちコンビニの ATM に行くアホな人じゃん?
  • ずっと http 接続を保持したままにしておき、データ更新があったときサーバがクライアントへデータを送信するのが Server Sent Events (SSE) だ。えっ、それいいじゃん。
    • といっても長時間送信がないと接続がタイムアウトしちまうので、定期的にコメントだけのメッセージを送ってタイムアウトを防ぐ。
  • サーバからクライアントへの一方通行だけでなくて、クライアントからサーバへのメッセージ送信も行いたいなら WebSocket を使う。 http でハンドシェイクしてから、 WebSocket プロトコルで通信するものだ。ただ、プロキシやファイアウォール、アンチウィルスソフトウェアによって通信が妨害されることもあるので、サーバからクライアントへの一方向の通信で事足りるなら SSE 使っとけ。
    • WebSocket 使うならサーバ側で socket.io モジュールを使い、クライアント側で socket.io-client モジュールを使う。
    • こちらがサーバ側コードの例だ↓
'use strict'
const http = require('http');
const Server = require('socket.io');
const server = http.createServer((req, res) => {}).listen(3000);
const io = Server(server, { origins: allowed.origin.com });

io.on('connection', socket => {
    // socket を roomA に入れる。
    socket.join('roomA');
    // roomA に存在する socket にデータを送信
    io.to('roomA').emit('someEvent', 'foo');
    // socket を roomA から出す
    socket.leave('roomA');
});

 

データストレージの設計はこれでヨシじゃね?

  • データストレージに対する操作を設計するときは "開放/閉鎖原則" に沿ってやること。
    • ひとつ、 "拡張に対して開いていること"。別のデータストレージに切り替えやすいということ。
    • ひとつ、 "修正に対して閉じていること"。別のデータストレージを切り替えるとき、メインのコードを修正する必要がないこと。
  • データベースにはこんなものがある……
    • リレーショナル・データベース (RDB)。行と列で構成されるテーブルに保存され、 SQL によって作成、取得、更新、削除される。
    • NoSQL。 Not only SQL の略で、ようは RDB じゃないデータベースということだ。たとえば Key-Value Store とか、 XML とか JSON で保存するドキュメントストアとか、ワイドカラムストアとか、グラフデータベースとかがある。

 

2021年ともなれば Docker 使うのがキホンじゃね?

  • Node.js の npm プロジェクトを Docker で環境構築するときは、こんな Dockerfile を用意するぞ。
FROM node:14-alpine

ARG NODE_ENV=development
ENV NODE_ENV=${NODE_ENV}

WORKDIR /usr/src/app

# package.json, package-lock.json に変更があるときのみ npm ci をやり直します。
# 変更がないなら Docker のキャッシュが使われるのでビルドが高速になる!
# 疑問: docker-compose up でもそうなの? build だけ?
COPY package*.json ./
RUN npm ci

# たとえばこのあとに npm ci しちゃうと、つねに npm ci が実行されて時間がかかる。
COPY . .

EXPOSE 3000
CMD [ "npm", "start" ]
  • docker-compose はこう用意するぞ。
version: '3'
services:
    web:
        build: .
        volumes:
            - .:/usr/src/app
            - /usr/src/app/node_modules/
        ports:
            - 3000:3000
        command: npm run dev
  • .dockerignore はこう用意するぞ。
node_modules
  • 起動コマンドはこうだ。
docker-compose up
docker-compose down

# 本番環境向けのイメージをビルド
docker build -t node-hello-world --build-arg NODE_ENV=production .
docker run -p 3000:3000 -d node-hello-world

 

ざっくり読了

というわけで500ページ級の読書、終了だ。楽しめる読書だったぜ。11章からなる大物を3記事に整理するのは骨が折れたが、11章あるからといって11記事書いていたら、ただの書き写しだものな。これまでの技術書読書では、章ごとに書いたり、1記事にまとめたりしてきた。その経験から、数記事にまとめるがもっとも性に合っていると感じた。

ちなみに本書 "今村謙士 『ハンズオン Node.js』 2020年11月13日 初版第1刷発行" には誤植がいくつかあるぞ。

  • 36ページ: "これららにはそれぞれ、そのモジュールのファイル名"
  • 345ページ: "(Docker Hub等" 閉じカッコがない。
  • 350ページ: "Dokcer"