Node.jsのExpress+Socket.IOを使ってチャットアプリを作る

Node.jsでチャットアプリ

今回は、Node.jsにExpressのフレームワークとSocket.IOのライブラリをインストールして「チャットアプリ」を作ってみます。

かかかず
かかかず

今回は、上記の記事を参考にさせて頂きながらローカルにチャットアプリを作っていきます。

かなり自分への備忘録的な内容が多い記事内容ですが、是非最後までお付き合いいただけたら嬉しいです。

チャットアプリで使用するExpressとSocket.IO

まずはじめに、チャットアプリを作るにあたって使用するフレームワーク「Express」と、ライブラリ「Socket.IO」について学んでおきます。

Express

Expressは、Node.jsのWebアプリケーションフレームワークです。

Express
画像:Express

具体的に「Expressって何なの??」をWikipediaで見てみると、以下の通りです。

Express.js は、サーバーサイドJavaScriptのNode.jsのWebアプリケーションフレームワークである。シングルページ/マルチページ/混在の各種Webアプリケーションの構築のためにデザインされている。

Wikipediaより
かかかず
かかかず

ちなみにWebアプリのフレームワークとしては、デファクトスタンダードみたいです。

また、利用しているユーザー数が多いからか、調べたい内容でググっていても検索で引っかかる情報量が多く、日本語での解説記事が多いのも、非常に嬉しいところです。

公式サイト Express

Socket.IO

Socket.IOは、ブラウザとサーバー間の双方向・リアルタイム通信を可能にするライブラリです。

Socket.IO
画像:Socket.IO

これも同様Wikipediaで見ると以下の通りです。

Socket.IOは、リアルタイムWebアプリケーション用のイベント駆動型ライブラリです。これにより、Webクライアントとサーバー間のリアルタイムの双方向通信が可能になります。

Wikipediaより

今回のチャットアプリでは、「リアルタイムの双方向通信」がチャットが動く時のポイントになるので、このSocket.IOが活躍してくれそうです。

公式サイト Socket.IO

全体の流れ

手順と方法

はじめに、チャットアプリを作ってローカルで動かすまでの全体の流れについておさらいしておきます。

全部で9つのSTEPで完了します。

準備

Node.jsとVS Codeのセットアップをします

ターミナルでディレクトリを作成

ターミナルからディレクトリを作成します。

Expressをインストール

Socket.IOをインストール
コードを書いてチャットアプリの作成

コードを書いていきチャットアプリを作っていきます。

ローカルサーバーを起動して確認
Socket.IOの組み込み

各ファイルにSocket.IOを組み込みます。

app.jsとindex.htmlにコードを追記

Socket.IOが処理を行えるようコードを追記します。

サーバーを起動して確認

サーバーを起動して確認します。

完成すると、同時刻にアクセスしているブラウザが連携して、リアルタイムにメッセージが表示されるチャットアプリが出来上がります。

かかかず
かかかず

早速やってみましょう。

準備

まずは、Node.jsを使用できるようローカル環境の構築と、VS Codeのインストールを行っていきます。

既にこの2つが完了している方は、この「準備」はスキップして続きをご覧ください。

Node.jsのローカル環境設定

作業で使用するPCに、Node.jsのインストールと環境構築が必要です。

以下は、Node.jsをmacのローカル環境で使う為の構築方法です。こちらの記事を参考にして、設定をしておきましょう。

Node.jsのローカル環境構築で使うターミナルのコマンドまとめ

ターミナルからVSCodeを起動可能にする為の準備

次に、VSCodeをインストールします。

以下公式サイトからダウンロードをして、お使いのデスクトップで使用できるようにしておきましょう。

公式サイト Visual Studio Code

かかかず
かかかず

VS Codeは多機能ながらも無料で使えます。

ターミナルでディレクトリを作成

はじめに、ターミナルに以下のコマンドを入力してローカルに chat_app というディレクトリを作成します。

$ mkdir chat_app
$ cd chat_app
$ npm init -y

作成したディレクトリ内で、最後の行にある npm init を実行してプロジェクトを初期化して、パッケージインストールの準備を行います。

かかかず
かかかず

コマンド入力が完了すると、package.jsonというファイルが同ディレクトリに生成されます。

任意の場所にディレクトリを作るときは cd コマンドで移動してから

任意の場所にディレクトリを作る場合は、以下のコードを入力する前に、その任意の場所に cd XXX で移動してから以下のコマンドを入力するようにしましょう。

Expressをインストール

次に、以下のコマンドをターミナルに入力してExpressをインストールします。

npm install express --save
上記のディレクトリ作成した状態コマンド入力

上記のディレクトリ作成した状態で、そのまま続けてターミナルが開いていれば問題ないですが、この時上記で作ったディレクトリ(例でいうとchat_appのディレクトリ)で実行するようにします。もしも、chat_appのディレクトリじゃない場合は、 cd コマンドで移動しておきます。

Expressのインストールが完了すると、package.jsonの中の末尾の方に、"dependencies": { "express": "^4.16.4" } と、Expressの一文が追加されます。

また、app_chat のディレクトリ内は以下のようになってると思います。

画像:chat_appのディレクトリ
かかかず
かかかず

これでExpressのインストールは完了です。続けてSocket.IOのインストールを続けましょう。

Socket.IOをインストール

次に、以下のコマンドをターミナルに入力してSocket.IOをインストールします。

npm install socket.io

インストールが完了すると、package.jsonの"dependencies" にsocket.ioが追加されているはずです。

ここまでで、ExpressとSocket.IOのインストールは完了しました。

以下から、実際にコードを書いてチャットアプリを作っていきます。

コードを書いてチャットアプリの作成

インストールが完了したので、ここからチャットアプリに必要なファイルを作ってコードを書いていきます。

chat_appのディレクトリ内にapp.jsを作成

ExpressとSocket.IOをインストールしたディレクトリ「chat_app」に「app.js」で新規ファイルを作成します。

app.js
画像:VS Codeでapp.jsを作成

「app.js」の作成が完了したら、以下のコードを同ファイルに記述します。

// Expressをrequire
const express = require('express')
const app = express()
// ポートは3000番指定
const port = 3000

// ディレクトリでindex.htmlをリク・レス
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

// ポート解放
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

chat_appのディレクトリ内にindex.htmlを作成

次に、chat_appのディレクトリに「index.html」を作成します。

index.html
画像:index.html

「index.html」ができたら、その中に以下のコードを記述して保存します。

<!DOCTYPE html>
<html>
<head>
  <title>チャットアプリをつくってみよう</title>
  <style>
    * {margin:0; padding:0; box-sizing:border-box; }
    body { font-size: 14px; max-width: 600px; width: 100%; margin: 0 auto; padding: 0 15px; }
    .chatSend { padding: 3px; position: fixed; bottom: 0; width: 100%; }
    .chatText { border: 2px solid #ccc; padding: 10px; width: 90%; margin-right: .5%; border-radius: 5px; }
    .sendButton { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; border-radius: 5px; }
    #timeline { list-style-type: none; margin: 0; padding: 0; }
    #timeline li { padding: 5px 10px; }
    #timeline li:nth-child(odd) { background: #eee; }
  </style>
</head>
<body>
  <h1>チャットアプリをつくってみよう</h1>
  <ul id="timeline"></ul>
  <form id="chatSend" action="">
    <input id="chatText" autocomplete="off"><button class="sendButton">送信</button>
  </form>
</body>
</html>

こうすることで、app.jsで記述したindex.htmlが紐付けされます。

ローカルサーバーを起動して確認

ここまで完了したら、以下のコマンドをターミナルに入力し、ローカルサーバーを起動させてきちんと表示されるか確認してみましょう。

node app

上記のコマンドでローカルサーバーを立ち上げたら、Webブラウザに localhost:3000 を入力し表示させてみます。

ローカルでの表示
画像:localhost:3000での表示

起動して上記が画面が表示されていれば、問題なくhtmlファイルが読み込まれています。

ですが、下部のテキストエリアに入力をして送信を押しても何も起きません。ので、一旦 ^C でサーバーの起動を終了して、Socket.IOを組み込んでいきましょう。

Socket.IOの組み込み

ここまでで、htmlの表示の設定が完了しました。

ここからは、app.jsのファイルにライブラリのSocket.IOを組み込んでリアルタイム通信を可能にしていきます。

app.jsの編集

まずは、「app.js」のファイルを以下のように編集して保存をします。

// Expressをrequire
var app = require('express')();
// httpモジュールをrequire
var http = require('http').Server(app);
var io = require('socket.io')(http);

// ディレクトリでindex.htmlをリク・レス
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

// Socket.IOをコネクト
io.on('connection', function(socket){
  console.log('a user connected');
});

// ポートを3000番
http.listen(3000, function(){
  console.log('listening on *:3000');
});
かかかず
かかかず

編集前のコード、特に冒頭部分の記述が大きく変わります。

index.htmlの編集

そして、index.htmlのファイルに以下のコードを追記します。

コードは <body>〜〜</body> のクロージングタグ(閉じタグ)前に記述しましょう。

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io();
</script>

記述が完了したら保存をします。

app.jsとindex.htmlにコードを追記

参考記事のように、この時点でローカルサーバーを立ち上げてブラウザで動かしてみても、入力したメッセージは表示されません。

ので、サーバー・クライアントの両方で処理を行えるよう、「app.js」と「index.html」にコードを追加します。

かかかず
かかかず

完了まで、あと一息です。

app.jsにコードを追加

app.jsの、編集前 // Socket.IOをコネクト のコメントが記載されている中に、処理内容を追加します。

編集後のコードは以下のようになります

// Expressをrequire
var app = require('express')();
// httpモジュールをrequire
var http = require('http').Server(app);
var io = require('socket.io')(http);

// ディレクトリでindex.htmlをリク・レス
app.get('/', (req, res) => {
  res.sendFile(__dirname + '/index.html');
});

// Socket.IOをコネクト
io.on('connection', function(socket){
  console.log('a user connected');
  // メッセージ処理
  socket.on('chat message', function(msg){
    io.emit('chat message', msg);
  });
});

// ポートを3000番
http.listen(3000, function(){
  console.log('listening on *:3000');
});

こうすることで、io.on処理に、io.emit('chat message', msg);を追加して、サーバー側で受け取ったメッセージを全員に発信することが可能になります。

index.htmlにコードを追加

最後に、index.htmlにサーバー側から受け取ったメッセージをブラウザ上へ表示する処理をJavaScriptで追記します。

コードを追記する場所は、<body>〜〜</body> のクロージングタグ(閉じタグ)前に記述した場所です。

編集後は、以下のコードになります。

<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"/>
  <title>Chat app</title>
  <style>
    * {margin:0; padding:0; box-sizing:border-box; }
    #chatSec {
    width: 100%;
    height: 100vh;
    display: flex;
    align-items: center;
    background: #fafafa;
    }
    .chatBlock {
    width: 100%;
    max-width: 800px;
    margin: 0 auto;
    background: #fff;
    box-shadow: 0 0 3px 0 rgb(0 0 0 / 12%), 0 2px 3px 0 rgb(0 0 0 / 22%);
    transition: 0.2s ease-in-out;
    border-radius: 3px;
    }
    /* 見出し */
    .chatBlock h1 {
    background: #6bb6ff;
    color: #FFF;
    padding: 10px 25px;
    border-radius: 3px 3px 0 0;
    }
    /* チャットの中 */
    ul#timeline {
    padding: 20px 25px;
    width: 100%;
    height: 400px;
    overflow-y: scroll;
    flex-direction: column;
    display: flex;
    gap: 15px;
    max-height: 400px;
    }
    ul#timeline li {
    list-style: none;
    display: block;
    background: #eee;
    position: relative;
    padding: 12px 15px;
    animation: fadeIn 0.7s ease 0s 1 normal;
    }
    ul#timeline li span {
    position: relative;
    line-height: 1.4;
    display: inline-block;
    }
    ul#timeline li time {
    font-size: 0.7rem;
    position: absolute;
    right: 8px;
    bottom: 5px;
    color: #707070;
    }
    /* 新規チャット */
    ul#timeline li:last-child {
    background: #e8f3ff;
    }
    /* フォーム */
    form#chatSend {
    display: flex;
    justify-content: space-between;
    padding: 20px 25px;
    background: #ecf6ff;
    border-radius: 0 0 3px 3px;
    }
    input#chatText {
    line-height: 40px;
    border: solid 1px #eee;
    background: #FFF;
    text-indent: 10px;
    font-size: 1.1rem;
    display: inline-block;
    width: calc(100% - 100px);
    }
    input#chatText:focus-visible {
    outline-color: #6bb6ff;
    }
    button#sendButton {
    width: 90px;
    border: none;
    background: #6bb6ff;
    color: #FFF;
    letter-spacing: 0.07rem;
    font-weight: 500;
    border-radius: 3px;
    font-size: 1.03rem;
    }
    /* アニメーション */
    @keyframes fadeIn {
        0% {
            opacity: 0;
            transform: translateY(30px);
        }
        100% {
            opacity: 1;
        }
    }
  </style>
</head>
<body>
<section id="chatSec">
    <div class="chatBlock">
        <h1>Chat app</h1>
        <ul id="timeline"></ul>
        <form id="chatSend" action="">
            <input id="chatText" autocomplete="off"><button id="sendButton">送信</button>
        </form>
    </div>
</section>
<script src="/socket.io/socket.io.js"></script>
<script>
    let socket = io();

    const timeline = document.getElementById("timeline");
    const form = document.getElementById("chatSend");
    const input = document.getElementById("chatText");
    const messages = document.getElementById("sendButton");

    form.addEventListener("submit", function (e) {
      e.preventDefault();
      if (input.value) {
        socket.emit("chat message", input.value);
        input.value = "";
      }
    });

    socket.on("chat message", function (msg) {
      // li タグを生成
      let item = document.createElement("li");
      // 現在時刻を取得
      let posttime = new Date().toLocaleString(); 
      // メッセージと現在時刻をHTMLに挿入
      item.innerHTML = '<span>' + msg + '</span><time>' + posttime + '</time>';
      
      // timelineにliタグを挿入してスクロールも追従
      timeline.appendChild(item);
      timeline.scrollTo(0, timeline.scrollHeight);
    });
  </script>
</body>
</html>
かかかず
かかかず

CSSも追記したのでコード量も増えてます。

io.emitから発信された情報をsocket.onで受け取って、ブラウザで表示する処理を行っています。

サーバーを起動して確認

最後に、以下のコマンドでサーバーを再起動して、ブラウザで実際にチャットを動かしてみましょう。

node app

以下のような表示と動作が確認できたら完成です。

動画だと少し小さいですが、各メッセージと投稿時刻が下部に追加で表示されていきます。

かかかず
かかかず

メッセージがブラウザに表示されたらOKです!

さいごに

今回、ExpressとSocket.IOで、めちゃめちゃ簡単にチャットアプリを作ることができました。

特にExpressに関しては、Webアプリを作っていく中で頻出しそうなので、より深く学んでいきたいと思います。

参考サイト

参考Node.js入門⑧ Expressフレームワークを使おうプログラミング学習 きくちゃんの勉強部屋
カテゴリーのイラスト

同じカテゴリの記事一覧

ボタンの影