Webアプリケーションの非同期通信について

# 1.はじめに
みなさんは、非同期通信という言葉を聞いたことはありますか？私は、学校や会社の研修で習いましたが、未だに実装の仕方やそれを実装する意味の理解が染みついていないような気がして、危機感を覚えました。そこで、今回は、Java（サーブレット/Jsp）のWebアプリケーションを題材に、Ajaxによる非同期通信について学習してきたので、それについてアウトプットしていきたいと思います。

# 2.非同期通信とは
非同期通信とは、通信の完了を待たずに他の処理を同時に進められる通信方式となります。
非同期通信には、

- 通信（サーバーへのリクエスト）をしても、待ち時間中に画面が固まらない。
- ページの一部だけ更新したり、裏でデータを取得したりできる。

といった特性があります。
ここまでで、まだイメージをつかめていないと思うので、具体的な事例を元に、非同期通信を行った場合と、行わなかった場合の両方を説明します。

非同期通信を行わなかった場合の説明が必要な理由としては、非同期通信を行った場合が、みなさんの日常にとって当たり前すぎるため、行わなかった場合どれだけ不便になるかを理解していただくことで、非同期通信というものを理解して頂けたらという狙いがあります。

### 具体的な事例 （非同期通信を行った場合）
- 商品一覧をスクロールしていくと、自動で次のページの商品が追加される（無限スクロール）。
- 「いいね」や「お気に入り」の登録・解除が、画面を変えずにできる。
- 検索欄に文字を入力するとすぐに候補が出てくる。（サジェスト）
- 通知や更新情報がリアルタイムかつ自動で届く。

### 具体的な事例 （非同期通信を行わなかった場合）
- 無限スクロールは実現できないため、「次へ」ボタンを押してページを再読み込み。
- 「いいね」や「お気に入り」の登録・解除のたびに、ページ全体を再読み込みするので、スムーズではない。
- もちろん候補が表示されないため、検索ボタンを押して結果を得なければいけない。
- 手動でページを再読み込みしないと通知が届かないため、最新情報に気付けない。

このように非同期通信は、我々の生活の利便性を向上させるための重要な技術となっております。では、これから非同期通信を実現するための実装方法を紹介していきます。

# 3.サーブレットで受け取ってレスポンスを返す仕組み
非同期通信を行うためには、JavaScriptでAjaxという技術を使って実装するのですが、その前に、MVCのCのコントローラに当たるサーブレットで、どのような値を受け取って、どのような値を返してあげればいいかを紹介していきます。また、GET通信とPOST通信では、値の受け取り方が違うので、そちらも別々で紹介していきます。

### リクエストパラメータの受け取り方法（GET通信）
見た目上、特段、通常のリクエストパラメータの受け取り方と変わりません。getParameterメソッドに指定する引数には、URLのクエリパラメータ名を指定するのですが、そのURLは後述するAjaxで記述します。
```java
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        
    //URLのクエリパラメータから取得
    String username = request.getParameter("username");
}
```

### リクエストパラメータの受け取り方法（POST通信）
POST通信の場合は、基本的にJSONデータが送られてきます。よって、JSONデータを受け取るわけですが、その受け取り方も普通の通信とは異なります。POST通信の場合は、リクエストパラメータはリクエストのボディ部分にあるため、request.getReader()と記述してJSON形式の文字列を取り出します。あとは、その文字列をJSONオブジェクトに変換して処理を行っていきます。

```java
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    //JSONを読み取る（BufferedReaderを使う）
    BufferedReader reader = request.getReader();
    StringBuilder sb = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
        sb.append(line);
    }

    String jsonData = sb.toString();
    Gson gson = new Gson();
    JsonObject jsonObject = gson.fromJson(jsonData, JsonObject.class);
    String username = jsonObject.get("username").getAsString();
}
```

### リクエストパラメータの送信方法
送信方法については、GETもPOSTも変わりません。レスポンスオブジェクトに、JSON形式であることや文字コード等、必要な設定をしてから、Ajaxに値を返します。また、値の送信部分で、JSON形式の文字列を指定していることにお気づきでしょうか？これは、Ajaxに値を返す時は、構造化された複数のデータを返すことが多いためです。
```java
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        
    //リクエストパラメータの取得とその他処理

    //レスポンスの設定
    response.setContentType("application/json");     // JSON形式で返す
    response.setCharacterEncoding("UTF-8");

    PrintWriter out = response.getWriter(); // ← 出力先を取得
    out.print("{\"message\": \"こんにちは\"}"); // ← 値を送る
    out.flush(); // （オプション：即時送信）
}
```

# 4.JavaScriptでAjax通信を実装する
いよいよJavaScriptでのAjax実装について紹介していきます。こちらもGET通信とPOST通信で記述の仕方が違うので、それぞれ紹介していきます。なお、GET通信でもPOST通信でもfetchというAPIを使用するのが一般的です。
### GET通信の場合
GET通信では、リクエストパラメータはURLに付加します。URLの部分を詳しく見ていきましょう。「/user-check」の部分は、サーブレットの位置情報を表しています。「?」から右は、リクエストパラメータを表しており、「username」がキーで、「tanaka」が実際の値となっています。URLはこのように設定していきます。そして、「.then(response => response.json())」の部分で送られてきたJSONデータを取り出して、「.then(data => {・・・」の部分で受け取ったJSONデータを使って処理を行っています。
```javascript
fetch('/user-check?username=tanaka')
  .then(response => response.json())
  .then(data => {
    console.log("存在確認:", data.exists);
});
```

### POST通信の場合
POST通信の場合は、リクエストパラメータをボディ部分に含めます。記述するときは、「body:・・・」と続く場所にリクエストパラメータを記述します。 また、GET通信と違って、通信メソッドの種類がPOST通信であることを明記しなければなりません。POST通信では、構造化データをリクエストパラメータに持つことが多いので、「 headers: {・・・」で、リクエストパラメータがJSON形式であることを明記します。
```javascript
fetch('/api/user-check', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ username: 'tanaka' })
});
```
# 5.まとめ
今回は、非同期通信の概要とその実装方法を紹介しました。非同期通信は、ユーザにとっては、「それって当たり前じゃない？」と思われる技術ですが、習得するには、とても時間がかかりますし、コードの書き方以外にも、通信メソッド等のWeb技術も学習が必要なため、楽しく学んでいくことをおすすめします。