1から作るWebアプリケーション(Next/Nest)

はじめまして
今回からWebアプリケーションを作ります。
プロジェクトとしてWebアプリケーションを1からちゃんと作ったことはないので今回はしっかり作っていきたいと思います。

この記事では主に環境構築を行い次回以降の記事で開発を行っていきます。

# リポジトリを作る
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3946470/0fafd0c2-cc98-0a5d-db98-f638f1b7b5fc.png)

こんな感じで作って行きます
今回は`Public`で作ります。
他人に見られるのが嫌な人は`Private`を選択しましょう
`Initialize this repository with:`の部分は初期設定ですね
### `Add a README file`
READMEを作ってくれます。
今回は書かないかもしれませんが作っておきます、
### `Add .gitignore`
Gitで管理したくない物を管理する`.gitignore`のファイルを追加するかどうかです。
これについては一旦`None`にします
### `Choose a license`
ライセンスの名の通り権利関係になります。`None`でもデフォルトの著作権として機能するので著作権フリーにはならないです。安心ですね
# ローカルでプロジェクトを作成
開発するうえでディレクトリが無いと始まりません。以下のようにしてディレクトリを作成します
```
D:\>mkdir 04-stats
D:\>cd 04-stats
D:\04-stats>
```

バックエンドとフロントエンドのモジュールをそれぞれインストールします。
```
D:\04-stats>npx @nestjs/cli new backend
```
```
npx create-next-app frontend --typescript 
```
`create-next-app`で聞かれる事に関しては以下の記事を参考にしながら進めました


https://zenn.dev/ikkik/articles/51d97ff70bd0da


# Dockerの設定
次にDockerの設定をします。
現時点でbackendディレクトリとfrontendディレクトリは上記のnpxで作成されているはずなので次に`docker-compose.yml`を作成して以下のようなディレクトリ構造にします
```
04-stats/
├── backend/         # NestJS アプリケーション
├── frontend/        # Next.js アプリケーション
└── docker-compose.yml
```

## docker-compose.ymlの作成
今回はこんな感じで`docker-compose.yml`を作成します
```docker-compose.yml
services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    develop:
      watch:
        - action: sync
          path: ./frontend
          target: /home/node/app/
          ignore:
            - node_modules/
        - action: rebuild
          path: package.json
    environment:
      - NODE_ENV=development
    depends_on:
      - backend

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "4000:4000"
    develop:
      watch:
        - action: rebuild
          path: ./backend
    environment:
      - NODE_ENV=development
      - PORT=4000

```

`services:`
今回は`frontend`と`backend`をサービスとして設定します。

```
    build:
      context: ./frontend
      dockerfile: Dockerfile
```
`context`でDockerイメージをビルドする際のディレクトリ、`dockerfile`でビルドするときのDockerファイルを設定します

これで`./frontend`にある`Dockerfile`を使うよ！っていう設定ですね

```
    ports:
      - "3000:3000
```
イメージ内のポート3000とホストの3000をバインドするよという設定

```
    develop:
      watch:
        - action: sync
          path: ./frontend
          target: /app
          ignore:
            - node_modules/
        - action: rebuild
          path: package.json
```

今回は2023年9月に実装されたらしい`dokcer compose watch`を使ってみたいと思います。
ボリュームにマウントはせずにローカルのファイルの変化を見てそれに応じたアクションをしてもらいます
```
- action: sync
  path: ./frontend
  target: /app
  ignore:
    - node_modules/
```

対象のディレクトリやファイルが更新されたときにコンテナ内の指定のディレクトリと同期を取ってくれます。
今回は`./frontend`内のファイルが更新されたときにコンテナ内の`/app`にそれを反映してくれるという設定です。
```  
ignore:
  - node_modules/
```
で`node_modules`の更新時は無視してくれます。

```
- action: rebuild
  path: package.json
```

対象のディレクトリやファイルが更新したときにコンテナをリビルドしてくれます。`package.json`が変わったときはリビルドしてもらいましょう

```
environment:
  - NODE_ENV=development
```

環境変数の`NODE_ENV `を定義します。今回は`development`を定義

```
depends_on:
  - backend
```
これで`frontend`サービスは`backend`サービスに依存していることを設定できます。
これにより`backend`サービスが起動してから`frontend`サービスが起動するようになります



`backend`もほとんど同じですが`environment`での環境変数とウォッチモードの設定について少し差があります。
```
environment:
 - NODE_ENV=development
 - PORT=4000
```
`backend`サービス側では環境変数としてport番号も設定します。これでバックエンド側で`process.env.PORT`としたときに4000として使えます。

```
watch:
  - action: rebuild
    path: ./backend
```
`frontend`で書いた事と重複しますが、バックエンド側はファイルの更新があったときにはリビルドしてほしいので`backend`以下が更新されたときにはリビルドしてもらいます。

## フロントエンド側のDockerfileの作成
では次に`docker-compose.yml`内の
```
build:
      context: ./frontend
      dockerfile: Dockerfile
```
で指定した`/frontend/Dockerfile`を書いていきましょう

``` Dockerfile
# ベースイメージ
FROM node:22-alpine

# 作業ディレクトリを設定
WORKDIR /app

# 必要なファイルをコピー
COPY package*.json ./

# 依存関係をインストール
RUN npm install

# アプリケーションコードをコピー
COPY . .

# ポートを公開
EXPOSE 3000

# 開発モードで起動
CMD ["npm", "run", "dev"]

```
一つずつ見ていきましょう
```
FROM node:22-alpine
```
ベースイメージです。node22がLTSの最新なので今回はnode22を使ってみようと思います
`alpine`についてですがこれはDockerのイメージの種類です。
`distroless`とかありますがshellとか使えないので今回は`alpine`にします。
無駄なパッケージが含まれていたりでその分脆弱性も上がるのですが今回は個人情報などを扱うわけではないので良いとしましょう

```
WORKDIR /app
```
これはワーキングディレクトリの設定です。以降のコマンドは`/app`で実行されます

```
COPY package*.json ./
```
`package.json`や`package-lock.json`がコピーされます。これが`/app`で実行されます

```
RUN npm install
```
`npm install`します。流石に説明不要かと


```
COPY . .
```
Dockerfileがあるローカルディレクトリ内のファイルすべてをコンテナ内の`/app`にコピーします。
コピーしたくないファイルがある場合は`.dockerignore`を作成して制御しましょう

```
EXPOSE 3000
```
コンテナの3000ポートを開放します。`docker-compose.yml`でバインドの設定をしたのでローカルの3000で使えます


```
CMD ["npm", "run", "dev"]
```
これで自動的に`npm run dev`を実行します。


## バックエンド側のDockerfileの作成
バックエンドも同様に`/backend/Dockerfile`を書きます

``` Dockerfile
# ベースイメージ
FROM node:22-alpine

# 作業ディレクトリを設定
WORKDIR /app

# 必要なファイルをコピー
COPY package*.json ./

# 依存関係をインストール
RUN npm install

# アプリケーションコードをコピー
COPY . .

# ポートを公開
EXPOSE 4000

# 開発モードで起動
CMD ["npm", "run", "start:dev"]

```
ポートと起動以外は同じです。


# 起動！

それでは実際に起動してみましょう！

```
D:\04-stats>docker compose watch
```

しばらくしたら起動すると思います。

起動したら
http://localhost:3000 にアクセスしてみましょう。以下のような画面が表示されるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3946470/35b6f3fa-382d-ff11-5a7a-7a8970b0605f.png)

バックエンド側も正しく起動できているか確認してこの記事は終わりたいと思います。

そのままでもいいですが今回は以下のようにして出力文を変えてみます。
```backend\src\app.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello Qiita!';
  }
}

```
curlで`http://localhost:4000`へリクエストを投げます。もちろんブラウザからhttp://localhost:4000 へアクセスしても構いません。

正しく文字が出力されればNestとNextの準備が整いました。次回からアプリケーションの開発を進められます


```
D:\04-stats\backend>curl http://localhost:4000
Hello Qiita!
```