自作バックエンドでアプリケーション開発！ その1

# 経緯
今年からWebエンジニアになる23卒エンジニアです。
在学中は主にFlutterとFirebaseで開発をしていたので、Webの知識に疎い＆バックエンドを自作したことがありません。。

そこで、このシリーズで自作バックエンド＋Webアプリの経験をすると共に
- Docker使いこなしたい
- Webの知識つけたい
- AWS触りたい
- CI/CDやってみたい

という野望をゆるーく掲げつつ、
APIサーバ / コンソールアプリ（Web）/ クライアントアプリ（Mobile）な構成の書籍管理アプリを作っていきます。

# ざっくり
[phpのapache入りイメージ](https://hub.docker.com/_/php)をベースにコンテナを作ります。
APIとコンソールアプリそれぞれサーバーを立てます。
こんな感じのURLにしていきます。
|             |URL                             |
|:-:          |:-:                             |
|API          |https://localhost:8000/api/*    |
|コンソールアプリ|https://localhost:8080/console/*|

# フォルダ構成
```
book_app
    ├ api
    ├ console
    └ docker
       └ docker-compose.yml
```

# docker-compose.yml
```yml:docker-compose.yml
version: "3.6"
services: 
  api:
    container_name: "api"
    image: php:8.1-apache
    ports:
      - "8000:80"
    volumes:
      - ./../api/:/var/www/html/api/
        
  console:
    container_name: "console"
    image: php:8.1-apache
    ports:
      - "8080:80"
    volumes:
      - ./../console/:/var/www/html/console/
```

## 自分なりの理解
### php8.1及びapacheの環境が揃ったイメージ
php＆apacheサーバ わくわくスターターキット！

### コンテナの80番ポートとホストマシンの8000番ポートを繋ぐ
apacheがデフォルトだと80番ポートでListenしているので80番を指定。
ホストマシンの8000番に来たリクエストはapiコンテナのapacheサーバに繋がる。

### apacheのプロジェクトルート配下にapiフォルダをマウントする
こうすればhttps://localhost:8000/api/* のURLにできる。
デフォルトのプロジェクトルートは/var/www/html


# 詰まったところ
- ### Dockerfileで指定できるファイルには制限がある
    Dockerfileで書いていた時に、apacheのプロジェクトルートにapiフォルダをCOPYしようとして詰まった。
    apiフォルダはbuild contextの外側にあるので参照できないということだった。
    詳しくは[この記事](https://qiita.com/mk-tool/items/1c7e4929055bb3b7aeda)に書いてあった。
    
    この詰まりについてはそもそも手段がいまいちで、apiフォルダをCOPYする方法ではコードを触るたびにビルドし直す必要が出てくる。
    それだと開発時には辛い思いをすることになるのでdocker-compose.ymlのvolumesを使ってバインドマウントするのが良かった。

- ### docker-composeのポート指定が何をしているか分かっていなかった
    <ホストマシンのポート指定>:<コンテナのポート指定>と書くんだなーとは思っていたが、好きな数字を入れたら良いんだ！くらいに思っていた。
    今回はapacheサーバを使ったアプリケーションを作っているので、apacheがListenしているポート（デフォルトで80番）を指定してあげる必要があった。

# 成果
ベタ書きAPIをベタ書きJavaScriptでfetchして書籍一覧のリストを表示してみます。
## API（参考: [PHPでAPI作成](https://qiita.com/tyatya_maruko/items/1cda7b365e54771da6ac)）
```php:hodling_book.php
<?php
header('Content-Type: application/json; charset=utf-8');

$response = array();

$book_1 = array("name" => "良いコード／悪いコードで学ぶ設計入門");
$book_2 = array("name" => "ドメイン駆動設計入門");
$book_3 = array("name" => "Clean Architecture");
$book_4 = array("name" => "超すごい本");
$book_5 = array("name" => "バリすごい本");

$books = array($book_1, $book_2, $book_3, $book_4, $book_5);

$response["status"] = "success";
$response["books"] = $books;

$json = json_encode($response, JSON_UNESCAPED_UNICODE);

print($json);
```
![スクリーンショット 2023-03-07 1.22.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/6bf56e58-9f73-2507-e8ef-25249bee0464.png)


## consoleアプリ
```php:index.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>BookApp</title>
</head>
<body>
<h2>書籍一覧</h2>
<script type="text/javascript" src="./book_list.js"></script>
</body>
</html>
```
```js:book_list.js
const ul = document.createElement("ul");

fetch("http://localhost:8000/api/v1/holding_book.php")
    .then(async response => {
        
        const json = await response.json();

        json.books.forEach(book => {
            let name = book.name;
            const li = document.createElement("li");
            li.textContent = name;
            ul.appendChild(li);
        });
    })
    .catch((reason) => {
        console.log(reason);
    });

document.body.appendChild(ul);
```
## 俺たちが求めていたもの
最高！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/07c6133b-c3f1-fd21-f1ed-42928782ae00.png)


## 現実
CORSエラーが出てfetchできていません。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1159960/3afe6fa5-4a10-4ed0-8937-962a6ce32c8b.png)




# 次回の目標
フレームワークの力を借りてCORSの設定を行いたい!