Webサーバーの立ち上げからアプリケーションの作成まで全て一人でやってみた件

#初めに
　今まで書籍でLaravelについて勉強したり、[Laravelの仕組み（バインドについてなど）を調べて記事](https://qiita.com/SanQ/items/a08f200614718315a63b)を書くことなどをしてきたのですが、最近それらが飽きてきてしまったのでLaravelを使用したWebアプリケーションをAWSのツールを用いて作成してみました。そして、このアプリを作成した経験を記事にすれば自分と同じような境遇の人の助けとなったり、このアプリケーションの宣伝になるかもと思ったので、このアプリケーションの作成やAWSのツールを使用した際に個人的に苦労したこと・気を付けたことをここへ書いてみました。
　この記事は大きく分けて四章あるのですが、章ごとの内容は独立しているので気になるところを好きなように読んでください。

#Webアプリ宣伝編
　今回私が作成したWebアプリケーションはデンデンデン・・・・バーン！！
「[ロッテンポテト](https://talk-cinema.net/)」です！（トマト？何のことですか？）
このアプリは映画レビューサイトです。もっと詳しく言うと、管理する人（僕です）が最近見た映画を紹介するので、それに対しレビュワーたちが気ままにレビューをする感じです。
　このサイトのトップページはこんな感じです。
![ロッテンポテト　トップ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478883/d48a4c9c-d726-d7eb-3db8-e41d2428851d.png)
ほかのページはこんな感じです。
![ロッテンポテト　映画一覧.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478883/57306f0a-6318-8564-9402-3356dfa44401.png)


　画像を見てみると新規登録とか、満足度とかありますが気になる方はトップページを見てみて下さい。また、このWebアプリに関する疑問やこんな映画をみんなとレビューしてみたいな、などの要望がありましたら、このアプリ用のtwitterアカウントへDMを送ってください！
twitterアカウントはこちら
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【映画レビューサイト解説しました！！】<br>本日より映画レビューサイト「ロッテンポテト」を開設しました！<br>本サイトは管理する人が最近見た映画を紹介し、その映画に対してレビューを書くものとなっています。<br>暇つぶしがてらぜひ遊びに来てください～<br>（質問等はDMでお願いします。）</p>&mdash; ロッテンポテト (@RottemPotato) <a href="https://twitter.com/RottemPotato/status/1280484817222529026?ref_src=twsrc%5Etfw">July 7, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>



#AWS編
※インフラ、ネットワーク、Webサーバーに関する知識が全くない中で自分なりに調べてこの章を作成しました。なので間違いがあるかもしれません。もし間違い等がありましたら、指摘していただけるとありがたいです。

　今回自分が使用したAWSのツールの関係を図にするとこんな感じです。
![AWS.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478883/6e04f84f-2c5f-f47e-9fa4-0506edcd5339.png)

　この図に書いてある丸付きの番号は一般ユーザーがWebサーバー（今回はEC2）へリクエストを送るまでの道順を表しています。この番号通りにそれぞれのツールのやっていることを説明すると、

1. 一般ユーザーがブラウザなどからドメインを通してWebサーバーへアクセスする際まずはユーザーからAWSのDNSサーバーであるRoute53へ、ドメインに紐づいたWebサーバーのグローバルIPアドレスを教えるようにという命令が出される。そしてRoute53はElastic IPで管理しているWebサーバーのグローバルIPアドレスをユーザーへ教える。
2. グローバルIPアドレスを取得したユーザーはVPC（ネットワーク空間のこと。ここにWebアプリを構成するツールを置く。）の入り口であるインターネットゲートウェイを通る。
3. インターネットゲートウェイからロードバランサーまではhttps通信（http通信より安全に通信が行える通信）でリクエストの内容が伝えられ、そのリクエストはルートテーブル（VPCの外からリクエストが来た際の宛先を設定する）で設定されたサブネット内のEC2へ送られる。
4. ロードバランサーから送られたリクエストはセキュリティグループにてEC2へ通すか否かの判断が下され、許可が出ればそのリクエストはEC2へ行きその結果がWebアプリへ反映される。

**補足事項**

- グローバルIPアドレスとは？
AWSでサーバーを作成するにあたり二つのIPアドレスが使用されます。一つが`グローバルIPアドレス`でもう一つが`プライベートIPアドレス`です。グローバルIPアドレスはElastic Ipなどで後から割り当てる、VPC外からみたサーバーのアドレスを示すもので、プライベートIPアドレスはサーバーの作成時に自動的に割り当てられる、VPC内からみたサーバーのアドレスです。もしVPC内のみ（ローカルの環境）で通信を行う際はプライベートIPアドレスのみで大丈夫です。しかしVPCの外から通信を送る場合（例：第三者が利用者となるWebアプリケーションを運用する場合）はグローバルIPアドレスがなくてはサーバーへアクセスできません。

- Certificate Manager とは
ユーザーに対し、このWebアプリはhttps通信（SSL通信）を行っていることを証明する働きをしています。この証明をしているWebアプリはブラウザで見る際にURLの左側に鍵マークがあります。このマークがないWebアプリはユーザーのデータが漏洩する可能性が高いので、注意してください。

参考にした記事
[AWS初心者 入門編 脱・知ってるつもり！AWSのネットワーク関連用語を基礎からおさらい](https://www.bit-drive.ne.jp/managed-cloud/column/column_31.html)
[ウィキペディア　HTTPS](https://ja.wikipedia.org/wiki/HTTPS)
[AWS ルートテーブル](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/VPC_Route_Tables.html)

　自分が使用したAWSのツールの概要は以上です。今度は導入の際に自分が行ったことや参考にした記事をツールごとに紹介していきます。

##AWSのツールを使う前に
　AWSアカウントを作成する際の注意点やAWSの料金管理の仕方は以下の記事から教わりました。AWSを初めて使うけどどうすれば良いかよくわからないという方はこの記事を参考にしてみてください。
[Qita AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ](https://qiita.com/tmknom/items/303db2d1d928db720888#%E7%AE%A1%E7%90%86%E7%94%A8%E3%81%AEiam%E3%83%A6%E3%83%BC%E3%82%B6%E4%BD%9C%E6%88%90)

##VPC&EC2&RDS
　AWSでのVPCの立ち上げからEC2、RDSの作成は以下の記事を参考にしました。各章ごとの内容がわかりやすく書いてありますので、自分の知りたいことをこの記事から探すような活用の仕方が良いと思います。
[(下準備編)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで](https://qiita.com/naoki_mochizuki/items/f795fe3e661a3349a7ce)

##route53
　ドメインの作成からドメインとグローバルIPアドレスの結びつけをroute53で行いました。
参考にした記事は以下の二つです。
[Route 53 でドメインを取得・購入する（2019版](https://dev.classmethod.jp/articles/route53-domain-2019/)
[AWS Amazon Route 53 の開始方法](https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/getting-started.html)

##https通信(SSL通信)
　EC2を作成し、Elastic IPでグローバルIPアドレスを設定しただけだと、http通信でユーザーとサーバーの通信が行われるため安全な通信とは言えません。（通信に関する知識が全くないため、どこが危険なのかはわかりません。）そこでhttp通信をhttps通信に切り替える必要があります。
　自分がhttp通信をより安全なhttps通信に切り替える時やhttps通信（SSL通信）を行っていることに対する証明書を発行する時に参考にした記事はこちらです。
[ナレコムAWSレシピ　AWSでWebサイトをHTTPS化 その1：ELB(+ACM発行証明書)→EC2編](https://recipe.kc-cloud.jp/archives/11084)

#Webサーバーソフトウェア編
　自分のOS、Apacheのバージョンはこちらです。

| ツール | バージョン |
| :------| :-------|
|Ubuntu|18.04.4 LTS|
|Apache|2.4.29|

　LAMPの導入などはいろんな方が記事を書いてくれているので、それぞれの環境に合うものを参考にしていただければと思います。この章では自分が今までWebアプリケーションをデプロイした経験がないゆえにはまったことがあったので、それを紹介します。
　ちなみに自分がLAMPを導入する際に参考にした記事はこちらです。
[Install PHP 7.4 on Ubuntu 18.04](https://www.cloudbooklet.com/install-php-7-4-on-ubuntu/)

##Apacheではまったこと
　自分がはまったことは、ブラウザでドメインを入力したときに自動的に`自分のプロジェクト/public/index.php`を表示するようにApacheを設定するにはどうすれば良いのかということです。自分が見つけたこの問題に対する解決策は、Apacheの設定ファイル（`/etc/apache2/apache.conf`など）内のhttp通信（port80を使用）のときに表示するファイルのパスを設定する部分（`<VirtualHost *:80>`から`</VirtualHost>`にかけて）に`DocmentRoot /var/www/html/自分のプロジェクト/public/`を加えるというものでした。しかし自分の環境の場合それだけではうまくいきませんでした。なぜなら、自分のようにUbuntuをOSとしている場合のApacheの設定ファイルは一つにまとまっているわけではなく、いくつかの設定ファイルに分裂していたからです。
　では`DocmentRoot ~`をどこへ加えたのかというと、`/etc/apache2/sites-available/000-default.conf`に加えることでうまくいきました。
（自分のWebアプリケーションの通信はインターネットゲートウェイからロードバランサーまではhttps通信ですが、EC2からロードバランサーまではhttp通信のためhttp通信の設定を行う`/etc/apache2/sites-available/000-default.conf`を書き換えました。もしEC2からロードバランサーまでをhttps通信で行う場合はおそらくhttps通信の設定を行う`/etc/apache2/sites-available/default-ssl.conf`を書き換える必要があるかと思います。）

この問題に対する解決策は下の記事を参考にさせていただきました。
[index意外のページにアクセスすると404エラーが発生する](https://teratail.com/questions/47368)

#アプリケーション編
　このWebアプリケーションに使用した各ツールのバージョンは以下の通りです。

|ツール|バージョン|
|:--|:--|
|php|7.4|
|Laravel|7.12.0|
|Vue|2.6.11|

##Laravel（バックエンドorサーバーサイド）
###Laravelのインストール時にはまったこと
　メモリの小さいEC2を使用したがゆえにLaravelの一部しかインストールされないという問題にはまり、そのとき自分がteratailで質問したときに得た回答と参考にした記事は以下の通りです。間違えてメモリを１GBにしてしまった時などにはぜひ参考にしてみて下さい。

[AWSのEC2上でのLaravelとAWSでのRDS（MySQL）の環境で"php artisan migrate"を実行した時のエラー](https://teratail.com/questions/256816)
[Life with IT swap領域拡張手順(ファイル割当)](https://l-w-i.net/t/ubuntu/swap_001.txt)

###コードを書く上で気を付けたこと
　Laravelを使用しコードを書く上で気を付けたことは3つです。

1. 設計パターンに`MVC(Model View Controller)`を用いる
2. MVCのうちのControllerの内容はなるべく分かり易いものにする
3. phpとRDBとの手続きやOAut認証の手続きはModel内に収める

　1の理由はADR(Action Domain Response)など設計パターンには様々なものがありますが、初めてFWに触れる自分にとって単純なものを選ぶ方がやりやすいと思ったからです。
　2にある「分かり易い」の基準は二つあります。一つ目はController内のメソッドの引数と返り値には必ず型の指定を行う、二つ目はController内のすべてのコードの質を一定の状態に保つというものです。二つ目のコードの質を判断するために自分は[`PHPStan`](https://phpstan.org/user-guide/rule-levels)という静的解析ツールを使用しました。このツールはレベル別にコードの間違いを指摘してくれます。レベルの一番低いものだと、コード内に宣言されていない変数やメソッドが使用されていないかを確認してくれ、レベルが上がるにつれてメソッドの引数の型が指定されているかや返り値の型の指定がされているかなどを確認してくれます。コードの質のチェックだけではなく、コードのデバックにも使用できて便利です。
　2，3を気を付けるようになった理由は自分が他の方が以前書いたコードの修正を行った時の経験から来ています。私が修正依頼を受けたコードはGoogleAppScript(JavaScriptにGoogleカレンダーなどのGoogleのツールと簡単に連携が取れるライブラリが付け加えられた言語)で書かれたものでした。GASにはTypeScriptのような型の指定をする機能はありませんし、修正を行うコード自体にLaravelにおける`public/index.php`のような処理の全体が分かり易く書かれたコードはありませんでした。そのため個々のメソッドの役割や処理全体の把握にとても時間がかかりました。今回のWebアプリケーションは自分単独で開発・運用するもので自分以外の人が関わることはありませんが、未来の自分は他人と同じような理解力しかないので、そんな自分でもすんなり理解できるにはどうすれば良いのか考えた結果、今回のWebアプリケーションのコードを書く際も2，3を気を付けるようにしました。

###機能を実装する上で気を付けたこと
　機能を作成する際に気を付けたことは`小さく作り、小さく試し、小さく実装する`ということです。例えばこのような要件の機能を付けることになったとします。
`ユーザの入力事項を指定のデータベースへ登録する。`
この機能を作成するには以下の手順で作成するとします。

1. 登録するデータベースの作成
2. 入力値をデータベースへ登録する機能の作成(Model)
3. 入力値をModelへ伝え、Modelの結果によって動作を変える機能の作成(Controller)
4. 入力値のバリデーションの作成
5. 入力ホームの作成
6. フォームとデータベースの登録の機能を結びつけるためのルーティング

　この機能が正常に作成・実装できたかどうかを判断する方法の一つとして、これらの機能を一気に作成・実装し、Laravelのもとからあるデバック機能を使用してエラーの有無をブラウザ上で確認する方法があります。この方法はスピーディーに実装できる反面、本番用のデータベースへ値を入れなくてはならなかったり、複数のパターンで複数回テストするのに手間がかかったりします。そこで自分が行った方法は、手順を一つ進めるごとにその手順で行ったことが要件を満たすものかテストを行うという方法です。例えば、1が完了したらLaravelのテスト機能を使用して要件に合致するレコードが作成できたのか確認し、2が完了したら作成した機能にテスト機能を使用して入力値を入れることで指定のレコードへ登録できたかを確認するという感じです。この方法は、一つ一つ手順を進めるごとにテストを作成しなくてはいけないので実装のスピードは遅くなる反面、Laravelのテスト機能のおかげで本番のデータベースへ値を入れる必要がなかったり、`factory()`などのヘルパ関数を使用して様々なパターンで100件単位のテストを一瞬で行えたり、個別にテストを行うことによりエラーの原因を絞って考える（例えば、もし手順3の段階でエラーが出た場合、2までは正常だったため原因は手順3で行ったことにあると違いないと推測できる）ことができます。
　Laravelのテスト機能をはじめから使用するのは大変かもしれませんが、[Laravelの公式ドキュメント](https://laravel.com/docs/7.x/testing)でもたくさんのテスト機能の紹介がされているので、Laravelを使用するのならばぜひ使ってみて下さい。

##Vue（フロントエンド）
###LaravelとVueの両方で開発経験がなくてLaravelでアプリケーションを開発する方へ
　これは自分の質問に答えてくれたエンジニアの方が言ってくれたことであり、自分もLaravelとVueを組み合わせて開発していて思ったことなのですが、もしLaravelとVueのどちらも今まで触ったことがない場合はLaravelのみでWebアプリケーション開発を行う方が良いということです。なぜならWebアプリケーション開発で二つのFWを組み合わせて使用しようと思うと、考えることがFWが一つのときの倍ぐらいに増えてデプロイまでが長くなってしまうからです。デプロイまでが長くなると気力が一気に落ちると思うので、最初のうちはなるべく単純なものの方が良いかなと思います。

###LaravelにおけるVueの実装
LaravelでのVueの実装は以下の記事を参考にしました。
[Laravel7からVue.jsを使う最短レシピ](https://qiita.com/fruitriin/items/118c773b045101db7651)

###自分がドはまりしたところ
　自分がLaravelとVueの組み合わせたときに一番悩んだのはVueのコンポーネントにフォームを作成した際、そのフォームの値をどうやってCSRF（ユーザーになりすますことで他のサイトから不正にリクエストを送ること）対策をしたうえでバックエンドへ伝えるかということです。LaravelのViewの役割をもつ`blade.php`でのLaravel7に対するCSRF対策はこんな感じです。

```php
//省略
<form action="バックエンドまでのパス" method="post">
@csrf
<input name="name" type="text">
</form>
```

　これをVueのコンポーネントのフォームでも同じようなことをしようとすると`@csrf`の部分が無効となり419エラーで怒られてしまいます。この問題は、コンポーネントにCSRF対策を書くのではなく、コンポーネントを表示している`blade.php`にjQueryでこのように付け加えることで解決できます。

```js                                                                                                          
$('body').on('submit', 'form', function () {
$(this).append('@csrf')
});
```
　このコードで書いてあることは、body要素の中のform要素にsubmitというイベント（`<input type="submit">`を実行する）が発生した時にform要素に`@csrf`を加えるということです。解決方法は他にもいろいろあると思います。この解決方法は自分がteratailで質問した際に教えて頂きました。その時の質問と回答はこちらです。
[Laravelを使用したVueのcomponent内のformのpostリクエストで419エラー](https://teratail.com/questions/266990)

　LaravelとVueにおけるこの他のCSRF対策を紹介した記事はたくさんありました。また、[Laravelの公式ドキュメント](https://laravel.com/docs/7.x/csrf)ではCSRF対策をしているミドルウェアを無効にする方法が紹介されていました。しかし、CSRF対策が書かれた記事に書いてある内容が自分にとって複雑に感じるものが多かったり、CSRF対策を無効にしてWebアプリケーションの安全性を下げるのは良くないと思いjQueryを使用した方法を選びました。


#終わりに
　Webアプリケーションを作成しただけではなく、それらを運用する環境まで整えたのでたくさんの時間（3か月くらい）がかかりました。Webアプリケーションを作成しようと思わなかったら感じる必要のないストレスを感じました。しかしこの経験を通し、自分の頭の中でふわふわ浮いていたネットや書籍で知った言葉たちが、ようやく実感のある言葉になったなーという感覚を持てるようになりました。
　このWebアプリケーションのデプロイまでに自分がAWSに対して払った料金はドメイン取得時の千円ぐらいです。またEC2で初めに入れたOSが気に食わなければ何回も無料で作り直せます。（現に自分はAmazon Linux ⇒　Ubuntuへ変更しました。）AWSのEC2などのサービスは初めの12年は無料で使用できるので、初めてのWebアプリケーションのデプロイは年間で料金を払うようなレンタルサーバーよりもAWSを使用した方が良いと個人的に思っています。（BillingなどでAWSのコスト管理はしっかり行ってください。）
　なにも知らなかった自分がWebアプリケーションを作成できるようになったのは、無償でteratail・コミュニティなどで自分の質問を答えていただいた方々や分かり易いLaravelの公式ドキュメントを書いてくれた方々、書籍（[PHPフレームワーク Laravel Webアプリケーション開発](https://www.socym.co.jp/book/1184)）に携わった方々、ネットで分かり易い記事を書いてくれた方々、お金がない日本人学生でもAWSを使いやすいようにしてくれた方々のおかげです。今後自分と同じような部分で悩んでいる人へ解決策を伝えたり、OSSへの貢献（できるかなー？）するなどで恩返しできれば良いなと思います。

**追記**
このアプリケーションのコードはgithubで公開しています！
[Github LottemPoteto](https://github.com/santooooooo/LottemPoteto)