WEB開発未経験から一人でアプリを作りました

  この記事は「[NEXTSCAPE Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nextscape)」の11日目の記事です

WEB開発未経験の私が、基礎的なWEB開発の知識を身に着けるために書評WEBアプリを題材に作成しました。

この記事では最初にアプリの紹介をしたのちに

- インフラ構成について
- 書籍照会のためにGoogle Books APIsを採用
- React Hooksで自分がハマってしまったポイントについて

といったところについて、お話ししようと思います。

## どんなアプリを作ったのか

すでにお話に出ていますが、書評WEBアプリです。
ユーザーが本についてのレビューを書き込み、他のユーザーと共有できるといったものです。

## なぜこのアプリを作ったのか
ネクストスケープでは本を読む方が多く、お互いに本を薦めることが頻繁にあると聞いたからです。せっかく作るなら、使っていただける可能性のあるテーマで作成するほうがより、モチベーション持って続けられると感じたからです。

## アプリの写真と紹介
### トップページ
トップページでは新着レビューを閲覧したり、人気のレビューをランキング形式で見れるようにしてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889527/500792cd-12cb-57e8-1ba4-3cc41a237ad3.png)


### レビューページ
レビューを選択することで、詳細の閲覧やレビューに対して「いいね」することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889527/6e3bbae8-b5c0-8bea-287b-d1e766b5a517.png)

### マイページ
マイページでは投稿したレビューを管理できます。また、自分が過去にいいねしたレビューも閲覧できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889527/a98e931b-4faa-2cc0-9453-ad5706370dd7.png)

## 使用技術
#### フロントエンド
React, Material-UI

#### サーバーサイド
.NET Core3.1

#### インフラ
Microsoft Azure, AzureDevOps

サーバーサイドは、.NET Core で作成しました。
オブジェクト指向設計を意識して、業務システムを作成する際の練習になるようにしました。

また、AzureDevOpsを用いてパイプラインを作成し、masterブランチにpushされたら、ビルド、単体テスト、デプロイが自動で実行されるようにしました。

## インフラ構成について
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/889527/8b4e9fa7-1891-8637-8fb2-4d3be1727e0a.png)



## 書籍照会のためにGoogle Books APIsを採用
アプリで書籍の情報を取得する必要がありました。
当初は以下の三つで比較・検討

- [Google Books APIs](https://developers.google.com/books/docs/overview)
- [楽天ブックス書籍検索API](https://webservice.rakuten.co.jp/explorer/api/BooksBook/Search/)
- [Amazon Product Advertising API](https://www.amazon.com/ap/signin?openid.return_to=https%3A%2F%2Faffiliate-program.amazon.com%2Fassoc_credentials%2Fhome&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.assoc_handle=amzn_associates_us&openid.mode=checkid_setup&marketPlaceId=ATVPDKIKX0DER&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.pape.max_auth_age=0)

それぞれ実装の容易さ、書籍情報量、書籍情報の質を中心に紹介できればと思います。

まず、紹介に入る前にざっと内容をまとめてみました。

|  | 実装の容易さ | 書籍情報量 | 書籍情報の質|
|:-----------|:------------|:------------|:------------|
| Google Books APIs|認証必要なし|ページ数やカテゴリもとれる|ISBNがなかったり、フォーマットがまばら。JSONで取れる|
| 楽天ブックス書籍検索API|認証あり|ページ数が取れない|レスポンスが綺麗（ただしAPIによって形式が異なる）JSONで取れる|
| Amazon Product Advertising API|APIの内容みるために登録が必要|未検証|未検証（レスポンスはxml形式のみ）|

### Google Books APIs
単純な書籍情報が欲しい時に一番おすすめです。APIを利用する際に登録は不要で
```https://www.googleapis.com/books/v1/volumes?q=【ここに書籍名または著者など】```
だけで、すぐに使えます。
ここを押してみてください⇒https://www.googleapis.com/books/v1/volumes?q=相沢沙呼

また、書籍情報に関しても書籍のカテゴリや、ページ数も取得できるので結構充実してると思います。
しかし、ISBNがなかったり、フォーマットが決まっていないこと（123-xxxx or 123xxxxのようにハイフンがない場合）があるので注意は必要になると思います。
ちなみに、レスポンスはJSON形式で取れます。

今回は実装の容易さと情報量がある程度欲しいことから、こちらを採用しました。


### 楽天ブックス系API
楽天ブックスAPIを利用するためには、事前にアプリ情報の登録をしてアプリIDの発行が必要になります。
用途に合わせてAPIが複数あるので、ややこしいと感じました。
（それぞれレスポンス構成が異なる）

少し触ってみたところ、書籍のページ数が取れないという点もあり、もし必要な場合はほかのAPIと組み合わせる必要があります。

良い点としてはISBNはきちんと取得できて、また、書籍以外の検索にも利用できそうでありました。
GoogleBooksAPIsとの比較はISBNをきちんと管理したいかで判断するとよいと思います。
ちなみに、レスポンスはJSON形式で取れます。

### Amazon Product Advertising API
まず、APIの内容を確認しようとアクセスすると登録からするように案内されました。
また、レスポンスもxmlのみしか対応していないみたいです。

今回は内容を確認する前に登録が必要なこともあり、上記二つと比較して容易に扱えないと判断しました。

## React Hooksで自分がハマってしまったポイントについて
書評WEBアプリではフロントエンドの開発にReactを採用しております。
参考にさせていただいたサイト

- https://overreacted.io/ja/a-complete-guide-to-useeffect/
- https://snamiki1212.com/react-useeffect-pitfall


### useEffect内での非同期関数の書き方
非同期関数をuseEffect内で直接呼ぶことはできない！！！
つまり、下記のような書き方はできないということです。

``` sampleNgUseEffect.js
React.useEffect(async () => {
  await func1();
}, [fun1]);
```
なぜ、できないかといいますと、async で定義された非同期関数は、返り値に Promise オブジェクトを返す通常の関数のシンタックスシュガーだから、という点です。
useEffectに渡す関数の戻り値はcleanup関数だからです。

useEffect内で非同期関数を使う場合は、下記のように非同期関数を定義してから実行しましょう。

``` sampleUseEffect.js
React.useEffect(() => {
  // １）定義
  const func1 = aysnc () => {
    // ...
    await ...
    // ...
  }

  // ２）実行
  func1()

}, [...])
```

### useStateのsethogehoge()は即座に反映してくれない
useEffectと合わせて、内部でsethogehoge()を使うことが多々あると思いますが、今回のNGな書き方を下記に載せます。

``` useStateNg.js
const [userId, setUserId] = React.useState('default');
React.useEffect(() => {
  setUserId('newValue');
  console.log(`userId is ${userId}`); 
}, [userId]);
```

この場合logとして出力されるのは、「userId is default」となります。
なぜかといいますと、その関数が実行された時点のuserIdを使うからです。

setした値を利用したい場合は、変数に置き換えて利用するなどの工夫が必要です。

``` useState.js
const [userId, setUserId] = React.useState('default');
React.useEffect(() => {
  const newValue = 'newValue'; // << ここで変数に入れてあげる

  setUserId(newValue);
  console.log(`userId is ${userId}`); 
}, [userId]);
```

## まとめ
今後、増やしたい機能

- 別のユーザーにこの本おすすめだよといったレコメンド機能
- レビューに対してコメント機能
- 他ユーザーのページ（その人のレビューとかいいねしているのが見れる）
- カテゴリやタグなんかも入れたい

ここまでお読みいただきありがとうございます。今回は局所的であまり参考にはならない内容になってしまいましたが、書籍管理をどうしようと考えている方、React Hooksで苦戦中の方に少しでも参考になればと思います。