筋トレと体重の記録アプリケーションを作って運用している話(とそれの今後の展望)


タイトルの通りなのだが、インスタで毎日投稿している筋トレの実施記録を管理する機能と、ついでに体重の記録を管理する機能をもったアプリケーション自作していて、現状それを運用しているので、その話をしたい。それと、ついでにそのアプリケーションの抱える課題や、今後の展望について考えていることを挙げていきたい。


どんなアプリケーションか

  • 俺が日々実施している筋トレの実施記録と、体重(体脂肪、筋肉量など)の記録を行うための自作のアプリケーション。
  • 基本的にはHeroku上で動いているWebアプリケーション。Node.jsで作っている。詳細は後半「技術スタック」参照。
  • 原則「俺しか使わない」ので、Firewallで我が家のWiFiのIP以外からのアクセスを遮断しているのに加え、ログイン機能を設けて、俺専用にしている。ただし旅行いったときなど、出先で筋トレをせざるを得ない場合は、一時的にFirewallを解除して使う。
  • 画面操作が困難にならない程度には一応レスポンシブ対応を施してあり、PCでもスマホでも使える(少なくとも機能上の支障はほぼない)。実際筋レの開始や終了はいちいちPCまで移動して記録するとタイムロスが数秒発生してしまうので、手元でスマホで登録できるほうが効率が良い。また、旅行先で筋トレする場合など、そもそもPCが手元にない場合に、スマホから登録できないと運用上支障が出ることもあり、スマホ対応が必要だった。

筋トレ

機能概要

  • 筋トレの実施記録をする。実施記録(開始・終了日時や実施回数等)の登録、実施記録の集計(インスタ投稿用)、筋トレのメニュー管理(メニュー名、実施単位(回とか秒とか))などを搭載。
  • 筋トレの連続実施日数の記録(カウントアップ)、及びそれの個別メニュー版を実装。「連続◯◯日」「スクワット連続◯◯日」とかはこの機能の情報をもとにインスタに投稿している。いちいち「前日の数字+1」とかアナログなことしなくてよく、とても助かっている。作って良かった機能の一つ。

スクリーンショット等

俺しか使わない(他人はそもそもアクセスできない)ので載せてもあんまり意味ないんだけど、画面イメージとして、こういうのを使ってるんですよというのを紹介しておきたい。基本的にデザインセンスがないうえ、素CSSで頑張って書いてるので、社内管理画面のテイストにめちゃくちゃ近い。モダン感は皆無である。ただ、俺しか使わないので、別にデザインに拘りはない。こんな程度のシンプルさで問題ない。

筋トレの実施記録画面。メインの機能。左側のプルダウンで筋トレメニューを選んで、プルダウン配下のボタンを押すと記録開始(のための準備)する。同時にボタン名が「終了」に変わる。「終了」を押すと終了時間が刻まれ、同時にボタン名が「登録」に変わる。最後に「登録」を押すとDatabaseに記録が登録される仕組み。
当日実施分に関してのみ、実施時間の降順で画面下部に明細で表示される。各セット間に1分の休憩時間を設けるようにしているのだが、最後に実施した筋トレの実施記録+1分が一目でわかるようにするために設置している。画面上部には1秒ごとに表示が切り替わるタイマーも用意されていて、これで次セットの開始時間がすぐにわかるようになっている。

筋トレの実施結果をメニューごとにサマリする機能。インスタに投稿する画像を生成するために使用する。対象日を指定して「検索」を押すと、データを集計してきて表示する。同時に、筋トレの連続実施日数、メニューごとの連続実施日数も取得する。画面下部の「画像を取得」ボタンを押下すると、表示内容を画像にしてボタン下に表示する。

筋トレ実施記録の編集機能。日を指定して検索し、対象日の筋トレ実施結果を明細単位で全件抽出して編集枠と共に表示する。編集自体もこの画面上で行い、編集完了後、明細右端の「編集」ボタン押下で編集内容を反映させる。なお、スマホではinput type="time"が通用しないため、この画面の筋トレ実施日時項目はinput type="text"で、時刻のvalidationは頑張って自作実装で対応している。早くスマホも対応しないかな…

筋トレメニューの管理機能。筋トレの実施記録をする画面で、対象メニューをプルダウンで表示する部分の、元データを管理する機能。新規に追加したり、削除したり、変更したりできる。プルダウンへの表示順も持っており、これを指定・変更することでプルダウンでの表示順序を制御できる。個人的には「腹」「胸」「腕」「背中」「脚」で1000番単位くらいで表示順をわけており、追加メニューが出た場合も、他メニューの表示順を無理に動かすことなく追加できるよう運用している。画面機能としては上記の「筋トレ実施記録の編集機能」に近い。

体重記録

機能概要

  • 「肉体の記録」シリーズに載せているような体重や体脂肪量、筋肉量の変遷の、もととなるデータを記録する機能。タニタの体重計で体重計測した時に表示される以下項目

    • 体重
    • BMI
    • 体脂肪率及びその相対評価(「やせ」、「標準」など)
    • 筋肉量及びその相対評価
    • 推定骨量及びその相対評価
    • 体脂肪レベル及びその相対評価
    • 基礎代謝及びその相対評価
    • 体内年齢

    に加え、体重×体脂肪率を計算して、その時点の「体脂肪量」を記録する。「体脂肪量」は上記「肉体の記録」シリーズをまとめる際に使用していたので、どうせだからこの場で計算して保持しておこうと思って機能上付け加えた。

  • 誤って登録してしまった場合の編集機能、削除機能も搭載。(ただ削除機能は最初期のテスト段階以降ほとんど使ってない)
  • 加えて、「肉体の記録」シリーズで使える形で、記録をダウンロードする機能を保持。ダウンロード形式はTSVファイル。(テキストエディタで開いて中身をコピーして「肉体の記録」の結果を管理しているEXCELシートに張り付ける運用)

スクリーンショット等

タニタの体重計で計ったときに表示される管理項目(上記)を登録するための機能。タニタの体重計の表示順と同じ順番に上から並んでいて、入力がスムーズに済むように項目配置している。

計った記録を一覧表示する機能。FROMとTOで対象日を指定して検索する。また、上述した通り、結果をTSVでダウンロードする機能も設けている。

余談

  • この「体重記録」機能は、当初は実装されていなかった。というかこのアプリケーション上に同居させるつもりがあまりなかった。というのも、このシステムはあくまで「筋トレ」の実施記録のために用意したものであって、「体重」の記録は本来の目的とは異なると考えたためである。体重の情報を記録していくなら、このアプリケーションとは別の、それ専用のアプリケーションをもう一つ別に用意して、そっちで記録・管理していくべきだという考えがあった。…のだが、「俺個人を認証させる機能」とか「俺以外アクセスできない機能」とか、目的を果たすための前段部分の多くがこの筋トレ記録アプリとほぼ同じで、機能的に被ってるものをわざわざ別に一個作るのか??と考えると非常に抵抗があった。なので、別に変にこだわらなくていいかと思い、合体させた。今となっては、なんでこんなどうでもいいことに拘ってたのかというレベルで、正直なんとも思っていない…
  • もともとEXCELで管理していた「肉体の記録」の運用を、この機能を実装するにあたって最早EXCELから卒業しようと思ったことがあり、実際少しそれで動いたのだが、EXCELで実現できている「グラフ」の生成が、このシステム上でうまくいかず、面倒くさいので諦めた。このためEXCELはEXCELで生かしてある。ただし記録するというより最早グラフを生成するための経由点に過ぎなくなっている(記録管理はこのシステム上で行うため)。TSVファイルダウンロード機能を付けたのはEXCELに転記するためである。今考えてもちょっとイマイチな感じではある。そのうちグラフも含めてこのシステムで完結させたいなあ。
  • 2021年6月~2022年5月くらいまでの記録は、EXCELが正だったので(というかEXCELしかなかったので)、この機能を立ち上げるにあたって、記録管理の主体をこの機能に寄せるため、EXCELからPostgresqlに軽いデータ移行を施した。といってもEXCELの表の欄外にINSERT文つくってそれを流しただけだが。。ともあれ、現状このシステムに、2021年6月にダイエットを開始してからの全ての体重計測結果が保管されている形になっている。

技術スタック

  • 上述した通り、PlatformはHeroku
  • ソースコードはGithubでPrivate Repoで管理している。Herokuと繋げていて、ブランチにプッシュすると自動的にdeployされる。
  • 基本的にはNode.jsのExpressで動いてるWebアプリケーション。テンプレートエンジンにはEJSを使用。また、フロント側ではjQueryを多用。めっちゃガリついている。w
  • インスタに投稿する画像を生成するためにHTML2Canvasというライブラリを使っている。画面機能で指定日の筋トレ結果を集計してきて表示し、HTML2Canvasでそれをそのまま画像にする。その画像をインスタに投稿する…という流れである。
  • ログイン部分ではpassport-localを使用している。(今考えると自作するよりAuth0とかのAddon使ったほうが良かったなと思ってはいる。。Auth0無料で使えるし)
  • セッション管理にHeroku Redisを使っている。ケチなのでhobby-dev plan(無料w)である。別にセッション管理するだけならRedis使わなくても良かったのだが、なんとなく興味本位(好奇心)で使ってみた次第である。
  • データ管理にHeroku Postgresqlを使っている。ケチなのでこれもhobby-dev プラン(無料w)である。あと管理用にちょっとDataclipを使ってたりするが、運用上ほとんど使う機会はない。
  • ORMにSequelizeを使用している。releasesequelizeのMigrationも実施している。余談だが、SQL書きまくってた人間だからか、Sequelizeは(というかORMは)どうも肌が合わない。。実際半分くらいはSQL直書きしてたりする。ORMの意味とは。。。苦労話の一端はここにまとめている。
  • CloudflareをDNSに使用。なお、俺しか使わないからCDN機能は未使用。また、Firewallで我が家のグローバルIP(v4/32、v6/32)からのアクセスのみ許可し、他はアクセスを拒否。これで「俺以外使わない」を実現する。なお、旅行先で使う場合(スマホのキャリア回線経由でアクセスすることになる)は、一時的にこのFirewallを解除する。
  • とはいえ、herokuapp.comに直接アクセスがあるとCloudflareを経由しないのでFirewallが適用されない。このため、herokuapp.comへのアクセスがあったら、カスタムドメインに全リダイレクトするように実装。実質herokuapp.comへの直接アクセスを禁止している。
  • Papertrailを使用し、herokuapp.comへの直接アクセスがあった場合に、俺の個人slack宛てにalertメッセージを飛ばすよう仕組んだ。まあでも運用し始めて1年ちょっと経つが、そんなことしてるやつは今のところ俺しかいないけど。。

課題

  1. そもそもの、前提の話なのだが。生Javascriptで記述されているので、色々自由過ぎる。自由過ぎるって俺が自分で作ったのに何言ってんだって感じなんだが。そこら中で型定義なしのObject生成し、動的にプロパティ足しまくったり、データ層とかモデル層とか無視して渡しまくったり、最早作った本人の俺ですらよくわからんカオス状態になっている。要するに物凄く保守性が悪い…
  2. ケチなのでHeroku Postgresqlのhobby-devプラン(無料)使ってるんだが、このプランはROW LIMIT 10000という制限があり、つまりDatabase全体で10000レコードまでしか持てない。今のところ払拭してないが、過去データを消してないので延々貯まり続けている。これをなんとかしないと…という課題。一応過去の全ての記録はインスタに投稿済なので、そういう意味ではバッサリ消しちゃっても良いんだろうが、インスタに投稿しているのは「筋トレメニューごとのサマリ」であり、各明細レベルでデータが残っているわけではない。この明細レベルのデータをどこかに残しておきたい…のだが、その辺がまだ決まっていない…
  3. 連続日数の取り扱いに仕様考慮漏れがある。このシステムでは実施した筋トレ記録をあとから削除する機能があるが、特定の日の実施記録が全て削除された場合、その日で「連続日数」は途切れるはずだが、その考慮ができていない。実際に全く筋トレをしないまま1日間を空けるとそこで「連続日数」がリセットされるのだが、一度でも筋トレの実施記録を登録してしまうと、そこで連続日数のカウントアップロジックが動作し、あとから削除してもリセットする機構がない。また、リセットされたら、以後全ての日の「連続日数」をそこから再計算しなければならないが、その機構もない。今のところ毎日必ず実施するようにしているため、この課題は制約にはならないが、正確には対応しなければならないポイントである。
  4. 「日」の考え方が機械的に24:00で切り替わるようになっている。つまり、連続日数を絶やさないためには、必ず「その日のうちに」筋トレを実施しておく必要がある。これは筋トレの実施記録の編集画面でも同様で、あとから「日」をUPDATEできなくなっている(あえてそうしているんだが)これは課題3.にあげた内容とも密接に絡んでいて、あとから「その日の記録」が増える/あるいは減った場合に、連続日数のカウントを操作する機構が用意されていないためである。今のところ1年半以上毎日24:00までには完了できているので、直接的に問題にはなってないが、個人的に27:00くらいまでは「今日」なので、機械的すぎて柔軟性がないのが気にはなっている。
  5. データ編集が明細単位にしかできない。要するに一括編集機能がない。たまに複数明細を一括で編集かけたいときがあるのだが、その場合も1明細ずつ編集しなければならず、作業効率が悪い。ただ、これは作ろうと思えば比較的すぐ作れる機能なので、課題っていうほどの課題ではない。残タスクに近い。ただ面倒くさいから手を出していないだけ、というのが理由としては強いwそういうのが欲しくなるケースが、運用上かなり少ないというのも、腰が重い理由の一つになっている。まあ、そのうち…

今後

上記のように、課題はあるのだが、今のところ運用するだけでなら全然不満はなく、むしろ満足しているし、少なくとも「毎日筋トレ」の習慣実現のためには、現時点でそもそも必要不可欠のツールとなっている。まじこれがないと無理。。
また、毎朝計測した体重等の数値を、個人Slackにメモっておいて、あとでExcelに転記するという、実にアナログな運用も、このアプリのおかげで抜け出すことができ、運用がとても軽くなった。
また、ケチな性分が幸いしてか、この構成のシステムを、ドメイン代を除いて完全無料で実現できているのも素晴らしいポイントの一つ。とてもコスパがいい。
何度かインスタのほうでも書いてるが、作って良かったと思っている自作のオモチャの一つ。

だからこそ、特に課題1.のような点が気になっており、「どうせならちゃんとしたものに仕上げたい」という気持ちが大きい。上で挙げたような課題や不満点の対応のためのアイディアはいくつかあり、個人的好奇心から新たに試してみたいポイントもある。

  • 具体的には、個人的にも使い慣れているNext.js+Typescript+Tailwindcssで作り直したい。これだけでも大分保守性の悪さは改善されるはずである。特にTypescriptは上で書いた課題1.を100%完全に解決させること間違いなし。(というかTypescriptを使い始めたからこそ気付いた課題ともいえる)相変わらずデザインにこだわる気はないので、Tailwindは別になくてもいいんだが、素でCSS書くよりはいいはずなので、出来れば使いたい。それに、自分でExpress書くよりはNext.jsのサーバー使ったほうが絶対いいだろうしなあ~。等。
  • 課題2.の対応としては、例えばHerokuのAWS CLI Buildpackを使って、個人AWSアカウントのS3に定期的に(日次とかで)過去データを吐き出して削除していくようにできないかなと目論んでいる。定期実行には、簡易にはHeroku Schedulerが使える(ただこれは必ずしも設定日時に動くことを保証していないので若干使いづらい)し、これじゃなくてもNext.jsでAPI作って露出させ、IFTTTやLambdaでCronを組んでリクエストさせてもいい。やりようはいくらでもある。
  • 上でも書いたように、認証部分を自作実装から脱出したい。いちいち自作するよりは、Auth0などのAddonを利用して実現したほうが絶対運用が楽である。Auth0とNext.jsは別口で実現した例があるので問題なく実装できるし、というかそもそもAuth0が手順公開してるし、やろうと思えばすぐに実現できる。
  • あと個人的な好奇心なんだが、どうせTypescriptで作り直すならPrismaを使ってみたい。名前しか知らないし、Sequelizeより良いとか悪いとかそういう噂を聞いたわけでもないし、そもそもORMにはあまり良い印象がないのだが(上述の通り)、単純に技術的好奇心で使ってみたい。どういう使い勝手なのかというのを知りたい。まあORMである以上は根本的なところではSequelizeとは大きく変わらないんだろうが、知っておいても損はなさそうなので。

「俺だけしか使わないから」という理由で、スモールスタートでかなり適当に始めた開発案件ではあるため、それゆえの課題や問題はまだ残存しているが、一方でその分気付いたことや改善に向けたアイディアはいっぱい出てきていて、夢が広がるなあ~と思わずにはいられない。
「筋トレ」という主目的の補足として開発したアプリケーションだが、これを開発していくことそれ自体が俺の趣味の一環として成立していて、こういう意味でも筋トレやってて本当に良かったと思っている。
今はまだ他のオモチャwを作るのに忙しいので着手できていないが、近いうちに個人的にプロジェクトを立ち上げて、リプレースしたいと思っている。