タグ: PR

アフィリエイトリンクを含む記事に付ける。このタグがあると記事冒頭に広告表示が自動で出る。

  • フロントエンドエンジニア養成読本 — 知らないものは、検索できない

    フロントエンドエンジニア養成読本 — 知らないものは、検索できない

    フロントエンドの知識が、断片のまま溜まっていました。

    必要になるたびに調べて、動いたら次へ進む。それを繰り返していたので、個々のやり方は知っているのに、全体のどこに位置する話なのかが分かっていない状態でした。

    この記事で扱うこと

    調べながら覚えると何が抜けるのか、地図として1冊読むことに何の意味があるか、そして発行から時間が経った本をどう読むかです。

    調べて覚えると、地図が作られない

    検索して覚えるやり方は速いです。目的がはっきりしているので、無駄がありません。

    ただ、この覚え方には抜けるものがあります。自分が今どのあたりの話をしているのかが分からない。

    調べて覚えたこと 分かっていなかったこと
    この書き方で動く 他にどんなやり方があるのか
    この道具の使い方 そもそも何を解決する道具なのか
    この設定で直った なぜその設定が要るのか

    右の列が空いていると、選べません。目の前の記事に書いてあるやり方を、そのまま採用するしかなくなります。合っているかどうかも判断できません。

    知らないものは、検索できない

    いちばん困ったのは、存在を知らない選択肢は検索に出てこないことでした。

    調べるという行為は、探すものの見当が付いていて初めて成立します。見当が付いていない領域は、調べても素通りします。

    並んでいることに意味がある

    そこで、範囲を区切って1冊読むことにしました。この本を選んだのは、フロントエンドの守備範囲を一通り並べている構成だったからです。

    1つ1つの解説の深さは、専門書には及びません。ただ、目的が違います。深く知るためではなく、何があるかを知るために読みました。

    読み終えて変わったのは、知識の量ではなく、調べ始めるときの精度でした。何の話をしているかの見当が付くので、検索の言葉が決まります。出てきた記事が自分の状況に合っているかも判断できます。

    地図は、細かくなくていい

    地図に求めるのは正確な描写ではありません。どこに何があるか、その間がどうつながっているかだけです。

    細部は必要になったときに調べれば足ります。ただし「そこに何かがある」ことを知らないと、調べに行くこと自体が起きません。

    発行から時間が経っている

    この本の注意点は、そこに尽きます。フロントエンドは入れ替わりの早い領域で、当時の道具立てと今のものは同じではありません。

    実際、自分が今このサイトで使っている作り方とは、前提からして違います。当時の具体的な手順をそのまま真似すると、古い書き方を覚えることになります。

    それでも読む価値があると感じたのは、道具は変わっても、その道具が解こうとしている問題は残っているからでした。何を自動化したいのか、なぜ分けるのか、どこで速さが落ちるのか。その部分は入れ替わっていません。

    読み方を分けておく

    古い本を読むときは、「何を解こうとしているか」と「どう解いているか」を分けて読む必要があります。

    前者は持ち帰れます。後者は今の手段に置き換えて考えることになります。混ぜて読むと、解決済みの問題のために古い手順を覚えることになります。

    今から同じことをするなら

    全体像をつかむ目的なら、この本でなければならない理由はありません。今のフロントエンドを一通り並べた本であれば、同じ役目を果たします。

    大事なのは本の新しさよりも、断片を集める読み方と、地図を作る読み方を分けていることのほうだと考えています。前者は検索で足りますが、後者は検索では作れませんでした。

    今回の学び

    まず、検索は見当が付いている範囲にしか効かないということ。知らないものは検索語にできないので、調べ続けているかぎり空白は空白のまま残ります。

    次に、浅く広い1冊には、深い1冊とは別の役目があること。深さを求めて読むと物足りませんが、地図として読むなら浅さは欠点になりません。求めているものが違います。

    そして、古い本は、問題と手段を分けて読むこと。手段は入れ替わりますが、その手段が何を解こうとしていたかは残ります。分けて読めば、古い本からでも持ち帰るものがあります。

  • 達人プログラマー 第2版 — 教えにくいことほど、差が出る

    達人プログラマー 第2版 — 教えにくいことほど、差が出る

    プログラミングを教えていると、質問の種類が2つに分かれることに気づきます。

    ひとつは「この書き方が分からない」。これは調べれば答えが出ます。もうひとつは「どっちがいいですか」。こちらは答えが1つに決まりません。

    後者に答えるとき、自分が何を根拠に喋っているのかが曖昧でした。経験からそう思う、としか言えない。教える側としては、そこが一番あやしい部分でした。

    この記事で扱うこと

    「書き方」ではなく「進め方」を教えるときに何が足りなかったか、この本がそこにどう効いたか、そして教材として使うときの注意点です。

    教えやすいことと、伝えたいことがずれている

    教材にしやすいのは、正解のあるものです。文法、関数の使い方、エラーの読み方。手順が決まっていて、できたかどうかも判定できます。

    ところが、実務で差が出るのはそこではありません。

    教えやすいこと 実際に差が出ること
    書き方を覚える どこまで作り込むかを決める
    エラーを直す 同じエラーが起きない形にする
    言われたとおりに作る 言われていない前提を確認する

    右の列は、教科書の形になりにくい。正解が状況によって変わるので、「こうしなさい」と言えないからです。

    この本は、右の列について書かれている

    この本が扱っているのは、ほぼ全部が右の列です。原題は The Pragmatic Programmer で、日本語版は第2版が出ています。

    言語の解説はほとんどありません。代わりに、仕事の進め方そのものが並んでいます。同じことを2か所に書かない。壊れたまま放置しない。自分の道具を選んで使い込む。見積もりをどう扱うか。

    ひとつひとつは短く、独立しています。順番に読まなくても成立する構成なので、必要なところだけ拾えます。

    名前が付いていると、指し示せる

    助かったのは、それぞれの考え方に名前が付いていることでした。

    「同じ情報を2か所に置かない」という話は、経験があれば誰でも何となく分かっています。ただ、名前が無いと会話のたびに一から説明することになります。名前があると、一言で指して、その先の話ができます。

    教える側として、いちばん効いたところ

    この本を読んで、自分の説明の仕方が変わった部分があります。

    それまでは「こう書くといい」と結論だけを渡していました。読んだあとは、「こうすると後で何が起きるか」を先に話すようにしています。

    たとえば、同じ設定値を2か所に書いた場合。その場では動きます。問題が出るのは、片方だけ変えたときです。「今は動くが、変えるときに壊れる」という形で伝えると、納得の仕方が変わりました。禁止として覚えるのではなく、理由として残ります。

    経験の無い人には、まだ早い話もある

    ただし、この本の内容は失敗した経験があって初めて刺さるものが多いです。

    「壊れたまま放置しない」と書かれていても、放置して痛い目を見たことが無ければ、当たり前の標語として素通りします。初学者にそのまま渡すと、たぶん響きません。

    このサイトを作るときにも使った

    教える側の話だけではなく、自分の作業にも効きました。

    このサイトでは、ドメイン名やRSSの場所といった値が、いつの間にか複数のファイルに散っていました。どれも画面に出ない値で、書き換え漏れがあっても見た目では分かりません。

    気づいたときに1か所へまとめたのですが、その判断の速さは、この本で読んだ考え方が下敷きになっています。「今は動いている」を理由に放置しない、という部分です。

    教材として使うときの注意

    そのまま課題図書にするのは向いていないと考えています。読ませるなら、ある程度の期間、自分で作って詰まった経験があってからのほうが効きます。

    自分の場合は、必要な項目だけを取り出して、目の前の作業と結びつけて話す使い方をしています。本そのものを渡すのではなく、本から取った言葉を渡す形です。

    今回の学び

    まず、教えにくいことほど、実務では差が出るということ。正解のあるものは教材にしやすいのですが、そこは調べれば済む部分でもあります。時間をかけて伝える価値があるのは、判定しにくいほうでした。

    次に、考え方に名前が付くと、会話が短くなること。感覚として持っているだけだと、毎回説明が必要です。名前があれば指し示せて、その先の議論に進めます。

    そして、結論ではなく、後で起きることを渡すこと。「こう書きなさい」は覚えるだけで終わりますが、「こうしないと何が起きるか」は判断の材料になります。次に似た場面が来たとき、自分で決められる形になっていました。

  • リーダブルコード — 引き継ぐ側に回って分かったこと

    リーダブルコード — 引き継ぐ側に回って分かったこと

    自分で書いていないコードを直す仕事が続いた時期がありました。書いた人はもういません。仕様書もありません。あるのは動いているコードだけです。

    読んでいて思ったのは、これを書いた人を責めても何も進まないということでした。同時に、自分が書いたものも、いずれ誰かにこう読まれるということでもあります。

    この記事で扱うこと

    引き継ぐ側に回って何に困ったか、この本がその困りごとにどう答えているか、そして読んだあとに自分の書き方の何が変わったかです。

    読めないコードは、読みにくいコードとは限らない

    最初に困ったのは、きれいに整っているのに何をしたいのか分からないコードでした。

    字下げは揃っています。短い関数に分かれています。それでも読めない。理由を探していくと、名前でした。

    const data = getData();
    const result = process(data);
    if (flag) {
      doIt(result);
    }

    形としては悪くありません。ただ、この4行を読んでも、何の処理なのかが1つも分かりません。名前が全部「何かの入れ物」で止まっています。

    読む側は、名前を手がかりに当たりを付けている

    コードを追うとき、全部を順番に読んでいるわけではありません。名前を見て「ここは関係なさそうだ」と飛ばし、関係ありそうな場所だけ中に入ります。

    名前が中身を表していないと、この飛ばし読みが効かなくなります。結果として全部を読むことになり、1か所直すのに何時間もかかります。

    この本が答えていること

    この本は、そういう「読む側の負担」を軸に書かれています。副題が「より良いコードを書くためのシンプルで実践的なテクニック」で、扱っているのは設計論ではなく、もっと手前の話です。

    名前の付け方、コメントに何を書くか、条件分岐の並べ方、1つの関数にどこまで詰めるか。どれも今日書く1行に効く内容で、大きな作り替えを必要としません。

    いちばん残ったのは、コメントには「何をしているか」ではなく「なぜそうしたか」を書くという指摘でした。

    // ユーザーを取得する
    const user = fetchUser(id);

    これはコードを日本語に訳しただけで、読む側は何も得ていません。知りたいのは、なぜここで取りに行くのか、なぜこの方法なのかのほうです。

    引き継ぐ側にとって、いちばん欲しかったもの

    実際に困った場面を思い返すと、欲しかったのは常に判断の理由でした。

    コードから分かること コードから分からないこと
    何をしているか なぜその方法を選んだか
    どういう順番で動くか なぜこの順番でなければならないか
    この値を除外していること 除外しないと何が起きるのか

    右の列が分からないと、触っていいのかどうかが判断できません。一見無駄に見える処理が、実は過去の不具合への対処だった、ということは珍しくありません。理由が書いていないと、消して初めて分かります。

    読みやすさは、書く人への要求ではない

    読みやすく書くのは、丁寧さや性格の問題として語られがちです。この本の立て方は違っていて、「読む人が理解するまでの時間を短くする」という目的に対する手段として扱っています。

    目的が決まっているので、迷ったときの判断基準になります。短く書くか説明的に書くかで悩んだら、読む人が早く分かるほうを選ぶ。それだけです。

    読んだあとに変えたこと

    このサイトのコードでは、コメントの書き方を変えました。処理の説明ではなく、なぜその選択をしたのかを残すようにしています。

    たとえば「この値を1か所にまとめた理由」や「この設定を別のファイルに切り出した理由」を、選ばなかった案と一緒に書いています。半年後の自分は、今の自分が何に困っていたかを覚えていません。困りごとごと残しておかないと、また同じ場所で悩みます。

    合わないかもしれない場合

    扱っているのは小さな単位の話なので、設計全体をどう組むかという話は出てきません。そこを求めて読むと物足りなく感じます。

    あと、例に使われているコードはC++やJavaScriptなどが中心です。普段そのあたりを書いていなくても読めますが、言語固有の書き方を学ぶ本ではありません。考え方を持ち帰って、自分の言語に翻訳する読み方になります。

    今回の学び

    まず、コードは書く時間より読まれる時間のほうが長いということ。引き継ぐ側に回って初めて実感しました。書いているときは自分しか読者がいませんが、動き続けるコードには読者が増えていきます。

    次に、名前は飛ばし読みのための道具だということ。読む人は全部を読みません。名前で当たりを付けて、必要な場所だけ開きます。名前が中身を表していないと、この省略が使えなくなります。

    そして、残すべきは処理ではなく判断だということ。何をしているかはコードを読めば分かります。分からないのは、なぜそうしたかのほうでした。そこが書いていないと、後から来た人は消すことも直すこともできません。

  • Bose QuietComfort Ultra Earbuds — 「自動で切り替わる」に期待しなくなった

    Bose QuietComfort Ultra Earbuds — 「自動で切り替わる」に期待しなくなった

    作業はMacBookとiPadを行き来しながら進めます。調べものや資料はiPad、書くのはMacBookという分け方です。

    音は完全ワイヤレスのイヤホン1つで賄っています。2台あるのにイヤホンは1つなので、どちらの音を出すかを決める作業が毎回発生します。

    この記事で扱うこと

    2台を行き来する使い方で何が面倒になるか、自動で切り替わることに期待しなくなった理由、そしてノイズキャンセリングを常時オンにしている理由です。

    2台をまたぐと、必ず「どっちの音か」が出てくる

    1台だけで使っているうちは、この問題は存在しません。つなげば鳴る、それだけです。

    2台になると、イヤホンはどちらの音を出すかを選ばなければいけません。そして選び方は2つしかありません。機械に決めさせるか、自分で決めるか。

    最初は前者を期待していました。音が鳴ったほうに勝手に移ってくれるのが、いちばん手数が少ないはずだと思っていたからです。

    期待していたことと、実際にやっていること

    買う前に期待していたこと いま実際にやっていること
    音が鳴った側へ勝手に移る イヤホンを長押しして、自分で移す
    意識しなくていい 移すときだけ意識する
    手数はゼロ 手数は1回

    手数だけ見れば期待どおりにいっていません。それでも、いまのほうが困っていません。

    推測が外れたときのほうが、高くつく

    機械に決めさせるということは、「いま鳴っているこの音を、この人は聞きたいはずだ」と推測させるということです。

    推測は当たることもあれば外れることもあります。外れたときは、なぜ音が来ないのかを調べ、どちらにつながっているかを確かめ、戻す、という手順を踏むことになります。1回の手数を惜しんだ結果、何倍もの手数を払います。

    長押しで、自分で移している

    いまはイヤホン側を長押しして、つなぐ先を自分で切り替えています。

    作業の流れとしては、iPadで資料を見ながら音を聞いていて、書く段になってMacBookへ移る、という場面がほとんどです。移るタイミングは自分が決めているので、切り替えの操作もその流れの中に収まります。

    自分で決めるなら、外れない

    手数は1回増えますが、その1回は必ず狙いどおりになります。推測が外れる可能性がゼロになるので、つながり先を確かめる手間が消えます。

    「自動でやってくれる」の価値は、手数が減ることではなく考えなくてよくなることにありました。考える必要が残るなら、手数だけ減っても意味がありません。

    ノイズキャンセリングは、常にオンにしている

    ここも同じ話です。

    環境に合わせて切り替える使い方もできますが、自分は常時オンで固定しています。理由は静かだからというより、「いま要るか」を毎回考えなくて済むからです。

    切り替えられる機能は、切り替えるかどうかの判断を毎回発生させます。1回あたりは数秒でも、1日に何度も出てくると、そちらのほうが重くなります。

    固定してしまえば、イヤホンを着けた瞬間から作業に入れます。判断を1つ減らすために、選べる自由のほうを捨てている形です。

    合わなかったところ

    長押しでの切り替えは片手でできますが、どちらに移ったかは音でしか分からないのが少し不便です。無音のときに切り替えると、確かめる方法が「何か鳴らしてみる」しかありません。

    それと、これは製品というより自分の使い方の話ですが、2台とも音を出している状況には向きません。片方で動画を流しながらもう片方で作業する、といった使い方をするなら、イヤホンを分けたほうが早いはずです。

    まだ書けないこともあります。長時間つけたときの耳の疲れ方と、電池の持ちが1年後にどうなっているかは、いま判断できる段階にありません。

    今回の学び

    まず、自動化の価値は手数ではなく、考えなくてよくなることだということ。手数が1回減っても、確かめる手間が残るなら差し引きで損をします。ここを取り違えると、自動のほうを選んで後悔します。

    次に、推測させる仕組みは、外れたときのコストで評価すること。当たっているあいだは快適なので、外れる場面を想像しないまま良いものだと判断してしまいます。実際に効いてくるのは外れたときのほうです。

    そして、選べることは、それ自体がコストになること。切り替えられる機能は、切り替えるかどうかを毎回考えさせます。使い方が決まっているなら、固定してしまうほうが軽くなりました。

  • 7日間でハッキングをはじめる本 — 守り方は分かる。何から守るのかは分からない

    7日間でハッキングをはじめる本 — 守り方は分かる。何から守るのかは分からない

    自分のサイトを公開したあと、脆弱性の検知と監視の仕組みを作りました。依存関係を見張って、警告が出たら気づけるようにする、という内容です。

    そのとき読んでいた情報は、全部が守る側のものでした。

    この記事で扱うこと

    守る側の情報だけを読んでいて何が抜けていたか、体験して覚えることと読んで覚えることの違い、そして学んだことをどこまで使ってよいかです。

    守り方は分かる。何から守るのかは分からない

    「この設定を入れる」「このヘッダーを付ける」「このバージョンに上げる」。守る側の情報は、やることが具体的に書いてあります。そのとおりにやれば作業は終わります。

    ただ、終わったあとに本当に守れているのかが判断できませんでした。

    理由ははっきりしていて、攻撃がどういう手順で進むのかを知らなかったからです。手順を知らないまま対策だけ並べると、全部が同じ重さに見えます。

    優先順位は、攻撃側の手順から決まる

    対策のリストには、放置すると即座に踏まれるものと、他がすべて破られた後にようやく効いてくるものが混ざっています。

    順番が分からないと、上から順にやるか、やりやすいものからやるかしか選べません。

    手を動かす本を選んだ

    そこで読んだのがこの本です。TryHackMeという、攻撃を試すために用意された環境を使いながら進みます。

    解説を読むだけの本ではありません。指示されたとおりに調べ、見つけ、入る。その手順を自分でなぞります。

    やってみて分かったのは、残り方が違うということでした。読んだだけの知識は「そういうものがあるらしい」で止まりますが、自分の手で手順を踏むと、どこが引っかかりやすいかまで体に残ります。

    攻撃は、ひらめきではなく手順だった

    やる前は、攻撃というものをもっと特別なものだと思っていました。実際には調べる、試す、記録するの繰り返しです。

    特別ではないと分かったことが、いちばん大きい収穫でした。特別でないなら、こちらも手順で備えられます。

    読むだけでは、半分しか入らない

    この本の注意点はここです。手を動かす前提で書かれているので、読むだけだと半分で終わります。

    環境の準備や、うまくいかないときに止まる時間も含めて、それなりにまとまった時間が要ります。通勤中に読み進める種類の本ではありません。時間を確保してから始めるほうがいいと思います。

    7日間という題名ですが、1日ぶんに何時間かかるかは書いてある内容と自分の慣れ次第です。自分は日によってかなり差がありました。

    使ってよい範囲について

    ここは書いておきます。

    この本で扱うのは、そのために用意された練習用の環境です。同じ手順を、許可を得ていない相手に対して試してはいけません。技術として成立するかどうかと、やってよいかどうかは別の話です。

    自分の場合、試す先は自分のサイトと練習環境だけにしています。攻撃側の手順を知る目的は、守る順番を決めるためであって、それ以上ではありません。

    今回の学び

    まず、守る側の情報だけでは、優先順位がつかないこと。対策は列挙できても、どれを先にやるべきかは攻撃の手順を知らないと決まりませんでした。

    次に、体験は、残り方が違うこと。読んで分かったつもりになっていたことでも、自分の手で通してみると引っかかる場所が出てきます。その引っかかりのほうが、あとで効きます。

    そして、知ることと、やってよいことは別だということ。手順が分かるほど、その線を自分で引いておく必要が出てきます。本の中で扱われている環境が、練習のために用意されたものである意味は、そこにあります。

  • たのしいRuby 第6版 — フレームワークから入ると、言語が抜ける

    たのしいRuby 第6版 — フレームワークから入ると、言語が抜ける

    CMSを作っている会社にいたころ、製品のベースがRailsでした。仕事は管理画面やフロントの改修です。

    コードは目の前にあります。動いてもいます。それなのに、何をしているのか読めませんでした。

    この記事で扱うこと

    フレームワークの側から入ると何が抜けるのか、なぜ言語の本を読み直したのか、そしてこの本で埋まった部分と埋まらなかった部分です。

    読めない、の中身

    最初は「経験が足りないから読めない」のだと思っていました。実際には、もっと具体的な詰まり方をしていました。

    目の前の1行が、Railsが用意した仕組みなのか、Rubyの文法なのかが区別できない。

    これが分からないと、次の一手が決まりません。

    その行が何か 調べに行く先
    Railsの機能 Railsのドキュメント、そのバージョンの仕様
    Rubyの文法 言語のリファレンス
    製品独自の実装 社内のコードと、書いた人

    3つのどれかは分かるのに、どれなのかが分からない。結果として、調べ始める場所が決まりませんでした。

    いちばん時間を食ったのは、書くことではなかった

    手が止まっていたのは、書き方が分からないときではありません。どこを調べればいいか分からないときでした。

    検索の言葉すら決まらないので、当てずっぽうで探すことになります。

    フレームワークから入ると、言語が抜ける

    入り方が逆だった、というだけの話です。

    ふつうは言語を覚えてからフレームワークに進みます。自分は製品の改修という入口から入ったので、Railsの決まりごとは仕事の中で覚えていきました。「こう書けば動く」は増えていきます。

    ただ、その下にある言語そのものは通っていません。だから、Railsの型に当てはまらない行に出会った瞬間に読めなくなります。

    動かせることと、読めることは別

    フレームワークの作法だけでも、決められた形の改修はできます。できないのは、形から外れたときの判断です。

    「動いているから正しい」以上のことが言えないまま進むことになります。

    言語の本に戻った

    そこで読んだのがこの本でした。Railsの本ではなく、Rubyそのものの入門書です。

    文法を順番に追う構成なので、フレームワークの側から入って穴が空いている状態を埋めるのに向いていました。「ここはRubyの書き方だ」と分かるようになると、調べに行く先が決まります。それだけで手が止まる回数がはっきり減りました。

    特に効いたのは、ブロックやシンボルのようなRubyでは当たり前だが、他の言語から来ると初見のものです。Railsのコードにはこれらが大量に出てきます。知らないまま読むと全部が「Railsの魔法」に見えます。

    この本で埋まらなかったこと

    当然ですが、Railsの作法はこの本には書いてありません。言語を埋めるための1冊と割り切る必要があります。

    それと、他の言語をすでに書ける人にとっては、前半は既知の内容が続きます。飛ばしながら読んで、知らない書き方が出てきたところで止まるという読み方になりました。最初から順に読む本としては、少し長く感じるかもしれません。

    今回の学び

    まず、入る順番は逆でもいいが、どこかで戻る必要があること。フレームワークから入るのは実務では珍しくありませんし、そのほうが早く戦力になります。ただ、戻らないままだと「形から外れた瞬間に止まる」状態が続きます。

    次に、詰まりの正体を言葉にすると、対処が決まること。「読めない」のままでは何を勉強すればいいか分かりませんが、「言語かフレームワークかの区別がつかない」まで分解できれば、読むべき本は1冊に決まります。

    そして、調べる場所が決まらないことが、いちばん時間を食うこと。答えを探す時間より、探す場所を決められない時間のほうが長かった。ここは知識の量ではなく、切り分けの問題でした。

  • Keychron Nape Pro — 1週間かかる、は誰の話か

    Keychron Nape Pro — 1週間かかる、は誰の話か

    キーボードの手前に置くポインティングデバイスを使い始めました。マウスへ手を伸ばす動作が消えます。

    レビューを読むと、だいたい「慣れるまで1週間はかかる」と書いてあります。自分は2〜3日で普通に使えるようになりました。

    速く慣れたという話ではありません。その1週間を、自分はすでに別のところで払い終えていただけです。

    この記事で扱うこと

    「慣れるまで1週間」という数字が誰の話なのか、慣れる対象を分けて数えるとどうなるか、そして買う前に見るべきなのは期間ではないという話です。

    何を置き換えたのか

    これはトラックボールの一種で、キーボードの手前に置いて、親指や指でボールを回してカーソルを動かします。

    マウスを使っていると、カーソルを動かすたびに右手をキーボードから離して横へ振ります。1回は小さな動作ですが、1日に何百回も繰り返します。手前に置くデバイスなら、その振る動作がなくなります。

    肩の開き方が変わるので、長時間の作業で姿勢が戻らなくなる感覚は減りました。ただ、それはこの製品でなくても、手前に置くデバイスなら得られる効果です。

    実際の置き方

    HHKBの手前にKeychron Nape Proを置き、左右に分かれた木製パームレストの真ん中に収めた机の上
    奥がHHKB、手前がNape Pro。左右の木がパームレストで、その間にちょうど収まっている。

    キーボードは HHKB Professional HYBRID Type-S で、その手前に Nape Pro を置いています。

    パームレストは左右に分かれていて、その真ん中に Nape Pro が入ります。分離型を選んだのは見た目の好みではなく、ここに置き場所が必要だったからです。

    一体型だと、この場所が空かない

    横に長いパームレストを置くと、キーボードの手前はそれで埋まります。手前に置くデバイスを使うなら、パームレストの形まで連動で決まります。

    デバイスを選んだ時点では気づいていませんでした。

    手は左右のパームレストに載せたままで、指だけでボールに届きます。木なので沈み込まず、手首の高さが一定に保たれます。

    キーボードに貼ってある付箋は、接続先の番号です。Nape Pro もキーボードも複数の機器に繋がりますが、どの番号がどの機器かは覚えていられませんでした。覚えておくことを前提にした仕組みは、たいていこうなります。

    「慣れる」は1つのことではない

    ここが今回いちばん考えた部分です。

    「慣れるまで1週間」と書かれると、1つの壁を1週間かけて越えるように読めます。実際には別々のものが3つあり、それぞれ独立して越える必要があります。

    慣れる対象 中身
    ボールでカーソルを動かす 指の動きと画面の動きが結びつくまで。狙った場所で止まらない期間がある
    手を右へ振らない姿勢 マウスを取りに行く動作が体に残っている。空振りする
    ボタンの割り当て どの操作をどこに置くか。決めて、指が覚えるまで

    マウスから乗り換えた人は、この3つを同時に相手にします。だいたい1週間、というのは妥当な数字だと思います。

    自分の場合、1つめは終わっていた

    その前に使っていたのが、ロジクールのトラックボールでした。

    つまり「ボールでカーソルを動かす」は、乗り換える前から体に入っていました。残っていたのは置き位置とボタンの割り当てだけです。2〜3日というのは、そのぶんが短くなった数字にすぎません。

    払っていないのではなく、先に払っていた

    そのロジクールのトラックボールを使い始めたときは、1〜2週間ほど操作に苦労しました。

    狙ったところで止まらない、細かい選択ができない、そのたびに手が止まる。あの期間はきちんとあって、しかも今回より長かったです。

    並べてみると分かりやすくなります。マウスからトラックボールへ移った時点で1〜2週間、そこからこの製品へ移って2〜3日。合計すれば、結局「1週間より長い」ところに着地しています。

    レビューの「1週間」は、間違っていない

    ここを取り違えないようにしたいと思いました。

    1週間という数字は、マウスから来た人にとっては正しいのだと思います。3つを同時に越えるならそのくらいかかります。書いた人が誇張しているわけではありません。

    問題は、その数字に「どこから来たか」という前提が書かれていないことです。前提が省かれた数字は、条件の違う人にそのまま当てはめられてしまいます。

    数字には、書いた人の出発点が含まれている

    「慣れるまで◯日」は、製品の性質だけで決まる値ではありません。製品と、その人が何を使っていたかの差で決まります。

    同じ製品でも、出発点が違えば別の数字になります。どちらも嘘ではありません。

    買う前に見るべきなのは、期間ではない

    そう考えると、検討している人が確かめるべきなのは日数ではなくなります。

    いま何を使っているか。マウスなら、上の3つを全部越えることになります。すでにトラックボールなら、越えるのは2つです。ここが分かれば、レビューの数字を自分向けに読み替えられます。

    逆に言うと、「何日で慣れました」とだけ書かれたレビューは、そのままでは使えません。読むときは、その人が前に何を使っていたかを探すことになります。

    正直に書いておくこと

    この製品はクラウドファンディングで手に入れたので、一般の発売より前から使っています。そのぶん使用期間はありますが、周辺の情報はまだ少ない状態です。困ったときに検索して出てくる記事の数は、定番の製品とは比べものになりません。

    それと、ここまで書いてきたのは操作に慣れるまでの話だけです。長く使ったときに手や肩がどうなるかは、まだ判断できる段階にありません。分かったらあらためて書きます。

    実際に割り当てているもの

    3つめの「ボタンの割り当て」は、慣れるというより決める作業です。決まっていないあいだは指が迷うので、そこが長引くと「まだ慣れない」と感じます。

    いま入れているのは次のとおりです。ボタンは短く押したときと長く押したときで別のものを出せるので、2つ書いてある行はその区別です。

    場所 短押し 長押し
    M1(手前・上) 右クリック —
    M2(手前・下) 左クリック —
    01(左上) 英数 Win
    02(左下) 戻る —
    03(右上) かな ボールジェスチャ
    04(右下) 進む —
    ホイール 上スクロール 下スクロール

    決め方の基準はひとつだけで、キーボードから手を離さずに済むものを優先することです。

    英数とかなを左右に分けて置いたのは、日本語入力の切り替えがいちばん回数の多い操作だからです。ここをキーボード側に取りに行くのなら、手前に置いた意味が半分なくなります。

    戻ると進むも、調べものをしているあいだずっと使います。これも本来は別の場所にあった操作でした。

    割り当ては、覚える数を増やさないほうに寄せる

    ボタンがあると、つい何か入れたくなります。ただ覚えていない割り当ては、無いのと同じです。

    いま入れているのは、どれも元から毎日やっていた操作の置き場所を変えただけで、新しく覚えることは増やしていません。3つめが早く終わったのは、そのぶんもあります。

    今回の学び

    まず、「慣れるまで◯日」は製品の値ではないこと。製品と、その人の出発点との差で決まります。数字だけを取り出すと、自分に当てはまるかどうかが判断できません。

    次に、前提が省かれた数字ほど、そのまま信じられてしまうこと。1週間という数字には「マウスから来た場合」という条件が付いていましたが、書かれていませんでした。書いた人にとって当たり前すぎる前提ほど、文章から抜け落ちます。

    そして、自分が書くときも同じ立場になること。「2〜3日で慣れた」とだけ書けば、同じ落とし穴を作ることになります。前に何を使っていたかを一緒に書いておかないと、読んだ人の役に立ちません。

  • サーバーは、要らないはずだった

    サーバーは、要らないはずだった

    ヘッドレス構成の作り方を調べると、だいたい「Next.jsをVercelに置く」と出てきます。無料枠で動きます。

    それを読んで、サーバーは借りなくていいのだと思っていました。実際には借りました。何を勘違いしていたのか、そして借りるときに何を見て決めたのかを書いておきます。

    この記事で扱うこと

    WordPressをVercelに置けない理由、無料の選択肢を外した判断、そして「安いほうで足りる」と結論した根拠です。

    ヘッドレスは、置き場所が2つ要る

    ヘッドレスという言葉は「WordPressの見た目を切り離す」という意味です。切り離すだけで、WordPress自体が消えるわけではありません。

    部分 役割 置き場所
    Next.js 読者が見る画面を作る Vercel
    WordPress 記事を書いて保管する レンタルサーバー

    調べていた記事の多くはNext.js側の話をしていて、WordPress側は「すでにある前提」で書かれていました。既存サイトをヘッドレス化する人が多いので、当然といえば当然です。これから作る人だけが、置き場所の話に気づかないまま進みます。

    VercelにWordPressは置けない

    置けないかどうか、最初に確かめました。結論は置けません。

    WordPressはPHPというプログラムで動き、記事はMySQLというデータベースに入ります。Vercelはどちらも常時動かす場所ではありません。仕組みが違うので、無理に寄せる話ではありませんでした。

    無料で済ませる道を、ひととおり見た

    お金をかけずに済むなら、そのほうがいい。いくつか検討して、どれも外しました。

    外した理由は共通していて、止まると記事が書けなくなることでした。

    無料の枠には、一定時間アクセスが無いと停止するものがあります。読者が見るのはVercel側なので、CMSに人が来ない時間は長くなります。書こうと思ったときに毎回起動を待つのは、書く回数が減る方向に効きます。

    続けられるかどうかで選ぶ

    費用を抑えるほど、手間か不便さのどちらかに移ります。移った先が「書くたびに発生する手間」だと、書くこと自体が減ります。

    月に数百円で消える手間なら、消したほうが結果的に安いと判断しました。

    何が要らなかったか

    ここが今回いちばん効いた整理でした。

    ふつうWordPressをサーバーに置くときは、表示速度と同時アクセス数を気にします。読者が直接そこへ来るからです。

    ヘッドレスだと、読者はCMSに一度も来ません。来るのは自分と、ページを組み立てるときのNext.jsだけです。

    そうすると、こうなります。

    ふつうのWordPress ヘッドレスのCMS側
    表示速度が読者体験に直結 自分しか見ないので、多少遅くても困らない
    アクセス集中に耐える必要がある 同時に使うのは1人
    止まると全部見えなくなる 止まっても、公開済みのページは見え続ける

    いちばん下が大きい違いです。ページはあらかじめ組み立てて配っているので、CMSが止まっている間も、読む側には何も起きません。影響が出るのは新しい記事を出すときだけです。

    つまり、上位プランを選ぶ理由がほとんどありません。いちばん安い共用プランで足ります。

    逆に、確認したこと

    安ければ何でもいいわけではなく、これだけは満たしていないと動きません。

    ひとつめはPHPのバージョンを自分で選べること。古いまま固定されていると、必要な機能が動かないことがあります。

    ふたつめはサブドメインを独自ドメインで作れて、SSLが付くこと。表側とCMSを分けるので、cms. のようなサブドメインを1つ切ります。ここに鍵マークが付かないと、ブラウザ側で通信が弾かれます。

    みっつめはデータベースが使えること。記事の本体はここに入ります。個数が1つでも足ります。

    確認は、契約前に

    この3つはどれも、契約後に「入っていなかった」と分かるものです。仕様のページに必ず書いてあるので、申し込み画面に進む前に見ておくと、あとで移す手間がなくなります。

    結局どこにしたか

    上の3点(PHPのバージョンを選べる・サブドメインにSSLが付く・データベースが使える)を満たしていて、いちばん安い共用プランで足りました。上位プランを検討する理由が、ヘッドレスだと出てきません。

    ドメインと同じ会社にまとめたのは、請求と管理画面を1つに収めたかったからです。ただしこれには落とし穴があって、DNSの設定画面が2か所に増えます。片方は参照されないのにエラーも出ません。ここで丸一日使いました。

    お名前.comのレンタルサーバー

    実際に困ったこと

    安いプランで足りる、とは書きましたが、共用サーバー特有の引っかかりはありました。

    ひとつはセキュリティの仕組みによる誤検知です。記事の本文にHTMLのタグが多く含まれていると、攻撃だと判定されて保存が弾かれることがありました。これは別記事に書いています。

    もうひとつは予約投稿の時刻がずれることです。WordPressの予約投稿は、時計ではなくアクセスをきっかけに動きます。誰も来ないCMSでは、指定した時刻をしばらく過ぎてから公開されます。

    どちらも、ヘッドレスにしたから起きたというより、「人が来ないサーバー」であることの副作用でした。安さの問題ではありません。

    今回の学び

    まず、解説記事は前提を書かないこと。「Vercelに置く」という説明は正しいのですが、それはNext.js側の話でした。書いた人にとって当たり前すぎる前提ほど、文章から抜け落ちます。

    次に、要件は「誰が使うか」から決まること。同じWordPressでも、読者が来るのと自分しか来ないのとでは、必要なものがまったく違いました。製品の比較表を見る前に、自分の使い方を書き出すほうが早いです。

    そして、費用を削った先がどこに移るかを見ること。無料にすると、たいてい手間か待ち時間に移ります。それが「書くたびに発生する手間」なら、削らないほうが続きます。

  • ローカルのサイトを本番公開するまでにハマった話

    ローカルのサイトを本番公開するまでにハマった話

    ローカルのパソコンで作ったWebサイトを、世界中から見られる状態にする——いわゆる「本番公開」の作業をしました。手順書に沿えば1時間で終わるはずが、実際には丸一日かかっています。今回はその全過程を、途中で出てくる用語をひとつずつ説明しながら記録します。

    想定している読者は、HTML・CSS・JavaScriptは書けるけれど、サーバーやDNSやSSLはこれから、という方です。私自身がその立場で、今回いくつも初めてのことに出会いました。

    そもそも何をしようとしているのか

    今回のサイトはヘッドレス構成というものです。まずここから説明します。

    従来のWordPressサイトは、記事の管理も、見た目の表示も、ぜんぶWordPressがやります。訪問者のブラウザはWordPressに直接アクセスし、WordPressがHTMLを組み立てて返す。1つのソフトが全部を担当する形です。

    ヘッドレス構成では、この役割を2つに分けます。

    WordPress … 記事を書く・保存する(管理画面とデータベース)
    Next.js   … 見た目を作る・訪問者に表示する

    「ヘッドレス」は「頭がない」という意味で、この場合の”頭”は表示部分を指します。WordPressから表示機能を取り除き、データを保管して受け渡すことに専念させる、というイメージです。

    2つに分けると、置き場所も2つ必要になります。

    WordPress → お名前.comのレンタルサーバー(cms.kisaku.site)
    Next.js   → Vercel(kisaku.site)

    Vercelは、Next.jsで作ったサイトを公開するためのサービスです。GitHubにコードを置いておくと、それを自動で取ってきて、ビルドして、公開してくれます。

    ビルドという言葉も説明しておきます。開発中に書いているコードは人間が読みやすい形になっていて、そのままではブラウザで動きません。これを実際に動く形に変換する作業がビルドです。Next.jsの場合は、この段階でWordPressからデータを取ってきて、ページの形に組み立てるところまで行います。

    前回までの記事で、WordPress側の移行は終わっています。今回はNext.js側をVercelに載せ、最後に kisaku.site というドメインを新しいサイトに向けるところまでが目標です。

    デプロイ前に見つけた不具合

    Vercelに繋ぐ前に、設定ファイルを確認していて問題を見つけました。next.config.mjs の画像に関する設定です。

    {
      protocol: "https",
      hostname: "cms.kisaku.site",
      pathname: "/wp-content/uploads/**",   // ← ここが間違っていた
    }

    これが何の設定かというと、Next.jsの画像表示機能が使う「許可リスト」です。

    Next.jsには Image というコンポーネントがあり、画像を自動で最適化して表示してくれます。ただし、どこの画像でも無条件に扱うわけではありません。あらかじめ許可したホスト名とパスの画像しか受け付けない仕組みになっています。悪意のあるサイトの画像を勝手に処理させられるのを防ぐためです。

    ところが前回の移行作業で、このサイトのWordPressは /wp/ というフォルダの中にあることが判明していました。画像の実際のURLはこうなります。

    https://cms.kisaku.site/wp/wp-content/uploads/2026/07/image.png
                            ~~~~ ← これが入る

    許可リストのほうには /wp が入っていないので、一致しません。このままだとブログ記事の画像が全部表示されないことになります。

    直すのは1行です。

    pathname: "/wp/wp-content/uploads/**",

    この間違いは、私が以前「WordPressはルートフォルダにある」と誤解していた頃の設定が残っていたものでした。ひとつの誤解が、気付かないうちに別のファイルにも影響していたわけです。誤解が判明した時点で「この前提を使っている場所は他にないか」を探すべきでした。

    失敗1: 自動生成されるファイルをGitに入れていなかった

    最初のデプロイは20秒で失敗しました。原因はGraphQLの型定義ファイルがリポジトリに入っていなかったことです。

    順に説明します。GraphQLは、必要なデータを指定して取り出すための問い合わせ言語です。「記事のタイトルと本文と日付だけください」というように、欲しい項目を書いて送ると、その形で返ってきます。

    このプロジェクトでは GraphQL Codegen というツールを使っています。書いたGraphQLの問い合わせを読み取って、TypeScriptの型定義を自動生成してくれるものです。型定義があると、エディタが「このデータにはtitleという項目がありますよ」と教えてくれたり、存在しない項目を書いたときに警告してくれたりします。

    これは npm run codegen というコマンドで生成されます。自動生成されるものなので、私は .gitignore(Gitで管理しないファイルを指定するリスト)に入れていました。

    ところがVercelは、ビルド時に npm run build しか実行しません。codegenは走らないので、型定義ファイルが存在しないままビルドに入り、そこで落ちるという流れでした。

    対処は2つ考えられます。

    案A: ビルドコマンドを "npm run codegen && npm run build" に変える
    案B: 生成されたファイルをGitにコミットしてしまう

    一般論では案Aのほうがきれいです。自動生成物をGitに入れると、差分が読みにくくなり、複数人で開発するときに衝突の原因にもなります。

    それでも案Bを選びました。理由は、codegenがWordPressのGraphQLに接続してデータの構造を取りに行くからです。案Aにすると、ビルドが成功するかどうかが本番のWordPressが生きているかどうかに左右されます。

    案A: WordPressが落ちている → codegen失敗 → ビルド失敗 → デプロイできない
    案B: WordPressが落ちている → ビルドは成功する(表示するデータは無いが)

    ビルドは、なるべく外部のサービスに依存しないほうが壊れにくい。 そしてこの判断は、直後に正しかったと証明されます。まさにWordPressへの接続が失敗し続ける事態に陥り、案Aを選んでいたら原因調査のためのデプロイすらできなかったからです。

    教科書的な正解と、その場の正解は一致しないことがあります。大事なのはどちらを選ぶかより、選んだ理由を説明できることだと思っています。

    失敗2: ビルドは成功。でもデータが1件も出ない

    2回目のデプロイは43秒で成功し、サイトも表示されました。ところが中身を見ると、こうなっています。

    実績       : まだ実績が登録されていません。
    スキル     : まだスキルが登録されていません。
    ブログ     : まだ記事がありません。
    プロフィール: (WordPress管理画面で作成すると、ここに表示されます)

    WordPress側には投稿7件・実績4件・スキル14件・画像10件が入っています。それが1件も出てきません。

    そして厄介なのが、ビルドが「成功」と表示されていることでした。データ取得に失敗しているのに、エラーとして表面化していません。理由はコードにありました。

    const [profileResult, projectsResult, skillsResult, postsResult] =
      await Promise.allSettled([
        getProfilePage(), getProjects(), getSkills(), getPosts(),
      ]);
    
    const projects =
      projectsResult.status === "fulfilled" ? projectsResult.value : [];

    Promise.allSettled を説明します。JavaScriptで複数の非同期処理(時間のかかる処理)をまとめて実行する方法は主に2つあります。

    Promise.all        … 1つでも失敗したら、その時点で全体が失敗になる
    Promise.allSettled … 全部の結果を待って、成功・失敗を個別に返す(例外を投げない)

    allSettled を選んだのには理由がありました。たとえば「プロフィールページをまだ作っていない」という場合でも、それ以外の部分は表示されてほしいからです。1箇所の欠損でページ全体が真っ白になるのは避けたい。設計としては妥当な判断です。

    ただ今回は、4つ全部が失敗しました。それでも設計どおりに空の配列へ置き換えられ、「まだ登録されていません」という平和な画面が出てしまいます。

    想定していた「一部が欠ける」ではなく「全部が落ちる」が起きたとき、この防御は問題を隠す方向に働きました。

    ここから学べることを一般化すると、こうなります。「壊さない」と「気付ける」は別の要求であり、両立できる。 次の2行があれば、調査は最初の5分で終わっていました。

    if (projectsResult.status === "rejected") {
      console.error("[getProjects] failed:", projectsResult.reason);
    }

    エラーを飲み込む設計自体が悪いのではありません。飲み込んだ痕跡を残さなかったことが問題でした。

    失敗3: 犯人を推測して、2回外した

    接続先の設定を疑いました。GraphQLの接続先はこう書かれています。

    const endpoint =
      process.env.NEXT_PUBLIC_WORDPRESS_API_URL ?? "http://localhost:8080/graphql";

    process.env.○○ は環境変数を読み取る書き方です。環境変数は、コードの外側から値を渡す仕組みで、「開発中はローカルのWordPress、本番は本番のWordPress」のように環境ごとに変わる値を扱うのに使います。

    ?? はnull合体演算子といって、「左側が空だったら右側を使う」という意味です。つまりこのコードは、環境変数が読めなければ黙ってローカル用のURLにフォールバック(代替)するという動きをします。Vercel上にローカルのWordPressは存在しないので、全部失敗する——筋は通っています。

    Vercelの管理画面を見ると、環境変数が Sensitive(機密)という設定で登録されていました。これは値を秘匿するための設定で、登録後は管理画面からも中身が見えなくなります。

    ここで違和感を持ちました。この環境変数には NEXT_PUBLIC_ という接頭辞が付いています。Next.jsではこの接頭辞に特別な意味があり、付いた値はビルド時にコードへ直接埋め込まれます。

    【ビルド前】process.env.NEXT_PUBLIC_WORDPRESS_API_URL
    【ビルド後】"https://cms.kisaku.site/wp/graphql"   ← 値そのものに置き換わる

    埋め込まれたコードはブラウザに配信されるので、この値は原理的に秘密にできません。だから「公開してよい値にだけ NEXT_PUBLIC_ を付ける」というルールがあり、パスワードやAPIキーに付けてはいけないのもこのためです。

    公開前提の値をSensitiveにするのは矛盾している——そう考えて、いったん削除し、Sensitiveなしで登録し直し、再デプロイしました。

    結果は変化なし。 データは相変わらず空のままでした。

    この時点で、私は原因を2回外しています。しかも困ったことに、Sensitiveだと私自身も値を確認できないので、「URLが間違っているのか」「値が届いていないのか」の区別すらついていませんでした。

    観測できない対象を推測で潰しにいっても、当たったか外れたかが分からない。 やるべきだったのは、値を推測することではなく、実際に何が起きているかを表示させることでした。

    決め手: エラーを表示させるためだけのコードを書く

    方針を変えました。エラーが握りつぶされているなら、握りつぶさずにそのまま表示する経路を一時的に作ればいい、という発想です。

    診断専用のURLを1つ追加しました。アクセスすると、次の4つをまとめて返します。

    1. ビルド時に埋め込まれた環境変数の実際の値
    2. GraphQLクライアントが使っているURL
    3. 素のfetchで接続した結果(エラーの中身まで)
    4. アプリと同じ経路で実行した結果

    コードの要点はここです。

    try {
      const r = await fetch(endpoint, { method: "POST", /* ... */ });
      result.rawStatus = r.status;
    } catch (e) {
      result.rawError = {
        name: e.name,
        message: e.message,
        cause: String(e.cause),   // ← これが決定的だった
      };
    }

    e.cause を取っているのが重要な点です。Node.js(サーバー側でJavaScriptを動かす仕組み)の fetch は、通信の失敗をすべて同じメッセージに丸めてしまいます。

    TypeError: fetch failed

    名前解決に失敗しても、接続を拒否されても、証明書が一致しなくても、タイムアウトしても、これだけです。本当の原因は cause というプロパティの中に入っています。

    これはエラーのラッピング(包装)と呼ばれる設計です。低レベルの細かいエラーを、上位の統一されたエラーで包む。使う側が実装の違いを気にしなくて済む利点がありますが、原因を調べるときは包みを開ける必要があります。

    デプロイして、この診断URLを叩いた結果がこれでした。

    {
      "envRaw": "https://cms.kisaku.site/wp/graphql",
      "clientEndpoint": "https://cms.kisaku.site/wp/graphql",
      "rawError": {
        "message": "fetch failed",
        "cause": "Error [ERR_TLS_CERT_ALTNAME_INVALID]:
          Hostname/IP does not match certificate's altnames:
          Host: cms.kisaku.site is not in the cert's altnames:
          DNS:*.gmoserver.jp, DNS:gmoserver.jp"
      }
    }

    SSL証明書の不一致でした。 そして環境変数は正しく届いていたことも同時に分かりました。私が疑って作り直したSensitiveの件は、完全に的外れだったわけです。

    なぜブラウザでは繋がり、サーバーからは繋がらないのか

    この問題が見つけにくかった理由を説明します。ブラウザからは正常にアクセスできていたのです。鍵マークも付き、GraphQLもデータを返していました。

    理解するには、HTTPS通信の最初の手順を知る必要があります。

    HTTPSの「S」はSecure(安全)の意味で、通信を暗号化します。ただしその前に、相手が本物かどうかを確認する手続きがあります。これをTLSハンドシェイク(TLSは暗号化の規格名、ハンドシェイクは握手)といいます。

    1. こちらが「cms.kisaku.site と話したい」と伝える
    2. サーバーが、そのホスト名に対応する証明書を返す
    3. こちらが「証明書に書かれた名前」と「アクセス先の名前」が一致するか確認する
    4. 一致すれば暗号化通信を開始、しなければ接続を中断

    証明書は、身分証明書のようなものです。「このサーバーは確かに cms.kisaku.site です」と第三者機関が保証したデータで、そこには対象のホスト名が書かれています。

    今回失敗したのは3の段階でした。返ってきた証明書には *.gmoserver.jp としか書かれておらず、cms.kisaku.site が含まれていない。だからNode.jsは「名乗っている相手が違う」と判断して接続を打ち切りました。

    ここで重要なのは、この確認はデータを送る前に行われるということです。実際、Vercelのログには「外部への通信が1本もない」と表示されていました。リクエストを送って失敗したのではなく、送る前の握手で決裂していたのです。

    そしてヘッドレス構成では、この違いが致命的になります。

    従来のWordPress: ブラウザ ──────────────→ WordPress
    今回の構成      : ブラウザ → Next.js(Vercel) → WordPress
                                    ↑
                        ここの通信は、ブラウザからは見えない

    従来の構成なら、ブラウザで確認すれば十分でした。しかしヘッドレスではサーバーがサーバーを呼ぶ経路が主役です。私はずっとブラウザから確認して「サーバー側は正常だ」と判断していましたが、その経路は実際に使われる経路ではなかったのです。

    「自分の環境から動く」は「どこからでも動く」ではない。 検証は、実際に通信する経路で行わないと意味がありません。

    証明書はその後サーバー側で正しく適用され、コードを一切変えずにデータが表示されるようになりました。診断用のURLは役目を終えたので削除しています。

    危なかった話: DNSの設定画面を間違えるところだった

    サイトが動いたので、最後の工程です。kisaku.site というドメインを、新しいサイトに向けます。

    DNSを説明します。インターネット上のサーバーは、本来 216.198.79.1 のような数字(IPアドレス)で識別されます。しかし人間が覚えるのは大変なので、kisaku.site のような名前と数字を対応づける仕組みがあり、これがDNS(ドメインネームシステム)です。電話帳のようなものだと考えてください。

    Vercelにドメインを追加すると、設定すべき内容が表示されます。

    kisaku.site       A      216.198.79.1
    www.kisaku.site   CNAME  e2e46a9cb91ef14a.vercel-dns-017.com

    Aレコードは「この名前は、このIPアドレスです」という対応づけ。CNAMEレコードは「この名前は、別の名前の別名です」という指定です。

    さて、これをどこで設定するか。お名前.comの管理画面には「DNSレコード設定」という、いかにもそれらしいメニューがありました。開いてみると、入力欄が並んだ画面が出てきます。

    ところが、その画面の既存レコード一覧が空でした。いま動いているはずの設定が、1件も表示されません。

    ここで手を止めて、確認しました。

    kisaku.site のネームサーバー: dns01.gmoserver.jp / dns02.gmoserver.jp

    ネームサーバーは、そのドメインの答えを実際に持っているサーバーのことです。DNSには2つの階層があります。

    【上位】ネームサーバー … 「このドメインの答えは、どのサーバーが持っているか」
    【下位】レコード       … 「そのサーバーが持っている、実際の答え」

    つまり答えを持っているのはレンタルサーバー側で、私が開いていたのはお名前.comが提供する別のDNSサービスの画面でした。名前がそっくりなので紛らわしいのですが、まったく別物です。

    そして画面の下には「レコードの登録とあわせてネームサーバーも変更する」というチェックボックスがありました。もしこれを有効にして保存していたら、上位の階層が書き換わります。

    変更前: kisaku.site → gmoserverのDNS(3件のレコードが登録済み)
    変更後: kisaku.site → 別サービスのDNS(レコードは空)

    参照先が空のほうに切り替わるので、ぶら下がるすべての名前が一斉に消えます。

    kisaku.site      → 消える
    www.kisaku.site  → 消える
    cms.kisaku.site  → 消える  ← WordPressごと到達不能に

    cms が落ちればGraphQLも止まるので、新サイトもデータを取得できなくなります。「cms のレコードには絶対に触らない」と決めて作業していたのに、1行も触らないまま巻き添えにするところでした。

    「レコードを触らなければ安全」は成り立ちません。 上位を切り替えれば下位は丸ごと入れ替わります。作業前には、個々の行を見る前に「いま自分はどのゾーンを編集しようとしているのか」を確認する必要があります。

    気付けたのは、一覧が空だったことに違和感を持ったからでした。「あるはずのものが無い」は、たいてい自分が別の場所を見ている合図です。

    正しい場所で切り替える

    本来の作業場所は、レンタルサーバーのコントロールパネルでした。開くと、期待どおりのレコードが並んでいます。

    (なし) 標準 A     ← ルートドメイン(kisaku.site そのもの)
    www     標準 A
    ftp / pop / smtp / imap  標準 A   ← メール関連
    (なし) MX 10              ← メールの宛先
    (なし) TXT               ← 送信元の正当性を示す設定

    ルートドメインの行を編集すると「別サーバーを利用する」という選択肢があり、IPアドレスを指定できました。ここにVercelの 216.198.79.1 を入れます。

    画面には注意書きもありました。「ドメインのホスト名なしはAレコードのみ設定できます」。

    これはDNSの仕様上の制約です。理由を説明します。CNAMEは「この名前は別名です」という宣言なので、そのホスト名に他のレコードを共存させられません。ところがルートドメイン(www などが付かない、ドメインそのもの)には、DNSの仕組み上どうしても必要なレコードがあります。

    NS  … このドメインを管理するネームサーバー(必須)
    SOA … ゾーンの管理情報(必須)
    MX  … メールの宛先(メールを使うなら必要)

    これらと共存できないため、ルートドメインにCNAMEは置けないのです。VercelがルートにはAレコード、www にはCNAMEを指定してきたのは、この事情を踏まえたものでした。

    www にCNAMEを使うのは、接続先のIPが変わっても自動で追従できるからです。Aレコードで直接IPを書くと、相手のインフラ変更のたびに手で直す必要が出ます。

    失敗4: 末尾のドット1文字でエラー

    www の設定で、保存に失敗しました。

    「指定先」の入力内容が正しくありません。
    ・ドットの連続やドットとハイフンを連続することはできません。

    原因は末尾のドットでした。Vercelが表示していた値は e2e46a9cb91ef14a.vercel-dns-017.com. と、最後にドットが付いています。

    これはDNSの正式な記法で、「完全修飾名なので、これ以上ドメイン名を補完しない」 という意味を持ちます。ドットが無いと、システムによっては自動的に自分のドメイン名を後ろに付け足してしまうことがあり、それを防ぐための表記です。

    ところがこの入力欄は、ドットを受け付けない実装でした。外して入れ直したら通りました。

    情けないのは、私が事前に「入力欄によってはドットでエラーになるので、その場合は外してください」と自分で書いていたことです。注意点を知っていることと、実行時にそれを適用することは別だと痛感しました。

    反映を待つ ── 「変わらない」の正しい読み方

    DNSを変更した直後に確認すると、まだ古い値が返ってきます。ここで「保存できていないのでは」と疑って設定を触り直すと、事態が混乱します。

    DNSの答えにはTTL(Time To Live)という有効期限が付いています。今回は600秒でした。世界中のDNSサーバーは、一度取得した答えをこの秒数のあいだ保存(キャッシュ)し、その間は問い合わせ直しません。毎回問い合わせていたら世界中のDNSが処理しきれないためです。

    つまり変更直後に古い値が返るのは正常です。問題は「保存されていない」のか「キャッシュを見ているだけ」なのかをどう区別するかでした。

    ここで役に立ったのがTTLの残り秒数です。実際に観測した値を並べます。

    kisaku.site  A 133.130.64.184  ttl600   ← 取得したばかりのキャッシュ
    kisaku.site  A 133.130.64.184  ttl218   ← 382秒前に取得された答えを見ている
    kisaku.site  A 216.198.79.1    ttl600   ← 期限切れ後に取り直した結果

    TTLはキャッシュの残り寿命なので、600から218に減っていれば「これは382秒前の答えだ」と分かります。いま見ている情報がいつ時点のものかを読み取れるわけです。ゼロになるのを待って問い合わせ直せば、確実に現在の状態が得られます。

    なおブラウザで確認する場合は、パソコンとブラウザ自身のキャッシュも重なります。シークレットウィンドウで開くか、別のネットワークから見るのが手軽です。「反映されない」の多くは、どこかの層にキャッシュが挟まっているだけです。

    最終的にこうなりました。

    kisaku.site       → 216.198.79.1                          Vercel
    www.kisaku.site   → CNAME e2e46a9cb91ef14a.vercel-dns...  Vercel
    cms.kisaku.site   → 133.130.64.184                        無事
    

    守りたかった cms は最後まで無傷でした。https://www.kisaku.site/ で新サイトが表示され、SSL証明書も自動で発行されて、公開完了です。

    今回の学び

    技術的な収穫は、fetch failed の cause を必ず見ることでした。表面のメッセージだけ読んでいる限り、永遠に原因にたどり着けません。包まれたエラーは、包みを開けないと中身が分からない。

    進め方の話としては、推測から観測に切り替えるのが遅かったという反省です。今回、原因を2回外しました。どちらも「たぶんこれだろう」で設定をいじり、結果が変わらず、当たったのか外れたのかも判然としない、という時間の使い方です。

    最終的に効いたのは、エラーを表示させるためだけのコードを書くという、一見遠回りな手段でした。書くのに10分もかかっていません。推測で1回試すコストと、確実に答えを得るコストは、思っているほど差がない。

    そして、DNSのように間違えると広範囲が止まる作業では、操作の前に「いま自分はどの階層を見ているか」を確認する習慣が要る、ということ。レコードを1行も触らずにサイト全体を落としかけた今回は、それを実感する良い機会になりました。

    次回は、この作業を通して出てきた用語や仕組みを、もう少し体系立てて解説します。

    契約前に知っておきたかったこと

    ドメインとサーバーを同じ会社で契約すると、DNSの設定画面が2か所に増えます。

    ドメインを買った側の管理画面にもDNSの設定欄があり、レンタルサーバー側にもあります。そして実際に参照されるのは片方だけです。

    厄介なのは、参照されないほうに設定してもエラーが出ないことです。保存は成功し、画面上も正しく見えます。ただ、何も起きません。

    この1点を知っているだけで、丸一日の調査が要らなくなります。

    お名前.comでドメインを取得する


  • ポートフォリオサイトをヘッドレスWordPress+Next.jsで作り直した話 (2) WordPress構築編

    ポートフォリオサイトをヘッドレスWordPress+Next.jsで作り直した話 (2) WordPress構築編

    「サイト制作の流れ」シリーズ2回目です。前回は全体の設計方針を書いたので、今回は実際にWordPress側をどう構築したかをまとめます。

    Docker Composeでローカル環境を用意する

    まず、ローカル開発用にDocker ComposeでWordPress環境を用意しました。db(MySQL)とwordpress(WordPress本体)の2コンテナ構成にしています。

    services:
      db:
        image: mysql:8.0
        environment:
          MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
          MYSQL_DATABASE: ${MYSQL_DATABASE}
        volumes:
          - db_data:/var/lib/mysql
    
      wordpress:
        image: wordpress:latest
        depends_on:
          - db
        ports:
          - "8080:80"
        volumes:
          - wordpress_data:/var/www/html
          - ./wp-content:/export/sd220/www/jp/r/e/gmoserver/3/2/sd1108332/cms.kisaku.site/wordpress-6.2.2-ja-jetpack_webfont-undernavicontrol/wp-content
    

    ポイントは volumes の分け方です。WordPress本体(core)は named volume に置いたままにして、wp-content だけをホスト側にバインドマウントしています。こうしておくと「コア本体の更新はコンテナにお任せ、プラグイン・テーマ・カスタムコードだけ手元のファイルとしてGit管理する」という役割分担がしやすくなります。

    Docker構成とWordPress+ACF+WPGraphQLのスタック図

    カスタム投稿タイプで「実績」「スキル」を作る

    標準の「投稿」をそのまま使うのではなく、実績用に project、スキル用に skill というカスタム投稿タイプを用意しました。それぞれにカテゴリ用のタクソノミー(project_category, skill_category)も追加しています。標準の投稿は今回作ったブログ用にそのまま使う形にしました。

    ACFでカスタムフィールドを持たせる

    実績には「役割(role)」「期間(period)」「使用技術(toolsused)」「外部URL」、スキルには「習熟度(level)」といった、投稿本文だけでは表現しづらい構造化データをACF(Advanced Custom Fields)のフィールドグループとして追加しました。使用技術は本来リピーターフィールドで持たせたいところですが、リピーターはACF Proの機能なので、今回は「HTML、CSS、JavaScript」のように読点区切りのテキスト1本で保存し、フロントエンド側で配列に分解する方式にしています。

    WPGraphQLでAPI化する

    WordPressの管理画面から入力したデータをNext.js側から取得できるように、WPGraphQL と WPGraphQL for ACF の2つのプラグインを導入しました。これにより、投稿タイプやACFフィールドがそのままGraphQLのスキーマに反映され、/graphql エンドポイント経由でJSON形式のデータとして取得できるようになります。管理画面にGraphiQL IDEという画面が追加されるので、実際にNext.js側のコードを書く前に、ここでクエリが正しく書けるか確認しながら進められるのも便利でした。

    次回は、このAPIをNext.js側からどう呼び出しているか、そして実装中に実際にハマった不具合について書きます。

    共用サーバーに置いてみて分かったこと

    WordPressの設置そのものは、管理画面から数分で終わります。つまずいたのはそのあとでした。

    ひとつはWAF(攻撃を遮断する仕組み)の誤検知です。記事の投稿が突然できなくなり、原因が自分のサーバー側にあると気づくまで時間がかかりました。共用サーバーでは既定で有効になっていることが多く、「サイトは動くのに、特定の操作だけ失敗する」という形で出ます。

    もうひとつは、PHPのバージョンを自分で上げる必要があること。放置すると、使っているソフトの更新が止まります。

    どちらも管理画面から対処できます。逆に言えば、そこに設定項目があると知らないと、原因にたどり着けません。

    お名前.comのレンタルサーバー