ソース解説(1) 全体の地図と、境目の引き方
このサイトの作りについては何度か書きましたが、中のコードそのものには触れていませんでした。
ここから何本かに分けて、実際のファイルを開きながら説明していきます。まずは全体の地図と、読むために知っておいたほうがいい前提からです。
フォルダの分け方、依存しているものの数、そして「サーバーで動く部品」と「ブラウザで動く部品」がどこで分かれているかです。次回以降、個別のファイルを1つずつ読んでいきます。
本番で必要な依存は5つしかない
package.json を開くと、公開後に動かすために必要なものはこれだけでした。
| 名前 | 役割 |
|---|---|
| next | ページを組み立てて配る本体 |
| react / react-dom | 画面を書く仕組み |
| graphql-request | WordPressへ問い合わせる |
| graphql | その問い合わせ文を解釈する |
この他に、開発中だけ使うもの(型検査、書式の検査、CSSの生成、型の自動生成)があります。本番の動作に関わるのは上の5つだけです。
部品を足すたびに、その部品の脆弱性を見張る相手が1つ増えます。入れていないものは、更新も監視も要りません。
目次も検索も日付の整形も、自分で書いてあります。どれも数十行で、外から持ってくるほどの分量ではありませんでした。
フォルダは3つに分かれている
コードは src/ の下にあり、大きく3つです。
| 場所 | 入っているもの |
|---|---|
src/app/ |
URLと1対1で対応するページ |
src/components/ |
画面に出る部品 |
src/lib/ |
画面に出ない処理とデータ |
app/ は特別で、ファイルの置き場所がそのままURLになります。app/blog/page.tsx が /blog に、app/blog/[slug]/page.tsx が /blog/記事名 になります。角かっこは「ここは可変」という意味です。
ルーティングの設定ファイルはありません。フォルダを作ればURLが増えます。
境目は「ブラウザが要るかどうか」で引く
ここがいちばん説明の要るところです。
いま作っているのは、ほとんどのコードがサーバー側で動く形です。ページはあらかじめ組み立てられ、訪問者にはHTMLとして届きます。ブラウザで動くコードは、必要な部品にだけ付けます。
その指定が、ファイルの1行目に書く "use client" です。
| 書いていない | 書いてある | |
|---|---|---|
| どこで動くか | サーバー | サーバーとブラウザ両方 |
| できること | CMSへの問い合わせ、秘密の値の利用 | クリック、スクロール、保存 |
| 訪問者に届くJS | 増えない | そのぶん増える |
このサイトの部品を数えると、ちょうど半々でした。16個ずつです。
これとは別に、スクロールの位置や保存した設定を読むための小さな処理が3つ、src/lib/ にブラウザ用として置いてあります。部品ではないので上の数には入れていません。
書いてある側はどれも、ブラウザでしか手に入らないものを触っています。スクロールの位置、クリック、保存した設定、画面の幅。逆に言えば、それらが要らない部品には付けていません。
"use client" は、そのファイルから読み込んでいるものにも連鎖します。上のほうの部品に付けると、その下にぶら下がる部品まで一緒にブラウザへ送られます。
迷ったときに付けておくと動きはしますが、動く代わりに、送るものが静かに増えます。境目はできるだけ末端に置くほうがよく、このサイトでも「ボタン1つだけを別部品にして、そこにだけ付ける」という形がいくつかあります。
型は、CMSの都合に追いつくために使っている
TypeScriptを使っている理由は、書き間違いを見つけるためだけではありません。
記事のデータはWordPressから届きます。項目の名前も、値が空になり得るかどうかも、決めているのはCMS側です。手で型を書き写すと、CMS側で項目を足した日から、少しずつずれていきます。
そこで、CMSが公開している仕様を読み取って型を自動生成しています。コマンドを1つ走らせると、実際に書いた問い合わせ文に合わせた型ができます。
自動生成された型は、title や slug を「無いかもしれない」ものとして扱います。実際に下書きや移行中のデータでは空になり得るからです。
手書きなら、面倒なので「必ずある」と書いてしまうところです。生成された型は、こちらの願望ではなく相手の仕様を写しているので、そこで手が止まって「無かったらどうするか」を考えることになります。
この先で読んでいくもの
次回以降、次の順で中身に入ります。
| 回 | 読むもの | 話の中心 |
|---|---|---|
| 2 | 目次を作る処理 | HTMLを文字列として扱う |
| 3 | 同意を取る仕組み | 保存する形と、読み込まない実装 |
| 4 | CMSから取ってくる処理 | キャッシュと、失敗する前提 |
| 5 | RSSとサイトマップ | 文字列を自分で組み立てる場所 |
今回の学び
まず、依存の数は、後から効いてくるということ。今のところ5つで足りていて、そのぶん更新の通知も脆弱性の報告も少なく済んでいます。数十行で書けるものを外から持ってこない、という判断が積み重なった結果でした。
次に、境目は機能ではなく「何が必要か」で引くこと。ブラウザでしか手に入らないものを使う部品だけを、ブラウザ側に置いています。見た目の複雑さや行数とは関係がありません。
そして、相手が決めていることは、相手から写すこと。CMSのデータの形はこちらで決められません。手で書き写すとずれていくので、生成させています。ずれないことより、ずれようがない形にしてあることのほうが大事でした。
関連記事
読む体験をつくる — 目次の自動生成から読了状態の保存まで
記事を5本公開したあと、読む側の体験を作り込みました。目次の自動生成、追従する目次、読了状況の表示と保存、スマートフォンのメニュー、読了目安の時間。あわせて、トップページの見た目も見直しています。 この記事は、その実装記 […]

「画面に入った」で数えたら、17%残して読了になった
「画面に入った」で数えると何が起きるか、それを「通り過ぎた」に変えると何が揃うか。そして、速く動かしたときに何を数えていいのかです。

読む邪魔をしない
貼り付いたヘッダーと目次が、読んでいるあいだずっと画面を占めていた。下へ読むときは引っ込むヘッダー、スクロール中だけ薄くなる目次、先頭へ戻るボタン。3つの判断の記録。