NestJS・OpenAI APIを使ってオススメ観光地提案サービスを作ってみた

## 作ろうと思った経緯

前回の[WEBマンガ更新情報まとめアプリを作ってみた](https://qiita.com/1seedsa/items/b8dc5f572758636885f2)では会社のメンバーで個人開発をやっていこうというイベントで個人開発アプリを作りましたが、今回はメンバー数名でチーム開発でNestJSと生成AIを使用したアプリを作ろうというイベントがあり、そこで作成しました。

:::note info
株式会社mofmofは受託開発会社ですが、メンバー自らがオーナーとなってプロダクト開発にチャレンジしています。
この記事は、NestJS, 生成AIにチャレンジする「金曜日のチーム開発」の成果です。

https://indie-dev.mof-mof.co.jp/
:::

## 技術スタック

- NestJS 10.0.0
- Next.js 13.5.4
- OpenAI API gpt-3.5-turbo-1106
- GoogleMap API
- 楽天トラベル地区コードAPI

## どんなものができたか

検索条件を指定して検索するとAIがおすすめの観光地を提案してくれる、AI旅行プランナーというサービスです。プロダクト自体の内容についてはメンバーが別で記事を書いてくれていますのでそちらをご参照ください。

### AI旅行プランナー

https://trip-planner-frontend-7737db558800.herokuapp.com/

### プロダクトの記事

https://note.com/m_takei/n/na87df8320c22

## 全体的な構成

- フロントエンドはNext.js、バックエンドはNestJSを使い、それぞれをHerokuにデプロイ
- 検索条件の一つである各都道府県のエリア情報の取得のために楽天トラベル地区コードAPIを使用
- 検索条件として指定された条件をOpen AI APIに投げ、Open AI APIから帰ってきたおすすめ観光地の名前をGooglMap APIに投げて詳細情報を取得して表示する

## 開発時のあれこれ
### バックエンドのNestJSについて

- NestJSを使うのが初めてだったため、そもそもどういうものなのかを理解するところからスタートした
- バックエンドのAPIとして使う用途のため、REST APIモードで使用
- 「NestJSだとバックエンドもフロントエンドもTypescriptで書けるので脳みその切り替えが少なくて楽」というのをよく聞くが、今回はNestJSが初めてだったのでそもそものキャッチアップ部分で脳みそを使ってしまってる感じだった
    - やり慣れればその恩恵をもっと感じられると思う

### フロントエンドのNext.jsについて
- React自体は業務でも使用していて、かつ今回のUIはシンプルな感じだったので基本的にはそんなに手間はかからなかったが、少し手間のかかったところもあった
- 後述する楽天トラベル地区コードAPIから取得したエリアを都道府県選択時に自動でセレクトボックスに反映させる処理
    - コンポーネント間でのデータ受け渡しをする中でセレクトボックスの値が画面上の表示と合わなくなるなど、一部想定していない動きをしてしまっていた

### OpenAI API関連の処理について
- 実際にデプロイしてみるまでは、AIから回答が返ってきて処理が完了して画面表示できるまでどれくらいかかるか分からなかったため、当初は最悪数十秒程度かかるかもしれないという想定だった
- 待ち時間を退屈させないためにコンビニのコピー機の液晶画面に待ち時間で表示されるような間違い探しを表示させる案で進めていたが、実際にデプロイしてみたところ5秒程度で画面表示できた
- 間違い探しをするほどの時間は無かったので、AIの回答待ちの画像を表示させる形とした
    - こういうAIの回答待ちのアナウンスが表示されてしばらく待たされると「ああ、このサービスってAI使ってるんだなあ」って感じる。僕だけかもしれないw
    - でも実際こういうアナウンスなしにパッと動作してたらサービスによってはAI使ってるっていうこと自体に気づかないかもしれない
    - 最近のAIブームではAI使ってる感をこういうところでアピールするのも重要なのかも

<img width="400" alt="楽天トラベルの画像" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159896/4b89a240-ed5c-61af-4085-41d4e78bb3ba.png">

### GoogleMapAPI関連の処理について
- 元々はOpenAI APIで観光地の情報まで取得することも検討したが、情報の正確性の懸念や全ての観光地で必要な情報を取得することが難しそうだったため、OpenAI APIではオススメ観光地の名前のみを取得
- その観光地の名前をGoogleMap APIに投げ、観光地の詳細情報についてはGoogleMap APIから取得
- 結果一覧の各観光地情報のカードには実際のGoogleMapの該当箇所に飛べるリンクを設定

### 検索条件について
- 都道府県の指定に加えてもう少し詳しいエリア指定については市区町村を指定する形にするか？という案も出たのですが、観光地を調べたい時はエリア指定の方が分かりやすいと判断
- 参考にした楽天トラベルと同様の形にするために楽天トラベル地区コードAPIを使うことにした

https://travel.rakuten.co.jp/chiba/

<img width="400" alt="楽天トラベルの画像" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159896/cb6dbb20-7dda-1a6f-a272-ddb61fe6d6b6.png">

### 楽天トラベル地区コードAPI関連の処理について
- 都道府県を選択すると自動でその都道府県のエリアがセレクトボックスに入る仕様
- 都道府県を選択する度に楽天APIにリクエストを投げるのでは動作がもたつく・APIリクエスト回数が増える・APIリクエストエラーの可能性も増える、という懸念があった
- 画面表示時にAPIに一度だけリクエストして日本全国のエリアをまとめて取得してリストにしておき、都道府県選択時にはそのリストから都道府県名で各エリアを取得する形にした
- 楽天トラベル地区コードAPIから取得したデータを使いやすい形に整形する処理が入るので画面の初期表示時にそこで表示時間が取られる懸念もあったが、デプロイして試してみたところ表示速度に問題は無かったので良かった

<img width="400" alt="楽天トラベルの画像" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159896/4028b1fb-a97d-8caa-cfb7-a13de53aa438.gif">

## 所感

- 生成AIやNestJSについては今まで使ったことのない技術だったため、新しい技術に触れられて勉強になりました
- 楽天APIは以前の個人開発でも使いましたが、無料でサクッと使えるので使い勝手が良いなあと思います。今後も何か情報取得したい時には楽天APIを使えるかをまず考える気がします
- OpenAIで全てが出来るわけではないですが、作りたいものの情報を上手く取得する、という程度の使い方であれば非常に有益に使えるものであると感じました
- GoogleMapAPIは今回初めて使いましたが、無料で使えて今回のように必要な情報を補完するのに有益だと感じました。必ずしもGoogleMapに紐付けなくても、何かしら場所の写真だけを使いたいとか、公式HPの情報だけ取得したいとか、使い道は色々あると思います