Cloudflare Workers & Pagesを活用して大学にデジタル短冊を設置した話

:::note warn
このブログは本企画のシステムを開発した「◎部」の担当者が個人的に記述したものであり，中央大学の公的な文書ではありません。システムについて大学へのお問い合わせはお控えください。
:::

こんにちは。中央大学経済学部の[mizphses](https://twitter.com/mizphses)です。

中央大学のキャンパス内にある画面を使って"笹と短冊"もどきを作ってみたのでご紹介します。

https://x.com/itl_marubu/status/1808362557788639578

# 経緯
中央大学の市ヶ谷田町キャンパスには[国際情報学部](https://www.chuo-u.ac.jp/academics/faculties/itl/)というのが入っています。ここの友人と駄弁っていたところ，七夕の笹を飾りたいという話になり，それが盛り上がった結果デジタル七夕をやることになってしまいました。

友人その他各位のご尽力の結果，実際に開催の承認が取れてしまったので，7/3から運用を開始しています。

# 要件
実装にあたって，どのように短冊・七夕の体験をデジタルに落とし込むかを考えました。

## WEBシステム部分の要件
大まかに七夕の短冊と笹を考え直すと
* 短冊という投稿
* 笹に飾るという表示機能

という感じで，基本的なバックエンドの設計はブログとそう変わらないものになるかと思います。（なんなら投稿自体が定型なので掲示板より簡単かも）

また，大学の通路という公共の場に設置する以上，不適切なワードを回避する必要があるため，NGワードを大量に設定し運用しています。また，NGワードも万能ではないため，運営チームの職権で不適切なものは排除できるようにする必要があります。

その他，UI設計周りになりますが，それっぽさのために笹のイラストに短冊がいくつかくっついている風な画面が望ましいのだろうと思いました。

<a data-flickr-embed="true" href="https://www.flickr.com/photos/pv9007/28815551907" title="Tanabata in Kibune / 貴船神社の七夕"><img src="https://live.staticflickr.com/940/28815551907_0d45de6d07_6k.jpg" width="6016" height="4016" alt="Tanabata in Kibune / 貴船神社の七夕"/></a><script async src="//embedr.flickr.com/assets/client-code.js" charset="utf-8"></script>
↑よくある短冊と笹の例。

![イメージ図。笹に短冊風のイラストがついている。](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731800/2f3a8b36-c292-5a06-c74d-94b220cb78fa.png)
↑企画者にシェアした画像。本当は操作できるようにしようとか思っていた。

## ハードウェア
現実世界にコンピュータ上で作成された画像などを置くなら，画面然りホログラム然りなんでもいいので投影装置が必要です。ただ，大学という場所柄プロジェクターや画面は結構いっぱいあります。電子黒板のように可搬式の装置もいくつかあります。

厳守すべき要件として大学との交渉の都合上，何かしらの方法で夜間は画面の電源を切る（スリープ状態）必要があります。

### OS（フォント）の問題
クライアント端末の制約の話なので一旦ハードの項目へ入れてます。

画像生成はCanvasでやったのですが，Canvasの描画をフォントのダウンロードまで待つ実装を書くのが面倒だった（軽く調べたところ字幅で判定するとからしい）こともあり，それなりに高品質でカバーする文字数の多い「HG正楷書体PRO」を一部で使用することになりました。この都合からWindowsが動くクライアントPCも必要です。

# 技術的な構成
原則としてCloudflareの提供する開発者向けサービスを存分に活用しています。具体的にはWorkers, KV, D1, Pagesあたりを使用しています。

本当はFunction Directoryを使いたかったのですが，勉強時間が足りず一旦バックエンドとフロントエンドは完全に分離してFetchでやり取りすることにしました。

## バックエンド
Cloudflare Workersを実行環境としました。安くていいですね。

サーバーの実装には[Hono](https://hono.dev/)を採用しています。軽いし速いしオススメです。HonoのMiddlewareにjwt認証用のライブラリもあり，最低限のアクセス制限を設けるのにも便利です。（実際には利用者情報の取得などを別途書いていますが……）

Cloudflare D1をDBとして使用しています。[PrismaがD1を公式にサポートしている](https://www.prisma.io/docs/orm/overview/databases/cloudflare-d1)ため，ORMにはこれを採用しました。


バージョン管理とデプロイを容易にするため，GitHub Actionsを活用してリリースタグとデプロイを自動化しました。ワンクリックで公開作業が進みます。[Wrangler Action](https://github.com/cloudflare/wrangler-action)様様です。

![CI画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731800/f6037054-497d-b1cb-cd56-e58fd22a58d7.png)


## フロントエンド
最近業務で使っているというのもあり，会場表示用ページ，管理用ページ，投稿ページの全てをNext.jsのApp Directoryで実装しました。

また，デプロイ先はCloudflare Pagesにしました。これも安いからというのが正直な理由ですが，そのほかCloudflare Accessとの統合が非常に簡単なため，セキュリティを確保しやすいというのも理由です。今回は管理用サイトと投稿サイトを分離しましたが，Cloudflare Accessを使えばパス単位でアクセス管理できるので統合しても良かったかもしれません。

Cloudflare PagesはWorkers上で動いているという性質上，ダイナミックルーティングをするにあたり[Edge Runtime](https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#edge-runtime)を使用する必要があります。概ね問題はないんですが，LocalStorageとかを触ろうとするとたまにおかしくなるようです。（JotaiのatomWithStorageとか動かなかった。詳しい人詳細を教えて...）

## ハードウェア/PC
> 画面然りホログラム然りなんでもいいので投影装置が必要です。

実際に透明スクリーンのプロジェクターもお借りしてみましたが，最終的に透明ディスプレイは使用しませんでした。

![透明ディスプレイ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731800/40652446-3854-b150-1399-572f2bbf6bfb.png)

理由としては...
* QRコードの読み取りに支障があること
    * この画像では背景が黒いので十分撮影できそうに見えますが，実際の設置場所は通路なので怪しい。そもそも輝度が足りないかも……
* 設置可能な場所は結構暗くないといけないがその要件に足る場所がない
    * 暗い場所は自習スペースしかなく，そこでビカビカと光らせると迷惑

といったところになります。

> 何かしらの方法で夜間は画面の電源を切る（スリープ状態）必要があります。

赤外線光を遠隔で発射できる装置（[SwitchBot](https://www.switchbot.jp/products/switchbot-hub-mini)の類）を設置させていただき，画面の電源を管理することになりました。

> Windowsが動くクライアントPCも必要です。

以前アキバの最終処分場で購入した[ARROWS Tab Q506/NE](https://jp.fujitsu.com/platform/pc/product/arrowstab/q506ne/)というWindowsタブレットを使用しました。選定理由はほぼ使っておらず実質的に死蔵在庫だったため，盗まれても痛手がないというのが理由です。Officeのライセンスは大学提供のものを使用しています。

ただしできる限り悪いことに使われないよう，ペアレンタルコントロールでガッチガチに制限をかけています。

# 実運用
7/3に設置作業と若干の修正を行い，正午ごろ運用開始しました。私は多摩キャンパスで講義に出ないといけなかったため，その後のチラシ貼り作業などは他の運営スタッフの方にお願いしました。

![設置完了時の風景](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1731800/da24afa5-2a23-43e8-6456-39b992022fbe.png)

運営開始後から早速目を引いたようで，教員・職員の方を含め触っていただけました。初日でGoogle Analyticsデータによれば300PV，48件ほどの投稿をいただけたことになっています。共有機能を投稿画面につけておいたので，一部の方（特に先生）にはツイートもしていただけました。

https://x.com/iiojun/status/1808356829342453776

七夕当日に確認したところ150枚を超える短冊が投稿されており，学生が600人近くいる中でそれなりの人数にご参加いただけたのは非常にありがたいことと感じました。

多くの教職員の方からも助言や運営形式について助言をいただくことができました。このスピード感で実際に運用するための仕組みづくりや承認，アドバイスなど多岐にわたってご支援いただきました。改めて技術的・視覚的に面白いものでもその実現には政治力とかが大事だってことを実感しました。

# 謝辞
企画責任者[タムおじ](https://twitter.com/tamu_oji)をはじめ，推進に協力いただいたiTLその他部局の学生・教職員・事務の皆様に感謝申し上げます。


# 宣伝
「◎部」は市ヶ谷田町キャンパスを盛り上げるイベントの企画・運営を行っています。以前には（というか当団体の設立時には）生ハム原木をみんなで食べるイベント 通称「原木祭」を実施しました。今後の活動にもどうぞご期待ください。

https://x.com/itl_marubu