Spring boot + Vue.js + MySql + VS Code + Docker で開発環境を構築する


#まえがき　

###クリアしたいこと

* ホストマシンに特定のjdkを入れたくない
* springはホットリロードもデバックもしたい
* docker-composeにフロントバックDBオールインワンしたい
* VS Code使いたい

### なぜ書いたか

* 全部盛りの情報があんまりなかったので
* 実際に開発を始められるところまで環境構築を書いてる情報があまりなかったので
* 備忘録として

#git 

この記事の最後の手順まで実施したものを置いておきます。
https://github.com/PokoPoko2ry/docker_vue_spring_sample/tree/master

#前提
VS Codeインストール済み
Dockerインストール済み(試した環境はDocker For Windows + WSL2)

# ディレクトリ構成
とりあえず下記の形でDockerのホスト環境(WSL)に作成

```
.
├── .devcontainer
├── docker
│   ├── mysql
│   │   ├── initdb
│   │   └── my.cnf
│   ├── spring
│   └── vue
├── [spring-project]
└── [vue-project]

```


# Dockerのセットアップ
## docker-compose.ymlを作成

docker/に作成
vueはデフォルトだとspringとportがぶつかるので、**9000**としました。

```docker-compose.yml
version: '3.6'
services:
  mysql:
    container_name: mysql
    build: ../docker/mysql
    environment:
      MYSQL_DATABASE: mysqldb
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MYSQL_ROOT_PASSWORD: rootpassword
    ports:
      - 3306:3306
    volumes:
      - ./mysql/initdb:/docker-entrypoint-initdb.d
      - mysql_db:/var/lib/mysql
  backend:
    container_name: backend
    build: ./spring
    depends_on:
      - mysql
    ports:
      - "8080:8080"
    tty: true
    volumes:
      - ../spring_project:/srv:cached
    working_dir: /srv
  frontend:
    container_name: frontend
    build: ./vue
    ports:
      - 9000:8080
    volumes:
      - ../vue_project:/usr/src/app:cached
    stdin_open: true
    tty: true
volumes:
  mysql_db:
    driver: local
```

## Dockerfileを作成

###spring
docker/spring　に作成

```Dockerfile
FROM openjdk:11

VOLUME /tmp
WORKDIR /app

```
###vue

docker/vue　に作成

```Dockerfile
FROM node:14

WORKDIR /usr/src/app/

RUN npm install -g npm && \
    npm install -g @vue/cli

```

###mysql

docker/vue　に作成

```Dockerfile
FROM mysql

EXPOSE 3306

ADD ./my.cnf /etc/mysql/conf.d/my.cnf

#docker runに実行される
CMD ["mysqld"]
```

docker/mysql に設定ファイルを作成しておきます。文字コードを設定。

```my.cnf
[mysqld]
character-set-server=utf8

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8
```
#環境のセットアップ
## springのプロジェクトを作成

spring initializrを使用させてもらう。
https://start.spring.io/

いったんこんな感じで作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/6b19406b-e63b-dfa3-e173-cc795d9462ad.png)

Generateしたら、吐き出されるZIPを解凍してspringのプロジェクト格納先に置いておきましょう。

dbの接続のために、
src/main/resources配下に、application.ymlを作成しておきます。

```application.yml
spring:
  datasource:
    url: jdbc:mysql://mysql:3306/mysqldb
    username: user
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
```

## vueのプロジェクト作成
vue cliを使いたいので一度コンテナを立ち上げます。

```
$ docker-compose build
$ docker-compose up -d
Recreating mysql    ... done
Recreating frontend ... done
Recreating backend  ... done
$ docker exec -it frontend /bin/bash

```

はいれたらvue createでプロジェクトをお好みの内容で作成後、動作確認をします。


```
$ vue create .
<省略>
$ npm run serve
```

npm run serveを実行すると
localhost:8080　に行けと言われるかもしれませんが、composeファイルで9000番を指定したので
ポートは9000番を指定します。

確認 (http://localhost:9000) 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/a1cd6063-e806-a1ef-5cd3-2572f8fe49f9.png)

ここまでできたら、一度 docker-compose downでコンテナを止めます。

```

$ exit
$docker-compose down
Removing backend  ... done
Removing mysql    ... done
Removing frontend ... done
Removing network docker_default

```

## vscodeのセットアップ
ホスト側(WSL上)で
拡張機能の　Remote Development　をインストール。

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack

>This Remote Development extension pack includes three extensions:
ということで

Remote - SSH
Remote - Containers 
Remote - WSL 

が同時に入ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/aaeb8274-721c-910f-f366-51ba817654a2.png)

次に、Remote container用の設定ファイルを作成。
.devcontainer\に、devcontainer.json
を作成するとremote containerにattachした時のvs codeの設定を読み込んでくれます(便利)

```.devcontainer\devcontainer.json
{
    "name": "spring-vue-mysql",
    "workspaceFolder": "/srv",
    "dockerComposeFile": "../docker/docker-compose.yml",
    "settings": { 
        "terminal.integrated.shell.linux": "/bin/bash",     
    },
    "service": "backend", //attachするコンテナはspringを指定
    "extensions": [
        "vscjava.vscode-java-pack", // JavaExtensionPack
        "pivotal.vscode-boot-dev-pack", // Spring Boot Extension Pack
        "gabrielbb.vscode-lombok" //Lombok Annotations Support For VS Code
    ],
}
```

"name" →　設定に名前を付ける。任意の内容で大丈夫かと。
"workspaceFolder" → containerにattachした後、どのフォルダを開くか。ここではvolumeのバインド先のdirを設定
"dockerComposeFile" → 立ち上げるコンテナのcomposeファイルを指定。
"settings" → shellを指定。
"service" → composeを使用するので、どのコンテナにattachするのかを指定する。
"extensions" → container側にインストールしておきたいVS Code拡張機能のextansion Idを入力。

## APIを作成

起動がうまくいったかどうかの確認で疎通を取りたいため、
java/com/example/spring-projectにAPIを作成。

vueからの実行確認を取りたいのでCORS対策でアノテーションをつけておきます。

```controller/HelloController.java
package com.example.spring_project.controller;

import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@CrossOrigin(origins = {"http://localhost:9000"})
public class HelloController {

    @RequestMapping("/hello")
    public String hello() {
        return "hello world!";
    }
}

```
#環境の立ち上げと動作確認

## Remote containerを実行する
VS code左下の><のところを押す。
devcontainer.jsonの文法エラーがなくvscodeに正しく認識されていれば、
[Remote-Containers: Reopen in Container]が選択できると思うので実行。

json内で指定したdocker-compose.ymlをもとに、containerが立ち上がり、
containerにattachされた状態でvscodeが再起動します。



##springを実行

VS Codeが立ち上がった後、左下の吹き出しでjavaのprojectをimportするか？的なことを聞かれると思うので
Yesを押します。

その後、左側のメニューから　[^1] Run and Debugを実行。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/95bef3b3-7b12-f95a-ad3b-fb26af321c90.png)

実行環境を選ぶように言われるので、Javaを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/2ded3ac7-f2cb-c51d-bba2-d3ccc7a88e2b.png)

しばらく待ちます・・・
🔁のアイコンを押すと進捗が見れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/81ce98ae-ac4c-5b62-799d-11f828afaea4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/5bcea6d8-e179-ba6c-a1ee-024af3744c3b.png)

終わると、デバッガがアプリを立ち上げてくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/36b4d529-3f75-fa86-8bd5-a0afd5d1c2b9.png)




## 動作確認

実行を確認します。　先ほど作成したAPIにアクセス！
http://localhost:8080/hello
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/df2d25e1-7a7b-d7de-99f2-d59375c0a6d5.png)

debugを試してみます。
ついでにreturnの内容を変えて、ホットリロードされるかも試してみます。
下図のようにソースを変更、break poitnを指定して保存後、ブラウザをリロード。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/2a0af68b-5eef-a043-5225-e36b7e58b320.png)

このように止まり、ブラウザの内容も変わりました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/9a617962-4f0d-b2a5-f0b1-226a74812368.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/d802dccf-b70e-b3c3-8d65-d6057d7991cb.png)

vueコンテナからAPIを叩いてみます。
VS Codeのwindowを新しく立ち上げて、frontendコンテナに入ったのち、axiosをinstall

```
$ docker exec -it frontend /bin/bash
$ npm install --save axios vue-axios
```

API実行の処理を書きます。
main.jsとApp.vueを下記のように変更。

APIのレスポンス内容をalertで表示するだけの簡単な機能を実装します。

```main.js
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios' 
import VueAxios from 'vue-axios' 

Vue.config.productionTip = false
Vue.use(VueAxios, axios) 

new Vue({
  render: h => h(App),
}).$mount('#app')

```

```App.vue
<template>
  <div id="app">
    <input @click="HelloWorld" type="button" value="HelloWorld">
  </div>
</template>

<script>

export default {
  name: 'App',
  methods: {
      HelloWorld() {
        this.axios.get('http://localhost:8080/hello')
          .then((response) => {
            alert(response.data);
          })
          .catch((e) => {
            alert(e);
          });
      }
    }
}
</script>
```

vueをスタートし、実行を試してみます。

```
$ npm run serve
```

→　http://localhost:9000

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317088/8e453985-1968-b616-4708-1b77e42641cf.png)

できました👏
コンテナ立ち上げと同時にvueのサーバーが立ち上がるように、Dockerfileを編集します

``` vue/Dockerfile
FROM node:lts

WORKDIR /usr/src/app/

RUN npm install -g npm && \
    npm install -g @vue/cli

CMD [ "npm", "run", "serve" ] //追加
```

# 最後に
指摘や改善案、編集リクエストなどあればどんどん頂けると嬉しいです。

# 参考にさせていただいた記事

##### VS Code
https://ameblo.jp/kazusa-g/entry-12535018096.html

https://qiita.com/d0ne1s/items/d2649801c6f804019db7

##### Vue
https://qiita.com/right1121/items/092ac7ff747e1c47b2b1

##### spring & docker
https://qiita.com/yamii/items/b2b5e6b6a7aff6d590d8


[^1]: projectのimportに関してちょこちょこエラーが出る場合が見られました。原因がよくわかってないですが、今のところ全部VS CodeをReloadで治ってます。