---
title: 勉強会_ジェネレーター実装解説
tags: 
author: [smile_yukiko_it](https://image.docswell.com/user/smile_yukiko_it)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/V7PK8MZDJ8.jpg?width=480
description: https://zenn.dev/yukiko_sapporo/articles/59e67faa3a509c
published: June 01, 26
canonical: https://image.docswell.com/s/smile_yukiko_it/K1QX8G-2026-06-01-044624
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/V7PK8MZDJ8.jpg)

エンジニア勉強会
ジェネレーターツールの実装解説
新人向け（1行ずつ）＋ 中堅向け（設計・UML）
題材：最小版ジェネレーター mini-gen（入力 → 生成 → 出力 ＋ セルフテスト）
WHY → WHAT → HOW ／ 人ではなく仕組み


# Page. 2

![Page Image](https://bcdn.docswell.com/page/2JVVN9WGJQ.jpg)

全体像
ツールは「4つの役割」でできている
①
②
③
④
入力 INPUT
生成 GENERATE
出力 OUTPUT
セルフテスト
ユーザーが値を入れる（&lt;input&gt;）
入力から文字列を組み立てる
作った文字列を画面に出す
起動時に壊れていないか確認
うさうさラーメン店のたとえ：①注文を聞く → ②作る → ③ 出す → ④ 味見。当たり前の流れをコードに分けただけ。


# Page. 3

![Page Image](https://bcdn.docswell.com/page/5EGLKZGDJL.jpg)

第1部
新人向け：1行ずつ読む
役割で小さく分ける／外の入力は疑う／作ると出すを分ける／起動時に味見


# Page. 4

![Page Image](https://bcdn.docswell.com/page/4JQYNLXX7P.jpg)

新人向け｜① 入力を読む
val()：入力欄の値を安全に読む
function val(id){
var el = document.getElementById(id); // idで部品を取り出す
return el ? el.value : &quot;&quot;;
// 無ければ空文字（落ちない）
}
getElementById：idで部品を1つ探す
三項演算子 el ? A : B は「あればA、無ければB」
見つからなくてもエラーで止まらない。これが「落ちないコード」の第一歩。


# Page. 5

![Page Image](https://bcdn.docswell.com/page/K74WGD62E1.jpg)

新人向け｜② 入力を疑う
esc()：危険な文字を無害化（XSS対策）
function esc(s){
return String(s).replace(/[&lt;&gt;&amp;]/g, function(c){
return {&quot;&lt;&quot;:&quot;&amp;lt;&quot;, &quot;&gt;&quot;:&quot;&amp;gt;&quot;, &quot;&amp;&quot;:&quot;&amp;amp;&quot;}[c];
});
}
&lt; &gt; &amp; を表示用の安全な表記に置き換える
合言葉：外から来た文字は、まず疑う。
例）&lt;script&gt; をそのまま画面に出すと危険 → &amp;lt;script&amp;gt; にして無害化


# Page. 6

![Page Image](https://bcdn.docswell.com/page/LJ1YDZVKEG.jpg)

新人向け｜③ 生成（心臓）
generate()：入力から文章を組み立てる
function generate(inp){
var course = inp.course || &quot;（コース名なし）&quot;; // 未入力でも落ちない
var who = inp.audience ? inp.audience+&quot;向け&quot; : &quot;受講者向け&quot;;
var md = &quot;&quot;;
md += &quot;# &quot; + esc(course) + &quot; 設計メモ\n\n&quot;; // 見出し
md += &quot;- 対象：&quot; + esc(who) + &quot;\n&quot;;
// 箇条書き
return md;
// 作るだけ。画面には触らない
}
大事：この関数は「作る」だけ。「出す」のは別の係（render）。役割を分けるとテストしやすい。


# Page. 7

![Page Image](https://bcdn.docswell.com/page/GJWGY9LP72.jpg)

新人向け｜④ 出力と流れ
render() と doGenerate()：出して、つなぐ
function render(md){
document.getElementById(&quot;out&quot;).textContent = md; // 安全に表示
}
function doGenerate(){
var inp = collectInput();
// ①集める
if(!inp.course){ alert(&quot;...&quot;); return; } // ②検証（早期リターン）
render(generate(inp));
// ③生成 → ④表示
}
流れが「集める→検証 →生成 →表示」と一直線。読めば分かるのが良いコード。


# Page. 8

![Page Image](https://bcdn.docswell.com/page/4EZLX9M673.jpg)

新人向け｜セルフテスト
起動時に「味見」を自動化する
T(&quot;生成に見出しが入る&quot;, function(){
return generate({course:&quot;テスト&quot;}).indexOf(&quot;# テスト&quot;) &gt;= 0;
});
✓ PASS
generate() を実際に呼び、結果に見出し
が含まれるか確認。含まれれば合格。
人が毎回手で確認しなくて済む ＝ 「人ではなく仕組み」。壊れたら起動時に赤く分かる。


# Page. 9

![Page Image](https://bcdn.docswell.com/page/Y76W4KNL7V.jpg)

第2部
中堅向け：設計の意図とUML
副作用を1か所に閉じ込める／純粋ロジックでテスト容易に／追加は「分岐＋テスト」をセットで


# Page. 10

![Page Image](https://bcdn.docswell.com/page/G75MQP5M74.jpg)

中堅向け｜関心の分離（SoC）
単方向の流れ：副作用は1か所に
DOM入力
collectInput
generate
generate() は純粋関数に近い
→ 同じ入力なら同じ出力／DOMに副作用なし → テストが容易
render() だけが DOM に触れる
→ 副作用の出口を1か所に閉じ込めるのが保守性の肝
実物：generate() → generateMarkdown() の switch ルーター（16成果物へ振り分け）
render
DOM出力


# Page. 11

![Page Image](https://bcdn.docswell.com/page/9J29P6NRER.jpg)

中堅向け｜クラス図（UML相当）
責務の地図：UI / Controller / Service / View / Test
&lt;&lt;UI&gt;&gt; InputForm
&lt;&lt;Model&gt;&gt; InputData
reads
#course / #audience
#genBtn
course: string
audience: string
click
&lt;&lt;Controller&gt;&gt; doGenerate
collectInput()
検証（早期return）
&lt;&lt;Service&gt;&gt; Generator
generate(inp): md
esc(s): string
&lt;&lt;Test&gt;&gt; SelfTest
runSelfTests()
T(name, fn)
MVC＋Service＋Test。役割が一方向に流れ、依存が循環しない。
&lt;&lt;View&gt;&gt; render
DOMに出力
textContentで安全表示


# Page. 12

![Page Image](https://bcdn.docswell.com/page/DEY459P5JM.jpg)

中堅向け｜シーケンス（UML相当）
実行時の流れ：click から表示まで
User → InputForm : click
InputForm → doGenerate()
doGenerate → collectInput() : 入力を集める
collectInput → doGenerate : inp
doGenerate : course空? ⇒ alert して return（早期リターン）
doGenerate → generate(inp) : 文章を組み立て
generate → doGenerate : md（Markdown）
doGenerate → render(md) : 出力
render → DOM(out) : textContent をセット（表示）
生成（純粋）と表示（副作用）が分かれているので、各ステップを単体で検証できる。


# Page. 13

![Page Image](https://bcdn.docswell.com/page/VJNYNL5478.jpg)

中堅向け｜テスト容易性
なぜこの分け方？ → テストが書けるから
内蔵セルフテスト
起動時に自動実行。生成・反映・エスケープを関数で確認
商用テスト（jsdom）
ブラウザ相当で検証：出力・CSV・XSS・DL・ページエラー0
出荷条件
5回連続オール緑（再現性のある安定）
mini-gen の runSelfTests() は、その最小サンプル。観点を関数で持つ形は実物と同じ。


# Page. 14

![Page Image](https://bcdn.docswell.com/page/YE9PR4Y4J3.jpg)

中堅向け｜拡張の型
新機能を足す手順（破綻させない）
1
generate() に分岐を足す（実物：genXxx()＋switchに1行）
2
入力UIを足す（collectInput に1項目）
3
セルフテストを1〜2個追加（生成される・反映される）
4
商用テストに観点追加 → 5回連続緑を確認
「分岐＋テストをセットで足す」習慣が、16成果物まで破綻なく育てられた理由。


# Page. 15

![Page Image](https://bcdn.docswell.com/page/GE8DWQ6VED.jpg)

まとめ
新人
•
•
•
•
役割で小さく分ける
外の入力は疑う（esc）
作ると出すを分ける
起動時に味見（テスト）
中堅
•
•
•
•
副作用を1か所に閉じ込める
純粋ロジックでテスト容易に
追加は「分岐＋テスト」をセットで
5回連続緑を出荷条件に
小さく理解して、実物で確認。mini-gen が読めれば実物（16成果物）は同じ型の拡大版。
面白きこともなき世を面白く


