ダイエットに欠かせないチェーン店のPFCバランスを検索できるサイトを作った

こんにちは。[ぬこすけ](https://twitter.com/nuko_suke_dev)です。

皆さんは **PFCバランス** という言葉を聞いたことありますか？
トレーニングやダイエットをしている方はこのPFCバランスを意識して食事を摂っている人も多いかと思います。

PFCは三大栄養素の **P**rotein(たんぱく質)、 **F**at(脂質)、 **C**arbohydrate(炭水化物）の略です。
この **PFC をバランスよく摂ることで健康を維持するのはもちろん、筋肉をつけたり体重を減らしたり、理想の身体を作っていく** わけです。

![macho_man.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164159/a9bd4c89-c6ea-336a-420d-f36a6f7a14f4.png)

外食でPFCバランスを管理するのはなかなか難しいものです。
現在地近くに、例えば吉野家やCoCo壱などいくつか飲食チェーン店があっても、「どの飲食店のどのメニューが自分が管理しているPFCバランス的に良いのか」がすぐわかりません。

そこで **飲食チェーン店のメニューを一覧化し、栄養素ベースで検索できる「ちぇいさー」というサイトを作りました！**

https://chaisear.com/

ちぇいさーでは例えば「タンパク質 20 g 以上、かつ炭水化物 10 g 以下」のように栄養素ベースで横断的に飲食チェーン店のメニューを検索できます。
PFC だけでなく体づくりに欠かせない **カロリーや塩分での検索もできます** 。

この記事では個人開発の参考になるような **サクッとできるニーズ/競合の調査方法や、コストを抑えた技術スタック** を紹介したいと思います。

## 企画の背景

サクッとできるニーズ/競合の調査方法を紹介する前に、[ちぇいさー](https://chaisear.com/)を企画した背景について簡単にお話させてください。

私は筋トレが趣味で、ジムに行くのはもちろん、 Youtube で筋トレ系 Youtuber さんの動画もよく見ています。

https://youtu.be/cW1vmmGRogY

（個人的によく見る山澤さんという Youtuber の方の動画）

ダイエットやトレーニング系の動画でよく **PFCバランス** という言葉を聞くようになりました。
例えば「ローファットダイエット（脂質を抑えてタンパク質と炭水化物を摂るダイエット）」や「ケトジェニックダイエット（炭水化物を抑えてタンパク質と脂質を摂るダイエット）」という、 PFCバランスをベースとしたダイエットがあるそうです。

自宅で料理する場合は自分で食材を選べるのでPFCバランスによるダイエットはしやすいですが、外食する場合は難しいのではないかなーと思います。

![computer_search_kensaku.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164159/906c4187-af2c-b98b-6b31-379d718f7186.png)

どこのお店でどのメニューが脂質が少ない、などといった情報を事前に探しておく必要があります。
例えば、「できるだけ脂質が低い牛丼を食べたい」となったときには吉野家や松屋、すき家のメニューをそれぞれ調べておく必要があります。

全ての飲食店でメニューの栄養素を公開しているわけではありませんが、チェーンの飲食店であれば公式ページに公開されています。

このような背景から、身近な飲食チェーン店のメニューを集めて、横断的に炭水化物やタンパク質、脂質などを検索できる[ちぇいさー](https://chaisear.com/)を開発しました。

## サクッとできるニーズ/競合の調査方法

「飲食チェーン店のメニューで炭水化物やタンパク質などといった情報を横断的に検索できるサイト」というコンセプトは固まったので、ニーズ/競合の調査です。
Web マーケティング会社で働いていた経験もある著者がサクッとできるニーズ/競合の調査方法を紹介したいと思います。

### サクッとできるニーズの調査方法

ニーズを調べる方法の 1 つとして、「 **Google でどのくらい検索されているか**」というのがあるでしょう。
検索キーワードを調べるツールとしては [Ubersuggest](https://neilpatel.com/jp/ubersuggest/) や[キーワードプランナー](https://ads.google.com/intl/ja_jp/home/tools/keyword-planner/)があります。

PFCバランスを気にしており、飲食チェーン店のメニュー調べるユーザーがどのようなキーワードで調べるか想像してみてください。

![pose_souzou_woman.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164159/7fc3ceca-4df7-46b5-3125-a34ce98b6ce5.png)

例えば「吉野家 脂質」や「吉野家 牛丼 脂質」、「吉野家 脂質 少ない」といったキーワードが挙げられるでしょう。
執筆時点で Ubersuggest で調べた結果、月間の検索数で「吉野家 脂質」は 210, 「吉野家 牛丼 脂質」 は 90 , 「吉野家 脂質 少ない」は 70 ありました。
ある程度ニーズはありそうです。

このような要領で他にキーワードも調べていき、 **Google での検索数でニーズを調べます** 。

### サクッとできる競合の調査方法

[サクッとできるニーズの調査方法](#サクッとできるニーズの調査方法)でユーザーの検索キーワードを洗い出しました。
次に **実際にその検索キーワードでググってみます** 。

試しに「吉野家 脂質 少ない」でググってみましょう。
執筆時点では、吉野家のメニューを調べて脂質が少ないものを紹介しているブログ系の記事が上位表示されています。

私がコンセプトとして考えている「飲食チェーン店のメニューで炭水化物やタンパク質などといった情報を横断的に検索できるサイト」はないため、仮に Google 検索での集客を考えた場合は上位表示に食い込む可能性はありそうです。
その理由は以前私が公開した次の記事で詳しく説明しています。

https://qiita.com/nuko-suke/items/652366597ce2ce0a91f9#%E3%83%86%E3%83%83%E3%82%AF%E3%83%96%E3%83%83%E3%82%AF%E3%83%A9%E3%83%B3%E3%82%AF%E3%81%AE%E3%81%99%E3%81%94%E3%81%84%E3%81%A8%E3%81%93%E3%82%8D

このように **実際にググってみることで競合調査ができます** 。

## 技術スタック

さて、ここまでサクッとできるニーズ/競合の調査方法を紹介しました。
次に費用や時間といったコストを抑えた技術スタックについて、フロントエンドとバックエンドに分けて紹介します。

### フロントエンド

#### バックエンドにデータアクセスをさせない仕組みを構築

個人開発の 1 つの悩みとしては「 **バックエンドに対するアクセスをどう抑えるか？** 」というのがあるでしょう。
特に個人開発ではあまりコストはかけられないですから、バックエンドへガンガンアクセスが来ると、ガンガン費用がかさんでしまいます。

![money_fly_yen.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164159/b5f61c5a-2820-f337-446b-9938f3345dbb.png)

[ちぇいさー](https://chaisear.com/)では飲食チェーン店メニューのデータに対して「タンパク質 5g 以上」みたいな条件で検索をかける機能があります。
一見、「バックエンドのデータベースにアクセスして SQL で検索かけているのでは？」と思われるかもしれませんが、 **一切バックエンドにアクセスしていません** 。

ではどうやってバックエンドにアクセスせずデータを検索できる仕組みを作っているのか。
ポイントは次の 3 つです。

1. メニューデータを事前に静的ファイルに出力
2. `AlaSQL` を使った SQL 検索
3. WebWorker を使ってパフォーマンス最適化

##### 1. メニューデータを事前に静的ファイルに出力

[ちぇいさー](https://chaisear.com/)は `Next.js` を使って静的なサイトとしてビルドしていますが、 **ビルド前にメニューデータも静的なファイルとして出力** しておきます。

簡単に `package.json` の例を書きます。

```json-doc
  "scripts": {
    // ...
    "prebuild": "node ./scripts/createMenusData.js",
    "build": "next build",
    // ...
  }
```

`Next.js` を使っている場合は `next build` コマンドでビルドをすることが多いでしょう。
この `next build` コマンドの前に `node ./scripts/createMenuData.js` で **メニューデータを取得し、 JSON ファイルに出力するスクリプトを実行** します。

余談ですが、 `package.json` の `scripts` で **pre**XXX と登録しておくと、 XXX のコマンドの前に自動で実行されます。
この `package.json` の例では、 `npm run build` を実行すると `npm run prebuild` も実行されます。

そして、出力した JSON ファイルをアプリケーションのコードで `import` します。

```javascript
import menus from '~/data/menus.json';

// JavaScript のオブジェクトして使える！
menus.forEach(({ name }) => console.log(name));
```

このように **ビルド前に必要なデータを静的ファイルに出力し、アプリケーションで直接読み込む** ことでバックエンドに対してデータ取得のためのリクエストを抑えることができます。

##### 2. `AlaSQL` を使った SQL 検索

[メニューデータを事前に静的ファイルに出力](#1-メニューデータを事前に静的ファイルに出力)してアプリケーション内で JSON データを参照できるようになったものの、検索機能の実装はどうでしょうか？

「タンパク質が 20g 以上かつ脂質が 15g 以下かつカロリーで昇順ソート」みたいに色々な条件で JSON データを検索する機能を実装するのはかなりしんどそうです。

できれば **JSON のデータに対して SQL を投げて検索結果が返ってくるような、データの検索部分の実装を省略し、加えて直感的にもわかりやすい実装にしたい** ところです。

これを実現するのが `AlaSQL` です。

https://github.com/AlaSQL/alasql

この `AlaSQL` を使うことで SQL ライクにデータを検索することができます。

```javascript
import menus from '~/data/menus.json';
import alasql from 'alasql';

const sql = 'SELECT * FROM ? WHERE protein >= 20';
// タンパク質が 20g 以上のデータが返ってくる！
const response = await alasql(sql, [menus]);
```

`AlaSQL` を使うことにより **データ検索のためにバックエンドにリクエストすることなくフロントで検索機能を実装できました** 。

##### 3. WebWorker を使ってパフォーマンス最適化

さて、ここまでの「[メニューデータを事前に静的ファイルに出力](#1-メニューデータを事前に静的ファイルに出力)」と「[`AlaSQL` を使った SQL 検索](#2-alasql-を使った-sql-検索)」のお話でバックエンドにアクセスすることなく飲食チェーン店のメニューデータを取得し、検索することができました。

しかし 1 つ問題があります。それは **パフォーマンス** です。

[ちぇいさー](https://chaisear.com/)は UX を重視し、ユーザーの入力に対しリアルタイムでメニューを検索します。
ですが、膨大なメニューデータに対して頻繁に検索をかけるのはあまりパフォーマンス的によろしくありません。
ましてや今後データを増やした場合はなおさらです。

そこで **WebWorker** の出番です。

https://developer.mozilla.org/ja/docs/Web/API/Web_Workers_API

**WebWorker を使って別スレッドを立てて処理させることで、描画などのメインスレッドの処理をブロッキングされることを防ぎます** 。

[ちぇいさー](https://chaisear.com/)では WebWorker 内にメニューデータを保持しておき、 `AlaSQL` を使った SQL 検索も WebWorker 内でやっています。

パフォーマンス最適化の観点で WebWorker を紹介しましたが、 **アーキテクチャ的な面でもメリット** があります。
データアクセスの部分を WebWorker で隠蔽することで、仮に「やっぱりデータの取得や検索をサーバー側でやる」となったときにはアプリケーションへの影響を抑えて移行することができます。

![スクリーンショット 2023-01-09 14.48.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164159/000fce10-1cef-0cda-8742-a70e5c0b77fc.png)

簡単な全体像です。
基本的にアプリケーションは検索条件などのパラメータを送信して、結果を取得するだけの実装です。
データアクセスするための SQL 文の構築や `AlaSQL` ミドルウェアの使用などをアプリーケーション側で実装してしまうと、「やっぱりデータの取得や検索をサーバー側でやる」場合はアプリーケーション側に影響が出るのは想像できるでしょう。

話は少しそれましたが、 **WebWorker を使ってパフォーマンス最適化する** ことができます。
（アーキテクチャ的なメリットも得られます！）

#### Vercel より Netlify

[ちぇいさー](https://chaisear.com/)は `Next.js` を使っています。
`Next.js` を使うのであれば、 `Next.js` の開発元である [Vercel](https://vercel.com/) を使ってデプロイするのが楽でしょう。

ですが、 Vercel でのデプロイは 1 点問題があります。
それは **無料枠だと商用利用が禁止** されている点です。

個人開発者にとってマネタイズしたいなと思うのは当たり前でしょう。
ちぇいさーもなんらかの形で将来的にマネタイズしたいなぁとは考えています。

そのため Vercel ではなく [Netlify](https://www.netlify.com/) を選択しました。
**無料で使えますし、商用利用もできます** 。

その他、無料枠で使える代表的なデプロイ先として [firebase](https://firebase.google.com/?hl=ja) もありますが、認証やデータストアなど充実した機能はいらなかったので Netlify にしました。

### バックエンド

#### 手軽に始めるなら Google Apps Script が最強

**個人開発でバックエンドを構築する際に Google Apps Scriptも有力候補の 1 つ** だと思っています。

無料で Web API を公開できますし、データベース（という名の Google スプレッドシート笑）も使えます。
テーブルが 1 つしかない単純なデータ構造であれば Google スプレッドシートで十分です。

Google Apps Script の 1 つのデメリットとしてローカル開発のしにくさがあるかと思います。
ブラウザで Google Apps Script の管理画面を開いて直接 JavaScript を書くイメージも持っている方もいらっしゃるのではないでしょうか？

![gakkari_tameiki_man.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164159/347e7d1a-bf3e-1097-f611-e7e3a088c08a.png)

**そんなイメージは捨ててください** 。
今や Google Apps Script の Developer Experience は高いです。

Google Apps Script のコードを `git` で管理したり、 CLI ベースでデプロイしたり、 TypeScript で開発したりできます。

Google Apps Script での開発で欠かせないツールは Google が開発している `clasp` でしょう。

https://github.com/google/clasp

`clasp` を使うことで CLI でローカルのコードをデプロイできたり、 TypeScript での記述をサポートしてくれたりします。

その他、 Google Apps Script の開発で便利なツールを紹介しておきます。

- TypeScript の型定義
  - [@types/google-apps-script](https://www.npmjs.com/package/@types/google-apps-script)
- ESLint
  - [eslint-plugin-googleappsscript](https://github.com/selectnull/eslint-plugin-googleappsscript)
- webpack
  - [gas-webpack-plugin](https://github.com/fossamagna/gas-webpack-plugin)

#### データは気合いの手入力

エンジニアならデータの入力もスマートにプログラミングで解決したいですね。
[ちぇいさー](https://chaisear.com/)はもちろん **手入力** です。

![pose_english_wow_man.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164159/8fa8914b-27cc-2722-e984-94632d4648e6.png)

吉野家や松屋などは「どのメニューにどのくらいのカロリーや炭水化物などが含まれているか」を PDF で公開しています。

https://www.matsuyafoods.co.jp/matsuya/pdf/211221_nutritional_matsuya.pdf

この PDF を見ながら Google のスプレッドシートに転記しているわけです。
先ほどの「[手軽に始めるなら Google Apps Script が最強](#手軽に始めるなら-google-apps-script-が最強)」でもお話しましたが、データベースには Google スプレッドシートを使っているので、もし PDF 側であれ私自身であれ記述ミスがあってもすぐ直すことができます。

エンジニアだったらデータの登録もプログラムで解決したいところですが、できれば時間をかけずに早いうちにリリースしたいところです。
早めにリリースしたい理由はたくさんあるのですが、以前個人開発したときに公開した記事にも載せているので興味があればご覧ください。

https://qiita.com/nuko-suke/items/652366597ce2ce0a91f9

まあとは言っても私もエンジニアの端くれです。
最初にリリースしたときは手入力でのデータ登録でしたが、今は Python で PDF の読み込みも一部しています。
今後、データを拡充するためにプログラムで色々とデータを収集予定です。

## まとめ

飲食チェーン店のメニューを一覧化し、栄養素ベースで検索できる「[ちぇいさー](https://chaisear.com/)」を通して、サクッとできるニーズ/競合の調査方法や、コストを抑えた技術スタックを紹介しました。

もしダイエットやトレーニングをしている方はぜひご活用ください！今後も飲食チェーン店を増やす予定です！

https://chaisear.com/

今回の記事の他にも個人開発の企画や技術スタックを紹介した記事もあるので、興味ある方はこちらも覗いてみてください。

https://qiita.com/nuko-suke/items/652366597ce2ce0a91f9

https://qiita.com/nuko-suke/items/8765118a3020692083c2

本日 Zenn でも記事を投稿しているので、 OSS に興味ある方はこちらもぜひ！

https://zenn.dev/nuko_suke_dev/articles/a02ebfb0b5ab4b

ここまでご覧いただきありがとうございました！ by [ぬこすけ](https://twitter.com/nuko_suke_dev)