【Angularアプリケーション開発 #8】フロントエンドとバックエンドを繋げてみよう

## 目的
フロントエンドとバックエンドを疎通させること。
実際の開発現場に近い環境を再現し、経験と知識を増やすこと。

## 今回やること
なんちゃって従業員管理を作ります。
従業員一覧をDBから取得して、webページで表示するといった内容になります。
↓今回作ったwebページです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689436/2415bd29-5552-41ae-8691-e2fa26ada783.png)


## 開発言語とフレームワーク
フロントエンド：javascript、typescript
フロントエンドフレームワーク：angular
バックエンド：java
バックエンドフレームワーク：spring boot
DB：mysql
※DBはdockerのコンテナに構築しています。ローカルに直でインストールしていても問題はありません。

## フロントエンド
MenbersComponentを作成します。
ngOnInitでバックエンドにリクエストを送る処理を行っています。
レスポンスとして返ってくるのは従業員のidとnameです。
それを```members```に設定しています。
html側ではmembersをループさせてメンバーリストを作成しています。
```typescript:members.component.ts
import { Component, OnInit } from '@angular/core';
import { Member } from '../member';
import { MEMBER } from '../mock-member';

@Component({
  selector: 'app-members',
  templateUrl: './members.component.html',
  styleUrls: ['./members.component.css']
})
export class MembersComponent implements OnInit {

  members;
  selectedMember: Member;

  constructor() { }

  ngOnInit(): void {
    const myAsync = async (url) => {
      const response = await fetch(url); //await で fetch() が完了するまで待つ
      const data = await response.json(); //await で response.json() が完了するまで待つ
      return data;
    }
    myAsync('http://localhost:8080/api/getUserInfo').then(value => this.members = value).catch((err) => console.log("データ取得に失敗"));
  }

  onSelect(member:Member): void{
    this.selectedMember = member;
  }

}
```
```html:members.component.html
<h2>従業員一覧</h2>

<ul class="members">
  <!-- (click)はイベントバインディング -->
  <li
   *ngFor="let member of members"
   [class.selected]="member === selectedMember"
    (click)="onSelect(member)"
  >
    <span class="badge">{{member.id}}</span> {{member.name}}
  </li>
</ul>
<app-member-detail [member] = "selectedMember"></app-member-detail>
```
## バックエンド
### プロジェクト構成
spring bootのプロジェクト作成については今回は省略します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689436/84c8ce07-bf6e-9690-8532-b6511e514719.png)

### srcの説明
#### UserInfoApi.java
```@GetMapping("api/getUserInfo")```
⇒フロントでリクエストに使用するエンドポイントを定義しています。
```jdbcTemplate.queryForList("select id,name from userinfo");```
⇒DBへ投げるsqlです。
```@CrossOrigin```
⇒これをつけておかないとレスポンスが返ってきません。
　詳細は省略しますが、要は異なるオリジン間の通信を許可しています。
　オリジンとはhttp://～/api/get〇〇:8080のようなurlのことです。
　フロントエンドとバックエンドは使用しているportが異なるので、
　こちらのアノテーションが必要になります。

#### UserInfo.java
役割としてはDBから取得した値とjava側からフロントエンドでの返却値を格納する箱になります。
#### ApitestApplication.java
main関数があるので、javaを起動するためには必要なクラスになるのだと思っています。
#### ServletInitializer.java
こちらは何をしているのかわかりませんが、必要になったら調べます。

```java:UserInfoApi.java
package com.example.demo.api;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.demo.entity.UserInfo;

@RestController
@CrossOrigin
public class UserInfoApi {

	@Autowired
	JdbcTemplate jdbcTemplate;

    @GetMapping("api/getUserInfo")
    public List<UserInfo> getHoge() {
    	List<Map<String,Object>> userInfoListMap;
    	List<UserInfo> userInfoList = new ArrayList<UserInfo>();
        userInfoListMap = jdbcTemplate.queryForList("select id,name from userinfo");
        //listの中のmapを取り出す
        for(Map<String,Object> map:userInfoListMap) {
        	UserInfo info = new UserInfo();
        	info.setId((int) map.get("id"));
        	info.setName((String) map.get("name"));
        	userInfoList.add(info);
        }
        return userInfoList;
    }
}
```
```java:UserInfo.java
package com.example.demo.entity;

public class UserInfo {

    private int id;
    private String name;

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
```
```java:ApitestApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ApitestApplication {

	public static void main(String[] args) {
		SpringApplication.run(ApitestApplication.class, args);
	}
}
```
```java:ServletInitializer.java
package com.example.demo;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {

	@Override
	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
		return application.sources(ApitestApplication.class);
	}
}
```
## DB
userinfoでテーブルを作成しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2689436/986c3902-bf61-9fb1-6a31-6de38b9dc3ac.png)

## 詰まった部分
spring bootでDBを使用するのであれば、DBは疎通できる状態で待機しておく必要があります。
というのもpom.xmlでDBに関するライブラリを定義しているのですが、DBに接続できる状態でないとビルドは通りますが、実行時にエラーになります。ちなみにDB接続情報は```application.properties```にあるので、こちらもdb名やら間違わないように書く必要があります。
私はjavaのバージョンが異なるのかと思い込んで苦戦しました。
エラー内容の抜粋↓
```CommunicationsException: Communications link failure```

## まとめ
今回は、従業員情報を取得するだけでしたが、改良すれば登録、削除、更新等もできそうです。
一連の疎通ができたわけですが、細かい部分をあまり追及せず、まずは目的を達成することが大事だと感じました。
仕事で実際に作業をする場合は、既に開発環境が出来上がっている状態が多いので、今回のように一から作っていくのは貴重な体験でした。