無限の猿定理をWebアプリ化した

#はじめに

皆さんは『無限の猿定理』というものをご存知だろうか。
>無限の猿定理（むげんのさるていり、英語: infinite monkey theorem）とは、十分長い時間をかけてランダムに文字列を作り続ければ、どんな文字列もほとんど確実にできあがるという定理である。比喩的に「猿がタイプライターの鍵盤をいつまでもランダムに叩きつづければ、ウィリアム・シェイクスピアの作品を打ち出す」などと表現されるため、この名がある。

[Wikipedia](https://ja.wikipedia.org/wiki/%E7%84%A1%E9%99%90%E3%81%AE%E7%8C%BF%E5%AE%9A%E7%90%86)より

要するに”無限”というものの凄さの例えである。
この定理に着想を得て、以前「[頭文字を打つだけで文章ができていくアプリケーション](https://qiita.com/ck_qiita/items/76fc79e0323a7485a966)」というものを作成した。
このプログラムはコンソール上だけでの動作だったので，Webアプリに移植して，公開することにした。
URLはこちら。

**[https://infinite-monkey-theorem.herokuapp.com/](https://infinite-monkey-theorem.herokuapp.com/)**

本記事では、公開までに躓いたポイントの備忘録である。

<dl>
  <dt>環境</dt>
  <dd>Windows10</dd>
  <dd>VSCode</dd>
  <dt>構成</dt>
  <dd>Java11</dd>
  <dd>Spring-Boot</dd>
  <dd>PostgreSQL</dd>
  <dd>Heroku</dd>
</dl>


#ローカル開発環境の準備

####VSCodeに必要な拡張機能をインストールする

- Java Extension Pack
- Spring Boot Extension Pack
- Lombok Annotations Support for VS Code

####アプリの雛型を自動生成する

Ctrl+Shift+Pでコマンドパレットを開く
"Spring.Initializr: Create a Maven Project..."を選択
Spring Boot Version 2.5.3を選択
Project Language Javaを選択
Group Idを入力，今回はworks.izumi
Artifact Id を入力，今回はinfinite_monkey_theorem
packaging type Jarを入力
Java Version 11を選択
dependenciesは以下を選択

- Spring Boot Dev Tools
- Lombok
- Spring Web
- Thymeleaf
- Spring Data JPA
- PostgreSQL Driver

プロジェクトを格納するフォルダを選択して終了。

####初期動作確認
動作確認のため以下のファイルを追加。

:::note info
【躓きポイント】利用するDBを指定しないと実行に失敗するので注意
:::

SPRING BOOT DASHBOARDから実行し，localhost:8080にアクセス。
『hello』と表示されたら，成功。


```java:demo\src\main\java\com\example\demo\controller\AppController.java
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AppController {
    
    @ResponseBody
    @RequestMapping("/")
    public String init(){
        return "hello";
    }

}
```

```properties:demo\src\main\resources\application.properties
# 接続情報
spring.datasource.url=jdbc:postgresql://localhost:5432/[データベース名]
spring.datasource.username=[ユーザ名]
spring.datasource.password=[パスワード]
```

####DB作成と接続テスト
pgAdminでテーブルを作成。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521715/0a4dc832-8953-d9f7-a81a-ee7773a22efb.png)


さらに，Entity，Repository，serviceを追加し，controllerも追記．

```java:AppEntity.java
package com.example.demo.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import lombok.Data;

@Entity
@Data
@Table(name="test")
public class AppEntity implements Serializable{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int nounId;

    @Column(name="initial")
    private String initial;
    
    @Column(name="notation")
    private String notation;

}

```

```java:AppRepository.java
package com.example.demo.repo;

import java.util.List;

import com.example.demo.model.AppEntity;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface NounRepository extends JpaRepository<AppEntity, Integer>{

    public List<AppEntity> findByInitial(String initial); 
    
}

```

```java:AppService.java
package com.example.demo.service;

import java.util.List;

import com.example.demo.model.AppEntity;

import org.springframework.stereotype.Service;

@Service
public interface AppService {
    
    public List<AppEntity> test();

}

```

```java:AppServiceImpl.java
package com.example.demo.service.impl;

import java.util.List;

import com.example.demo.model.AppEntity;
import com.example.demo.repo.NounRepository;
import com.example.demo.service.AppService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AppServiceImpl implements AppService {
    
    @Autowired
    NounRepository repo;

    @Override
    public List<AppEntity> test(){

        return repo.findAll();

    }

}

```

```java:AppController.java
package com.example.demo.controller;

import com.example.demo.service.AppService;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AppController {
    
    @Autowired
    AppService service;

    @ResponseBody
    @RequestMapping("/")
    public String init(){
        return "hello";
    }

    @ResponseBody
    @RequestMapping("test")
    public String test(){
        return service.test().toString();
    }

}

```

localhost:8080/testにアクセスし，テーブルの中身が表示されれば成功。

#Herokuへデプロイ
こちらの記事が大変参考になる。
[とりあえずHeroku + Spring Boot デプロイ](https://qiita.com/h2m_kinoko/items/e40014cc76318433ef1a)

:::note info
【躓きポイント】Procfileとsystem.propertiesをきちんと追加・設定しよう
:::

これでおそらくデプロイは成功する。
しかしHerokuのURLにアクセスしようとすると、おそらく『Application error』になる。
その場合、Procfileで設定したjarファイルが生成されていない可能性が高い。
こちらの記事が大変参考になる。

[Herokuにデプロイしたアプリの更新で「Application error」](https://qiita.com/hiroki1994/items/325fdf7e5943a563867f)

:::note info
【躓きポイント】Mavenビルドとjarファイルをgit addしよう
:::

#Sleep対策
Herokuの無料枠では30分アクセスがないとアプリケーションがスリープしてしまう。
スリープ中にアクセスするとSpring Bootの起動から始まるので表示にとても時間がかかる。
そこでNew Relicアドオンを用いて15分毎にアクセスを行うことで、スリープを防止した。
こちらの記事が大変参考になる。

>[Heroku Free DynoのSleepを防止する](https://unhurried.hatenablog.com/entry/heroku_prevent_sleep)

#その他忘れがちなこと
- テストクラスに@SpringBootTestをつける
- Ajax(jQuery)の雛型：
contentType: "application/json" とすることで、サーバ側@RequestBodyでうまく受け取れるようになる

```javascript:AjaxSample.js
$.ajax({
    type: "POST",
    url: "/hoge",
    contentType: "application/json",
    data: val,
})
.done(function (data) {
    // 通信成功時
    alert("通信成功");
})
.fail(function (data) {
    // 通信失敗時の処理
    alert("通信失敗");
})
.always(function (data) {
    // 常に実行する処理
    console.log(data);
});

```

#おわりに
お盆休みの暇な時間を有効に使おうと思い、セルフサマーキャンプ的なノリで始めた本開発。
実装の中身は既にあったので、今回はクラウドサーバへのアップロードとUIをメインの課題として取り組んだ。
飽き性なのでこれくらいコンパクトに絞って本当に良かった。
今度はツール的なものの開発に取り組んでみようかな。