Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

超!体系的に学ぶWebアプリケーション開発

この記事はWebサーバーを建てるところからWebアプリケーションを開発できるようになることを目標としています。

目次

この記事の目標

この記事では、Webフレームワークに頼らずにWebサーバーを建て、Webアプリケーション開発の環境構築まで行って、Web開発の基礎を固めることを目的とします。

  • Webサーバーの動作を理解する
  • Webアプリケーションの開発の始め方を理解する
  • LaravelやRuby on RailsなどWebアプリケーションフレームワークに通じる基礎を理解する
  • WebSocketやHTTP/2など最新技術がどこで動くのか理解する

[超!基本]

Webページが表示されるまで

あなたがブラウザでhttps://www.google.comにアクセスすると、Googleのページが表示されます。このときの流れをカンタンに説明します(かなり簡略化してあるので興味があれば実際のところ「ブラウザを立ち上げてページが表示されるまで」には何が起きるのか #Web - Qiitaなどをご参照ください)。

  1. ブラウザがwww.google.comに「HTMLファイルをください」というリクエストを送る
  2. Googleのサーバーがリクエストを受け取る
  3. サーバーはHTMLファイルをブラウザへ送る
  4. ブラウザは受け取ったHTMLファイルを元にサイトを表示する

このように、WebサーバーがHTMLファイルを返してブラウザがそれを表示します。これが超基本の流れです。

Webページが表示されるまで

HTMLとは?

さて、「HTML」というものが出てきました。HTMLとは「HyperText Markup Language」の略です。

HTMLは「文章に意味をつけるもの」として生まれました。たとえば次のようにして文章に「意味」をつけます。

文章です。<b>ここは太文字です。</b><a href="https://www.google.com">Googleへのリンク</a>

このようなファイルを受け取ってブラウザが解釈した結果、つぎのようなページが表示されます

なんとなく理解できたでしょうか?

JavaScriptとは?

カンタンにいえばウェブページで動作するスクリプト言語の一つです。もっとも、サーバーサイドだとか込み入った話は色々ありますが、ここでは割愛します。

10年前(2009年頃)とは比べ物にならないほど複雑となってしまいました。今回の記事では、JavaScriptを書き始めるまでについても書いてあります。

[超!Webサーバー]

Webサーバーとは?

Webサーバーとは、「ブラウザからリクエストを受け取り」「ファイルを返す」サーバーのことです。ファイルにはHTMLや画像、動画などが含まれます。ブラックボックス的に図にすると以下のようになります。

Webサーバーの動作

今回はNginxというwebサーバーを使いいます。Nginxも基本的にはブラウザからのリクエストに対応するファイルを返す処理を行います。また、Nginxには別のWebサーバーに受け取ったリクエストを渡し、ファイルの代わりに処理結果をブラウザに返す機能もあります。このとき、Nginxが別のWebサーバーの代理として動作しており、このようなサーバーをリバースプロキシサーバーと呼びます。

Nginxが代理した「別のWebサーバー」には、LaravelやRuby on Railsなどが含まれます。また、自分で独自にWebサーバーを建てることもできます。たとえばGraphQLを捌くサーバー、受け取った画像をディープラーニングで処理を行い、処理結果を返すWebサーバーなどが考えられます。

Nginxによる代理

Webサーバーは他にも、Dockerというもので作ることもできます。Dockerは「環境の仮想化」を行うためのツールです。サーバーの環境に影響されないように仮想化した環境を作り、そこでWebサーバー(LaravelやNginx)を建てることができます。ちなみに、Dockerには他にも様々な用途があります。

(ちなみに、LaravelやRoRをそのままWebサーバーとして使うこともできます)

ファイアウォールによるコントロール

Webサーバーには自由にどんな端末からでもアクセスすることができます。これは一見いいことのようですが、攻撃される危険性があるということです。そのため、ファイアウォール(防火壁)を使ってアクセスを制限します。最低限、使うポート以外は全て閉じるなどの対策をしたほうがいいです。

ファイアウォールを設定していない場合、Webサーバーへのアクセスはすべて通ります。

ファイアウォールの位置

[超!Webアプリケーション]

さて、ここまでは「Webサーバーがどう動くか」についてお話してきました。この章では、Webサーバーのコンテンツの作り方についてお話していきます。

現在、Webアプリケーションを作る技術も多様化しています。

  • フレームワークを使わずにそのまま作る
  • LaravelなどのWebアプリケーションフレームワークを使ってWebサーバーを建てる
  • Dockerにより仮想化した環境で構築する

Laravelなどを使ったWebアプリケーションについてはたくさん記事があるのでおまかせします。しかし、他の記事を読む際に、「ブラウザからのリクエストを」「Laravelがさばく」という動作を意識するとかなり読みやすくなるでしょう。

Dockerは仮想化するだけで、実際のWebサーバーを建てる部分はココで書くようなことをするので、特筆することはありません。

React+TypeScriptを始めてみる

ここでは環境構築からHello Worldするまでを書きたいと思います。それより先は他の記事を参照ください。

Reactというのは、JavaScriptのライブラリの一つです。主に描画を担当します。TypeScriptは、JavaScriptに型情報を持たせたスーパーセットです。

1. Node.jsをインストールする

公式からダウンロードしてもいいですが、バージョンアップに対応しづらかったり、過去バージョンでコードの検証を行いたいことがあります。そのため、ここではNode.jsのバージョンマネージャーであるNodebrewを用いてインストールする方法をご紹介します。

詳しくはGitHub - hokaccha/nodebrew: Node.js version managerにある方法を参照ください。以下にも載せておきます。

$ curl -L git.io/nodebrew | perl - setup
$ echo "export PATH=\$HOME/.nodebrew/current/bin:\$PATH" >> ~/.bashrc
$ # zshの人はzshrcのようにご自身のシェルに合わせて上の行は変更してください
$ source ~/.bashrc
$ nodebrew install-binary stable
$ nodebrew use stable

ターミナルで以下のコマンドを実行して結果が出力されれば成功です(以下の出力のバージョンは執筆現在のものなので、バージョン情報が出ていればそれでOKです)。

$ node -v
v10.15.2

Nodebrewなしでインストールする方法も一応紹介します。自分のOSに合わせてhttps://nodejs.org/ja/download/からインストールしてください。

2. 作業用ディレクトリを作る

ココは解説いりませんよね。

$ mkdir ~/Documents/react-typescript
$ cd ~/Documents/react-typescript

3. npmのセットアップを行う

Node.jsのパッケージシステムを利用してReactやTypeScript、Webpackをインストールするので、まずセットアップを行います。対話式で色々聞かれますが、あまり必要ないのですべてyesで回答してセットアップします。あとから変更することもできるのでご安心ください。

$ npm init --yes

4. ReactやTypeScript、Webpackをインストールする

作業用ディレクトリ内で以下のコマンドを実行してください。

$ npm install --save-dev @babel/core @babel/preset-react babel-core babel-loader
$ npm install --save-dev react react-dom
$ npm install --save-dev typescript @types/react @types/react-dom
$ npm install --save-dev webpack webpack-cli awesome-typescript-loader

見やすいように分けましたが、一括でも問題ありません。

$ npm install --save-dev @babel/core @babel/preset-react babel-core babel-loader react react-dom typescript @types/react @types/react-dom webpack webpack-cli awesome-typescript-loader

TypeScriptで書いたファイルをWebpackでコンパイルを行います。ReactではJSXという構文が取られており、これはそのままではJavaScriptとして動かないので、Webpack内でBabelを使って変換を行っています。まあよくわからないと思いますのでふーんと聞き流してください。

正直、$ npm run buildをするといい感じのファイルが出来上がる、ということさえ知っていれば問題ありません。

webpackやTypeScriptは設定ファイルを置いてやらないとうまく動いてくれないので、以下のファイルを作業用ディレクトリの直下に配置してください。

webpack.config.js

// webpack.config.js
module.exports = {
    mode: process.env.NODE_ENV || 'development',
    entry: {
        app:  __dirname + '/src/app.tsx',
    },
    output: {
        filename: '[name].bundle.js',
        path: __dirname + '/dist'
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js', '.jsx', '.css'],
    },
    module: {
        rules: [
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader?modules&localIdentName=[name]--[local]--[hash:base64:5]'],
            },
            { test: /\.tsx?$/, use: 'awesome-typescript-loader', },
        ],
    }
};

tsconfig.json

{
    "compilerOptions": {
        "jsx": "React",
        "target": "es5",
        "lib": ["es2015", "dom"],
        "sourceMap": true,
        "typeRoots": [
            "typings",
            "node_modules/@types"
        ]
    },
    "babelCore": "@babel/core"
}

そして、"scripts"をpackage.json内に記述してください。以下のような感じです。

{
  "name": "react-typescript",
  "version": "0.0.1",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --progress",
    "watch": "webpack --watch --progress"
  },

React+TypeScriptでHello World

ここまでかなり長くなってしまいましたが、ようやくHello Worldができます…この環境は構築するまでがとても大変ですが、一度構築してしまえばあとはReactの恩恵が受けられるので開発は楽に行うことが出来ます。

src/app.tsx

// src/app.tsx
import * as React from 'react';
import * as ReactDOM from 'react-dom';

interface IProps {}

const App: React.SFC<IProps> = (props) => (
    <h1>Hello World!</h1>
);

ReactDOM.render(
    <App/>,
    document.querySelector('#root')
);

index.html

<!DOCTYPE html>
<title>Hello world</title>

<div id="root"></div>
<script src="./dist/app.bundle.js"></script>

では、$ npm run buildをしてから、ブラウザでindex.htmlを開いてみてください。「Hello World!」と表示されているはずです。

あとはFluxを学ぶなりしていってください。

[超!通信]

HTTPSとHTTPの違い

HTTPSとHTTPは「通信が暗号化されているか」が異なります。細かいセキュリティの話(改ざんや盗聴はどのようにして行われるか)はここではしません。「HTTPだと他の悪意あるクラッカーにページを見られるかもしれない」とだけ理解しておけばとりあえずはOKです。最低限、パスワードを扱うページはHTTPS化しておく必要があります。

現在はLet's Encryptという認証局により無料でHTTPS化できます。以前はかなりのお金(年間数千円〜数十万円!)を払わないといけなかったので、かなり費用的に楽になりました。

HTTP通信の流れ

  1. コネクションを確立する
  2. リクエストを送信する
  3. サーバーがリクエストを受け取る
  4. サーバーがなんらかの処理をする
  5. レスポンスを返す
  6. コネクションを切断する

リクエストごとにコネクションを建て、レスポンスがあるとコネクションを切断します。HTTP/1.1では、1つのコネクションで1つのリクエストしか捌けないので、コネクションを多数張ることで並列化していました*1

HTTP/2とは?

HTTP/2では1つのコネクションで複数のリクエストを処理できます。HTTP/1.1でも一応は可能でしたが、実装の難しさからほとんど実用化されていませんでした。HTTP/2.0ではフレームという概念を導入したことで、ストリームを使って並列処理できるようになりました。そのため、TCPコネクションが乱立していて非効率的だったHTTP/1.1と比べてだいぶマシになりました。

また、他にも改善点があります。たとえばサーバープッシュです。HTTP/1.1では、リクエストがあったファイルしか返せませんでした。しかし、HTMLを読み込んでからそのページで必要なものをリクエストするより、サーバー側があらかじめ必要になるものを送ればすぐにページを表示できます。

そして、NginxならHTTP/2にカンタンに対応できます。設定をほんの少し変えればそれでOKです。

まあ色々と言いましたが、要するにすごくなったということです。

WebSocketとは?

チャットのように、サーバーとリアルタイム通信を頻繁に行うとき、いちいちコネクションを張り直すと効率が悪くなります。一回張ったコネクションをそのまま張りっぱなしにしたほうがいいです。コレを実現するのがWebSocketです。

*1:注: 仕様上は1つのコネクションで複数のリクエストをさばけるはずでしたが、バグの多い実装ばかりだったので実際に使用されることは殆どありませんでした