← ミライズ労務コンサルティング開発知識シリーズ 目次(全6部)
第 6 部
hokama@mirise ~ $ grep -E '^[0-9]{4}' employees.csv

寿命の長い3つ

HTTP、正規表現、SQL。
この3つは、フレームワークが何度入れ替わっても残り続けます。
だから学習の投資対効果が、他とは比較にならないほど高い。

$ why

なぜこの3つなのか

共通点があります。どれも半世紀前後を生き延びています。

技術成立現在
正規表現1950年代に理論、1968年に実装ほぼそのまま使われている
SQL1974年に IBM で考案ほぼそのまま使われている
HTTP1991年版は上がったが、考え方は同じ

この間に、無数のフレームワークが登場して消えました。第2部から第4部で扱った Next.js も、いずれ別のものに置き換わります。第3部で見たとおり、Gemini CLI は2026年に個人向け提供を終えました。

一方で grep は半世紀前の設計のまま動いています。SELECT も同じです。

投資対効果

Next.js を学ぶ時間は、Next.js が使われている間だけ回収できます。この3つを学ぶ時間は、おそらく生涯回収し続けられます。

しかも3つとも、覚える量が驚くほど少ない。実務で必要なのは、それぞれ1時間程度の内容です。

それぞれの使いどころ

技術いま効く場面
HTTPMCP サーバーの自作。API の疎通確認。エラーの切り分け
正規表現シェル、エディタの一括置換、コード、CSV の整形。すべてで同じ記法が使える
SQLDrizzle が生成するクエリの検証。遅い原因の特定。Supabase の RLS
$ curl -v

HTTP ─ 往復の作法

リクエストを送り、レスポンスが返る。ただそれだけの取り決めです。

この章の要点
  • HTTP は1往復で完結する。前回のことを覚えていない
  • だから毎回、自分が誰かを名乗る必要がある(認証ヘッダ)
  • リクエストはメソッド・パス・ヘッダ・ボディの4つでできている

第5部と同じ構造をしている

HTTP には重要な性質があります。ステートレス ── サーバーは前回のやりとりを覚えていません。

第5部で見た「モデルはリクエスト間で何も記憶していないため、毎回コンテキスト全体を送り直している」と、まったく同じ構造です。覚えていない相手と話すには、毎回すべてを持参するしかありません。

ログイン状態が保たれているように見えるのは、ブラウザが毎回トークンを送り直しているからです。サーバーが覚えているのではありません。

リクエストの中身

POST /api/check HTTP/1.1 ← メソッドとパス Host: miraise.example.com ← ヘッダ(宛先) Authorization: Bearer eyJhbGc... ← ヘッダ(身分証) Content-Type: application/json ← ヘッダ(中身の形式) ← 空行が区切り {"employee_id": 1001} ← ボディ(本文)

返ってくる側も同じ形です。

HTTP/1.1 200 OK ← ステータスコード Content-Type: application/json {"result": "該当", "grade": 22}

メソッド ── 何をしに来たか

メソッド意味2回送ったら
GET取得する。何も変えない同じ結果
POST新しく作る2つできる
PUT丸ごと置き換える同じ結果
PATCH一部だけ更新する場合による
DELETE削除する同じ結果

右の列が実務的に重要です。何度送っても結果が変わらない性質を冪等(べきとう)と呼びます。

通信が途中で切れたとき、再送してよいかどうかがこれで決まります。GET は安心して再送できます。POST は再送すると二重登録になります。「送信ボタンを2回押さないでください」という注意書きの正体です。

認証情報をヘッダに入れる理由

API キーやトークンは Authorization ヘッダに入れます。URL に含めてはいけません。

URL はあらゆる場所に残ります。サーバーのアクセスログ、ブラウザの履歴、プロキシの記録、他サイトへ飛ぶときのリファラ。ヘッダはこれらに残りません。

第4部の "use client" の話と同じ系統の注意です。秘密情報を、残る場所に置かない。

$ status

ステータスコード ─ 誰が直すのか

3桁の数字の、最初の1桁だけで用が足ります。

2xx 成功した 3xx 場所が移った 4xx こちらが悪い 5xx 向こうが悪い 追いかける リクエストを直す 待つ・連絡する 200 201 204 301 302 304 400 401 403 404 500 502 503
4xx と 5xx の区別が要点です。自分のコードを見るのか、相手の復旧を待つのか、これで決まります。

覚えておくと切り分けが速い5つ

コード意味実際の原因
401誰だか分からないトークンが無い、期限切れ、形式が違う
403誰だか分かるが、許可がない権限設定の問題。Supabase なら RLS
404その場所に無いURL の誤り。または「見せたくないので無いことにした」
429送りすぎレート制限。時間をおいて再送する
500サーバー内部でエラー相手側のコードが落ちている
401 と 403 の違いが効く

この2つを混同すると、原因の切り分けで遠回りします。

401 は「名乗っていない、または名乗り方が間違っている」。認証情報を見直します。403 は「名乗れているが、それを見る資格がない」。権限設定を見直します。

Supabase で 403 が返るなら、認証は通っていて RLS に弾かれている可能性が高い。この判断が1秒でできるかどうかで、調査時間がまるで変わります。

手元で確認する

$ curl -i https://example.com/api/check # -i でヘッダも表示。ステータスコードが1行目に出る $ curl -X POST https://example.com/api/check \ -H "Content-Type: application/json" \ -d '{"employee_id": 1001}' # -X でメソッド、-H でヘッダ、-d でボディを指定

MCP サーバーを自作されている場合、curl で直接叩けると切り分けが速くなります。アプリ側が悪いのか、サーバー側が悪いのかを、1コマンドで分けられるからです。

$ grep -E

正規表現 ─ 文字列の形を書く

個別の文字列ではなく、パターンを指定する記法です。

この章の要点
  • 「4桁の数字」「行頭が◯◯」のようなを書ける
  • 記号は10個ほど覚えれば実務に足りる
  • シェル、エディタ、コードで同じ記法が使える

名前の由来は「regular expression(正規な表現)」で、1950年代の数学理論に始まり、1968年に Ken Thompson がテキストエディタへ実装しました。grep という名前も、当時のエディタのコマンド g/re/p から来ています。

記号は10個で足りる

記号意味
.任意の1文字a.c → abc, a1c
*直前を0回以上ab* → a, ab, abb
+直前を1回以上ab+ → ab, abb
?直前を0回か1回ab? → a, ab
[ ]この中のどれか1文字[0-9] → 数字1文字
\d数字1文字[0-9] と同じ
^行の先頭^社員 → 行頭が「社員」
$行の末尾円$ → 行末が「円」
{n}ちょうど n 回\d{4} → 数字4桁
( )まとめる・取り出す後述
|または営業|管理

組み合わせる

# 4桁の数字だけの行 ^\d{4}$ # 電話番号の形 \d{2,4}-\d{2,4}-\d{4} # 日付(2026-07-27) \d{4}-\d{2}-\d{2} # 行頭が「1」で始まる従業員番号 ^1\d{3} # 営業部か管理部の行 営業|管理

実際に使う

$ grep -E '^1\d{3},' employees.csv # 従業員番号が1で始まる4桁の行だけ抜き出す $ grep -cE '営業' employees.csv # 営業部の行数を数える $ grep -vE '^#' config.txt # -v で反転。コメント行以外を表示

VS Code の検索欄でも、右端の .* ボタンを押せば同じ記法が使えます。一括置換のときに効きます。

取り出す ── 丸括弧の使い方

丸括弧で囲んだ部分は、あとから取り出せます。置換では $1\1 で参照します。

# 2026-07-27 を 2026年07月27日 に一括変換 検索: (\d{4})-(\d{2})-(\d{2}) 置換: $1年$2月$3日 # 姓名の順を入れ替える 検索: (\S+),(\S+) 置換: $2,$1

数百行の CSV の書式を揃える、といった作業がこれで数秒になります。

$ careful

正規表現の落とし穴

2つだけ知っておけば、大きな事故は避けられます。

落とし穴1 ── 貪欲マッチ

*+ は、可能な限り長く取ろうとします。これが直感に反します。

# 対象の文字列 <name>山田</name><dept>営業</dept> # 期待:<name> だけ取りたい <.*> → <name>山田</name><dept>営業</dept> 全部取ってしまう # 正しい:? を足して「最短で」にする <.*?> → <name> 期待どおり

*?+? のように ? を後ろに付けると、最短一致に変わります。「思ったより多く取れてしまう」ときは、まずこれを疑ってください。

落とし穴2 ── 正規表現で解いてはいけないもの

対象理由代わりに
HTML の解析入れ子構造は正規表現では原理的に扱えない専用のパーサ
複雑な CSV引用符の中のカンマや改行を判別できないCSV ライブラリ
メールアドレスの厳密な検証正式な規格が複雑すぎる大雑把に見て、実際に送って確かめる
公的番号の検証桁数は見られるが、検査用数字は計算が要る形の確認は正規表現、妥当性は別途
最後の行について

^\d{12}$ は「12桁の数字である」ことしか確認しません。マイナンバーには検査用数字が含まれているため、形が合っていても有効な番号とは限りません

正規表現は形の確認まで、と割り切るのが安全です。妥当性の判定は、その分野の規則に従った別の処理になります。この線引きは、AI が生成したコードでも曖昧にされがちな箇所です。

書いたら必ず試す

正規表現は、頭の中で正しさを確認しにくい記法です。必ず実データで試してから使ってください。

とくに一括置換の前は、まず grep で「何件当たるか」を確認します。想定より多ければ、パターンが広すぎます。

$ grep -cE 'パターン' ファイル # まず件数を確認してから置換する
$ psql

SQL ─ ORM があっても読む理由

Drizzle は SQL を生成します。生成物を読めないと、検算できません。

この章の要点
  • ORM は SQL を隠すが、無くすわけではない
  • 遅い原因は、ほぼ常に生成された SQL の形にある
  • Supabase の RLS も SQL で書く。読めないと何を許可したか分からない
  • 書く順序と実行される順序が違う。ここが最初の関門

基本の形

SELECT name, department, monthly_wage -- 何を FROM employees -- どこから WHERE weekly_hours >= 20 -- 条件 ORDER BY monthly_wage DESC -- 並べ替え LIMIT 10; -- 件数制限

第3部でシェルで書いた処理と、まったく同じことをしています。

シェルSQL
cat / tailFROM
awk '$5 >= 20'WHERE
sort -k4 -nrORDER BY ... DESC
head -10LIMIT 10
cut -f3SELECT の列指定

書く順序と、実行される順序は違う

ここが最初につまずく箇所です。SELECT を先頭に書きますが、実際には5番目に実行されます

書く順序 実行される順序 SELECT FROM WHERE GROUP BY HAVING ORDER BY LIMIT FROM WHERE GROUP BY HAVING SELECT ORDER BY LIMIT
SELECT が1番目から5番目へ移動しています。この一点を知っておくと、多くのエラーの理由が分かります。

この順序が分かると、次の挙動が説明できます。

現象理由
WHERESELECT の別名が使えないWHERE のほうが先に動くため、別名がまだ存在しない
ORDER BY では別名が使えるSELECT の後だから
WHERE で集計関数が使えない集計は GROUP BY 以降。WHERE はその前
集計後に絞るには HAVINGGROUP BY の後に動く条件指定だから

結合 ── 2つの表をつなぐ

SELECT e.name, s.amount FROM employees e LEFT JOIN salaries s ON e.id = s.employee_id WHERE s.month = '2026-07';
種類結果使う場面
INNER JOIN両方に存在する行だけ給与のある従業員だけを見たい
LEFT JOIN左は全部、右は無ければ空欄給与登録漏れを見つけたい

右列の違いが実務では大きい。INNER JOIN だと、給与が登録されていない従業員が結果から静かに消えます。「人数が合わない」の典型的な原因です。

$ EXPLAIN

遅い原因は、ほぼ2つ

インデックスの欠落か、N+1 か。この2つで大半が説明できます。

原因1 ── インデックスがない

インデックスは索引です。本の巻末索引と同じ役割で、「この語はこのページ」という対応表を別に持っておく仕組みです。

索引が無ければ、目的の語を探すのに1ページ目から全部めくることになります。データベースでも同じで、これを全件走査と呼びます。1万件なら気づきませんが、100万件になると止まります。

-- WHERE や JOIN で使う列に付ける CREATE INDEX idx_salaries_employee ON salaries(employee_id); -- 実際に使われているか確認する EXPLAIN ANALYZE SELECT * FROM salaries WHERE employee_id = 1001;

EXPLAIN の結果に Seq Scan(全件走査)と出ていれば索引が効いていません。Index Scan なら効いています。この2語だけ見分けられれば十分です。

付けすぎない

索引は書き込みを遅くします。行を1つ追加するたびに、索引も更新する必要があるからです。

付けるのは WHEREJOINORDER BY で実際に使っている列だけにしてください。「念のため全列に」は逆効果です。

原因2 ── N+1 問題

ORM を使うと必ず出会う罠です。従業員100人の一覧に給与を並べるだけで、101回の問い合わせが飛びます。

N+1 が起きている 結合でまとめる 従業員100件を取得 1件ごとに給与を問い合わせ 合計 101 回 結合して一度に取得 追加の問い合わせなし 合計 1 回
1回あたりは速くても、100回分の往復時間が積み上がります。件数が増えるほど比例して遅くなります。
// N+1 が起きる書き方 const 従業員一覧 = await db.select().from(employees); for (const 人 of 従業員一覧) { 人.給与 = await db.select().from(salaries) // ← 100回飛ぶ .where(eq(salaries.employeeId, 人.id)); } // 結合して1回で取る const 結果 = await db.select() .from(employees) .leftJoin(salaries, eq(employees.id, salaries.employeeId));
検算の観点

N+1 は、AI が生成したコードでも普通に発生します。ループの中にデータベースへの問い合わせがあれば、それが該当します。

開発中はデータが少ないので気づきません。顧問先のデータを入れた本番で初めて表面化します。第2部に書いた「出てきたものを疑う力」が、最も具体的に効く場面のひとつです。

確認は簡単で、ループの中にデータベース呼び出しがないかを見るだけです。

Supabase の RLS も SQL で書く

行単位のアクセス制御(RLS)は、SQL の条件式として記述します。

-- 自分の会社の行だけ見られるようにする例 CREATE POLICY "own_company_only" ON employees FOR SELECT USING (company_id = (auth.jwt() ->> 'company_id')::uuid);

USING の中身が、WHERE と同じ条件式です。これが読めないと、自分が何を許可したのかを確認できません。

顧問先のデータを扱う以上、ここは AI に任せきりにせず、自分の目で読める状態にしておく価値があります。

$ common

3つに共通すること

同じ性質を持っているから、まとめて1つの資料にしました。

性質内容
寿命が長い半世紀近く生き残り、当面消える気配がない
移植できる言語・フレームワーク・OS を問わず同じ知識が通用する
量が少ない実務に必要な範囲は、それぞれ1時間程度
検算に使えるAI が生成したものを疑うための道具になる

最後の行が、この資料の趣旨です。

第2部の冒頭で、AI が強いのは真ん中(実装)で、人間に残るのは両端 ── 何を作るかの指定と、出てきたものの検証 ── だと書きました。

この3つは、後者のための道具です。

技術検算できるようになること
HTTPエラーが自分側か相手側か。認証情報を安全な場所に置いているか
正規表現そのパターンが広すぎないか。形の確認と妥当性判定を混同していないか
SQLクエリが N+1 になっていないか。RLS が意図どおりか
学ぶ順序

正規表現 → SQL → HTTP を勧めます。

正規表現は今日から使えて、効果が即座に出ます。SQL は Supabase を使っている以上、避けて通れません。HTTP は MCP サーバーの自作で詰まったときに読めば足ります。

いずれも、通読してから使うものではありません。手を動かして、詰まったら引くという使い方が向いています。

6部を通して

第1部でシェルの原則、第2部で層の全体像、第3部で実例、第4部で Node と Next.js の内側、第5部で費用構造、そして第6部で寿命の長い技能を扱いました。

第1部に書いた1969年の設計思想が、この3つにもそのまま流れています。一つのことをうまくやり、テキストを共通語にし、組み合わせて使う。

道具は入れ替わります。原則は残ります。