今年もプロ研が学園祭Webサイト制作をした話

この記事は、[木更津高専 Advent Calendar 2024](https://qiita.com/advent-calendar/2024/nit_kisarazu) 17日目の記事です。
←前 [KotlinでBukkitプラグインを書く](https://zenn.dev/toma09to/articles/f3a9a4062b5bae) by @toma09to 
→次 [祇園祭Webサイト2024のサーバ周りについて](https://zenn.dev/nitkc_proken/articles/d1af2d9aeb029b) by @NXVZBGBFBEN 
# はじめに
こんにちは、木更津高専 プログラミング研究同好会のnaotikiです。
私達、木更津高専プロ研は[昨年](https://qiita.com/naotiki/items/6506ef15a50f1c49b981)に続いて今年度も木更津高専の学園祭Webサイト制作を担当しました。

↓↓↓

https://gionsai.jp

公開バージョンリポジトリ
※本番と全く同じではなく、公開用に認証、ダッシュボード機能などが取り除かれたバージョンです。

https://github.com/nitkc-proken/gionsai-web-2024-public

昨年度は、プロ研が初めてのWebサイト共同開発ということもあり、ノウハウなどが少ない中でもなんとか完成させました。今年度は昨年の多くの反省を活かしてより良いWebサイトを制作できたと思っております。（もちろん、まだまだ改善点はありそう）

この記事では今年度、開発体制や実装で工夫したことを話していきたいと思います。

いろいろ書いてますがこの記事は全て私個人の意見で、木更津高専、プロ研とは関係ありません。 

# 技術スタック
昨年は Next.js でしたが 
今年度は Remix + Hono という構成にしました。
Remixを選んだ理由はいくつかあり、
* 最新のNext.jsをしっかり追えていない
    * 最新のServer Actionsやキャッシュの挙動など、Next.jsについて十分に追えていない部分がありました。認証機能などもある関係で、馴染みのあるフレームワークを使いたかったという理由もあります
* Remixのほうが単純でわかりやすく初学者にも適していると思った
    * プロ研にはWebサイト開発経験のある人からない人まで様々なメンバーがいます。初学者が学ぶことを考えたときにNext.jsには複雑なことがやや多いように感じました

などです。

（ニコニコ(仮)がRemixで作られたとか、「You Don't Need Next.js」とかの影響も多少受けてる）

# デザイン面の超改善

今年度と昨年度で大きく変わったところがあります。 

↓2024

https://gionsai.jp

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357553/b55ac7f4-21b1-0863-80b1-1fd73b665350.png)

↓2023

https://2023.gionsai.jp

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357553/88df624d-322d-e5f6-a188-3da7a0311daf.png)


それは…
デザインがめっちゃいい感じになってる…！
です！

今年度はデザインの知見があるメンバーが参加してくれたため、大変いい感じ(語彙力)な見た目です。
いろいろなサイトのデザインを調べながらどんなデザインが合うかを考えてくれました。@Nimono-sleep-well には本当に感謝です。

> [@Nimono-sleep-well](https://qiita.com/Nimono-sleep-well) のアドベントカレンダー記事、[「Blenderとおでんの違い」](https://qiita.com/Nimono-sleep-well/items/4a3fbdd2202d11ab6200) も是非見てみてください！

もちろん、デザインが凝っている分、実装は少し考える必要がありました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357553/4d2c7918-074f-79ed-3251-5fa3e28ecd70.png)

この左側の文字とかは画像ではなくちゃんとCSSです。
検証ツールで見てみるとわかりますが一文字ずつ`<span>`で囲んで`justify-between`で均等配置してます。

# OGP画像の生成
satoriとsharpを使って生成しています

https://gionsai.jp/projects/A-07

https://github.com/nitkc-proken/gionsai-web-2024-public/blob/main/packages/datasource/src/preprocessor/ProjectOGP.tsx

# Remix SPAとCloudflare Pagesを活用した自動プレビュー生成
PRのレビューをするときにいちいち自分の環境でビルドして確認したり、スマホなどの複数端末から確認するのが面倒だったため、PRを作成すると自動でWebサイトのプレビューが作られるシステムを構築しました。

GitHub ActionsとCloudflare Pagesを使って構築しました。

https://github.com/nitkc-proken/gionsai-web-2024-public/blob/main/.github/workflows/cf-pages-pr-preview.yaml

PRにpreviewタグを付けるとSPAが自動で生成されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357553/a6c19cc7-b281-9a38-7f7c-af2d7c3debe1.png)

無事にビルドが終わるとPRにURLがコメントで投げられます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357553/0839a457-d320-d0e5-b250-a7bc1577d1e9.png)

ちなみにプレビューはCloudflare Accessでアクセス制限をしっかりかけてあります。

SPAプレビューはデザインの確認を主な目的としているため、APIを使用している部分はモックに切り替わります。

仕組みは単純で、ビルド時にコードを書き換えるViteプラグインを作成しました。

`loader`, `action` を `clientLoader`, `clientAction`に書き直したりモックを使うフラグを`true`にしています。
```js
export function previewSPA(env: Record<string, string>): Plugin {
  let config: ResolvedConfig;
  const remixAppDir = "./app";
  const absPath = normalizePath(path.resolve(remixAppDir));
  const project = new Project();
  return {
    name: "auto-client",
    configResolved(resolvedConfig) {
      // 解決された設定を保存
      config = resolvedConfig;
    },

    transform: {
      order: "pre",
      handler(src, id, options) {
        const shortPath = id.replace(absPath, "");

        if (!shortPath.startsWith("/routes/") && shortPath !== "/root.tsx")
          return;

        const source = project.createSourceFile(`${id}.tmp.tsx`, src);
        if (shortPath === "/lib/utils/mock.ts") {
          // replace PUBLIC_ENV.USE_MOCK === "true" to true
          source
            .getVariableDeclarationOrThrow("isUsingMock")
            .setInitializer("true");
          return {
            code: source.getText(),
            map: null,
          };
        }

        source.getFunction("loader")?.rename("clientLoader");
        source.getFunction("action")?.rename("clientAction");

        if (shortPath === "/root.tsx") {
          source
            .getFunctionOrThrow("Layout")
            .getVariableDeclarationOrThrow("env")
            .setInitializer(JSON.stringify(env));
        }

        const modified = source.getText();
        return {
          code: modified,
          map: null,
        };
      },
    },
  };
}
```

SPAプレビューを導入した結果として、様々な機器での動作確認が非常に容易になり、作業時間を短縮することができました。

# 構内地図
今年度から、Leafletを使った構内地図が追加されました。

https://gionsai.jp/map

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357553/2fca5cc3-b3cd-56bf-6abb-7756d3d63547.png)

建物の座標と地図画像の名前、属する企画のIDをひたすら**手作業**で打ち込みました。
時には根性も大事ですね。 
```yaml:map.yaml (一部)
# yaml-language-server: $schema=map.schema.yaml
- building: 総合教育棟
  x: 1529
  y: 418
  floor:
    - name: 1階
      image: 00_sougou_1.webp
      projects:
        - F-02
        - F-03
        - F-04
        - F-05
        - Z-00
    - name: 2階
      image: 01_sougou_2.webp
      projects:
        - B-02
        - B-03
```

# おわり
今年で2回目となった学園祭Webサイト制作ですが、昨年度と比べてとても良くなったな・・・と個人的に思っています。
デザインいいね！などの褒め言葉もたくさん頂き、とても嬉しかったです。（私がデザイン考えたわけではないですが！ありがとう！ @Nimono-sleep-well）

まだまだ改善点もあるので、来年はもっと進化したものをお見せできたらいいな・・・と思っています。

プロ研開発メンバーのみんなはもちろん、アドバイスをくださった木更津高専 学園祭実行委員会の皆様、使ってくださった来場者・企画出展者の皆様、ありがとうございました！

> P.S. 記事投稿めっちゃ遅くなってごめんなさい・・・1日勘違いしてました・・・