はじめに
前回で、Step 1 の単位換算ツールを自宅サーバーで公開し、要件定義書の受け入れ条件をすべて満たしました。第1回で紹介したロードマップでいえば、9段階のうち、最初の2段階が終わったところです。
この回は、ここまでの区切りとして、振り返りを書きます。V字モデルの最後の工程で、ツールごとに書いている「04_振り返り.md」を、シリーズ全体に広げたものです。組み込みでいえば、プロジェクトの終わりの反省会と、そこで残すノウハウ集にあたります。
最後に、ここまでに出てきた Web の概念を、組み込みの言葉に置き換えた対応表を1つにまとめました。同じように組み込みから Web に入る方の、手元の早見表として使っていただければうれしいです。
Step 0・1 で作ったもの
| Step 0:hello | Step 1:unitconv | |
|---|---|---|
| 何をするツールか | 「Hello」とサーバーの時刻を表示する | 血液検査の値の単位を換算する |
| 画面の作り方 | サーバーのプログラムの中で HTML を組み立てる | Vite でビルドした画面のファイルを返す |
| 計算する場所 | サーバー(時刻を取るだけ) | ブラウザ |
| 主に学んだこと | ビルド、常駐、中継、認証、公開の経路 | HTML、TypeScript、イベント、Vite、配信 |
| 組み込みでいうと | Lチカ | 入力を読み、計算して、表示するファーム |
どちらのツールにも、次の4つの文書を残しています。
| 文書 | 内容 | 分量 |
|---|---|---|
| 01_要件定義.md | 何を作るか、何ができたら完成か | A4 で1ページ強 |
| 02_設計.md | どう作るか(構成、画面、データ、ファイルの置き場所) | A4 で2ページほど |
| 03_テスト項目.md | 単体・結合・受け入れの確認と結果 | Step 1 で31項目 |
| 04_振り返り.md | できたこと、つまずいたこと、覚えたこと、次にしたいこと | A4 で2ページほど |
ソースと文書は、ツールごとに Git で管理し、GitHub の非公開リポジトリに送っています。Step の区切りには、step0-done、step1-done という目印(タグ)を付けました。出荷したファームに版番号のラベルを付けておくのと同じで、いつでもその時点の状態を取り出せます。
ご注意:このシリーズで作るツールやアプリは、すべて学習を目的としたものです。医療的な判断には使わないでください。検査結果の解釈や健康上の判断は、必ず医師にご相談ください。
テスト項目書の書き方
第3回で要件定義書と設計書を紹介したので、V字モデルの右側にあたるテスト項目書も、ここで紹介しておきます。確認の手順と結果そのものは、第5回〜第7回で、その作業をした回に分けて載せてきました。ここでは、それを1つの文書にまとめるときの形を示します。
表の列は、すべてのツールで次の7つに統一しています。
| 列 | 書くこと |
|---|---|
| No | 区分の記号と通し番号(U-01 など) |
| 対応要件 | どの要件を確かめる項目か(F-01、N-02 など) |
| 確認内容 | 何を確かめるか |
| 手順 | 何をするか(打つコマンドや、画面での操作) |
| 期待結果 | どうなれば合格か |
| 結果 | OK/NG |
| 日付 | 確かめた日 |
「対応要件」の列があるので、要件定義書の F-01 などから、どの項目で確かめたかを逆にたどれます。組み込みの試験成績書で、試験項目に要求 ID を書いておくのと同じです。
項目は、V字モデルの段階に合わせて4つの区分に分け、No の先頭の記号で見分けられるようにしました。
| 記号 | 区分 | V字モデルで対応する工程 | 確かめる範囲 | Step 1 の項目数 |
|---|---|---|---|---|
| U | 単体確認 | 実装 | 計算の関数だけ | 4 |
| S | 画面の確認 | 実装・設計 | 開発サーバーで、画面の操作と表示 | 10 |
| I | 結合確認 | 設計 | サーバー単体、PM2、Apache 経由 | 12 |
| A | 受け入れ確認 | 要件定義 | 外部の URL から、利用者と同じ使い方で | 5 |

各区分から、実際の項目をいくつか抜き出すと、次のようになります(URL やポートは架空の値です)。
| No | 対応要件 | 確認内容 | 手順 | 期待結果 | 結果 |
|---|---|---|---|---|---|
| U-01 | F-01、F-03 | 血糖の換算 | 100 mg/dL → SI | 5.55 mmol/L | OK |
| U-04 | F-01、F-03 | クレアチニンの換算 | 100 mg/dL → SI | 8840.00 µmol/L | OK |
| S-02 | F-03 | 逆向きの換算 | 血糖・SI → mg/dL・5.55 | 99.99 mg/dL、単位の表示が mmol/L | OK |
| S-06 | F-05 | 負の値 | -5 を入力 | 「0 以上の値を入力してください」 | OK |
| I-03 | 設計 §2 | JS の配信 | curl -sI で JS を要求 | 200、text/javascript、immutable | OK |
| I-06 | 設計 §2 | dist/public の外を狙う | curl –path-as-is で ../../../etc/passwd | Not Found(中身が出ない) | OK |
| I-09 | 設計 §2 | 自動再起動 | プロセスを kill -9 | PM2 が再起動し、再起動回数が増える | OK |
| A-01 | N-02 | 認証の後の表示 | PC で https://example.com/apps/unitconv/ を開く | 認証の後に画面が出る | OK |
| A-04 | N-03 | スマホの実機 | スマホで開いて換算する | はみ出さず、数字のキーボードで換算できる | OK |
大事なのは、「期待結果」を、確かめる前に書いておくことです。期待結果が先に決まっていれば、結果を見たときに合否で迷いません。I-06 のように「何が出ないこと」を期待結果にする項目も、表にしておけば確かめ忘れを防げます。
正直な感想
Step 1 を終えたときに、振り返りとして書いた言葉を、そのまま載せます。
いちばん「わかった」と感じたこと:まだまだふわふわした感じで自分のものにできていないが、処理の流れが見えてきた気がする。
いちばん苦労したこと:全体的に組み込みのベアメタルと違うので、概念だけが多く、少し戸惑っている。
これからについて:TypeScript も初めて(JavaScript すら初めて)だが、計算部分だけで言えば大きくは変わらないので、ついて行けていると勝手に思っている。
戸惑いの正体を考えてみると、覚える量が多いのは、扱う「箱」の数が多いからだと思います。ベアメタルでは、CPU とメモリと周辺回路という、目に見える1台のハードウェアの上ですべてが完結していました。Web では、ブラウザ、Cloudflare、Apache、Node.js、ビルドの道具という別々の箱が、それぞれのルールでつながっています。
それでも、要求が来て、住所を引き算して、ファイルを返し、ブラウザで計算する、という流れは見えてきました。名前はまだふわふわしていても、流れが見えていれば、細かいところは使ううちに定着していくはずだと考えています。
つまずいたことと教訓
Step 0 と Step 1 でつまずいたことを、教訓とあわせて並べます。プログラムそのものより、道具や手順でのつまずきが多かったのが印象的でした。
環境と運用
| つまずいたこと | 解決方法・教訓 | 詳しくは |
|---|---|---|
| Node.js と PM2 が、ユーザーごとに別の系統で入っていた | 使うものを1つに決め、方針を文書にしてから触る | 第2回 |
| 自動起動の設定に、VS Code の PATH が紛れ込んだ | 必要なフォルダだけを指定して登録し直す | 第2回 |
| 中継するパスには .htaccess の認証が効かない | URL のパスに対して、サイト全体の設定で認証をかける | 第2回 |
| 設定を1つ変えると、同じサーバーのブログまで止まるおそれ | コピー → 差分確認 → 文法チェック → 反映 → 回帰テスト、の順を崩さない | 第2回・第7回 |
理解
| つまずいたこと | 解決方法・教訓 | 詳しくは |
|---|---|---|
| Vite の base(住所の足し算と引き算)がつかめなかった | 要求の流れを図で1手ずつ追う | 第4回 |
| 「転送」が中継とリダイレクトのどちらかで混乱した | 誰が動くか(サーバーが渡すのか、ブラウザが要求し直すのか)で区別する | 第4回 |
1行に詰まった書き方(ITEMS.find((it) => …))が読めなかった | 部品に分解し、C で書いたらどうなるかに置き換える | 第5回 |
Git
| つまずいたこと | 解決方法・教訓 |
|---|---|
| 保存し忘れて、空のファイルをコミットした | コミットの前に、エディタの未保存の印と git status を確かめる |
日本語のファイル名が \350\250… と表示された | git config --global core.quotepath false |
| 初期のブランチ名が master だった/リポジトリ名が決めた規則と違った | 初期値を main に設定し、名前は早いうちにそろえる |
| push で認証エラーになった | VS Code のターミナルを開き直す |
| コミットし忘れて、別の変更と混ざりかけた | 動作確認が OK になったら、次の話題に移る前にコミットする |
Git のつまずきは、番外編で詳しくまとめています。
こうして並べると、ほとんどが組み込みの現場でも身につけてきた習慣で防げるものでした。「現状を調べてから触る」「変更は差分を確かめてから」「試験に通ったら記録を残す」。道具が変わっても、効くのは同じ習慣だと感じています。
Web と組み込みの対応表
このシリーズで、新しい概念に出会うたびに作ってきた「組み込みでいうと」を、1つにまとめました。まずは全体を、層ごとに大づかみに置き換えた図です。

以下は、項目ごとの対応表です。
道具とビルド
| Web の概念 | 組み込みでいうと |
|---|---|
| tsc(TypeScript のコンパイラ) | コンパイラ |
| Vite のビルド | コンパイル&リンクして ROM イメージを作る |
| Vite の開発サーバーと自動更新(HMR) | ICE につないで、保存のたびに書き込みとリセット |
base(/apps/<ツール名>/) | リンカスクリプトで指定する配置アドレス |
| ファイル名のハッシュ(index-xxxx.js) | ファーム名に CRC を含めて、版の取り違えを防ぐ |
| npm と package.json | パッケージマネージャと make、部品表 |
| node_modules/ | SDK のライブラリフォルダ(構成管理には入れない) |
| ソースマップ(.map) | アドレスとソース行の対応を持つデバッグ情報 |
strict: true の型チェック | -Wall -Werror |
TypeScript の書き方
| Web の概念 | 組み込みでいうと |
|---|---|
interface / type | struct / typedef |
| ユニオン型・リテラル型 | enum |
readonly 配列 | const 配列 |
undefined | NULL |
結果型 { ok: true; value } | { ok: false; error } | タグ付き共用体(タグの確認を型チェックが強制) |
export / import | ヘッダの公開宣言(extern)と #include |
| アロー関数とクロージャ | 関数ポインタ+コンテキスト(void *ctx) |
as(型の指定) | キャスト |
async / await | DMA を起動して、完了割り込みを待つ |
画面とイベント
| Web の概念 | 組み込みでいうと |
|---|---|
| HTML の部品と id | 基板の部品と、レジスタの名前 |
getElementById | レジスタのアドレスを取得する |
.value を読む/.textContent に書く | 入力レジスタを読む/表示器のレジスタに書く |
| イベント(change、input) | 割り込み要因 |
| イベントループ | 実行環境が持つメインループ |
addEventListener | 割り込みベクタへのハンドラ登録 |
console.log | UART への printf デバッグ |
通信とサーバー
| Web の概念 | 組み込みでいうと |
|---|---|
| HTTP の要求と応答 | コマンド/レスポンス型の通信プロトコル |
| ステータスコード(200、301、404…) | 応答コード(ACK/NAK) |
| リダイレクト(301、302) | 「このアドレスへ再送して」という応答。再送はホストが行う |
| 中継(Apache の ProxyPass) | 中継器、ルーティングテーブルの1行 |
| サーバーが base を引く | ペリフェラルのアドレスデコード |
| 境界チェック | 外から受け取った添字の範囲チェック |
| PM2 | ウォッチドッグ付きの起動管理 |
pm2 save | 起動構成を不揮発メモリに書き込む |
| 環境変数(env) | ディップスイッチ、設定用 EEPROM |
| ログのタイムスタンプ(末尾の Z) | UTC で記録した RTC の時刻 |
もちろん、どれも完全に同じではありません。違いに気づいたところが、そのまま理解が深まったところでもありました。たとえば、TypeScript の型チェックは、C のコンパイラよりも一歩踏み込んで、NULL やタグの確認漏れまで見張ってくれます。
AI と進めていて役立ったこと
このシリーズの学習は、AI(Claude)に質問しながら進めています。ここまでで、役に立ったと感じた進め方を残しておきます。
前提と方針を文書にして、毎回読ませる
サーバーの構成や、ツール開発で守る「決定事項」を1つの文書にまとめ、AI が会話のたびに読む場所に置きました。あわせて、自分の経歴と、教え方の希望(細かいステップで、組み込みの言葉に置き換えて、1回に詰め込みすぎない)も文書にしています。会話が変わっても前提がぶれず、提案が毎回少しずつ違う、ということが減りました。
わからない1行は、そのまま聞く
ITEMS.find((it) => it.id === id) や valueInput.value.trim() のように、1行の意味がわからないときは、その行をそのまま貼って「これは何?」と聞きました。部品に分解して、C ならどう書くかと並べて説明してもらうと、ほとんどの場合すっきりしました。
図にしてもらう
文章の説明でわかったような、わからないようなときは、図にしてもらいました。base の足し算と引き算や、開発中と本番の違いは、要求の流れを1手ずつ進める図にしてもらうことで、整理できました。この連載の図も、そのときの図をもとにしています。
AI の説明も、コマンドで確かめる
AI の説明が、いつも正しいとは限りません。実際、「開発サーバーで / を開くと案内のページが出るはず」という説明が、試してみると自動で転送される動きだったこともありました。そのときは、curl で応答を見て、302 という転送が返っていることを確かめ、正しい理解に直せました。
説明を鵜呑みにせず、手元で動かして確かめる。組み込みで、データシートの記述を実機で確かめるのと同じ姿勢が、ここでも大事だと感じています。
これから
次の Step 2 では、計算 API を作ります。Step 1 ではブラウザの中で行っていた換算の計算を、サーバー側に移し、画面からサーバーに「この値を換算して」と頼んで、結果を受け取る形にします。
| Step 1 | Step 2 | |
|---|---|---|
| 計算する場所 | ブラウザ | サーバー |
| サーバーの仕事 | 画面のファイルを返すだけ | 画面のファイルを返し、計算もする |
| 新しく学ぶこと | ― | HTTP の要求と応答の中身、JSON、fetch、非同期処理 |
| 組み込みでいうと | ターゲットだけで完結する処理 | ホストとターゲットが、コマンドとレスポンスでやり取りする処理 |
HTTP のコマンドとレスポンスは、組み込みの通信プロトコルに近い話なので、むしろ馴染みやすいのではないかと期待しています。同じ計算をブラウザとサーバーのどちらで行うかを比べることで、画面とサーバーの役割分担も見えてくるはずです。
その先は、記録ツール(データの保存)、データベース、グラフ表示、ファイルの取り込みと続き、最終目標の血液検査アプリに向かいます。紙で受け取った検査結果をスキャンした PDF から、文字認識(OCR)で値を読み取る、という難所も控えています。
この連載も、Step ごとに続けていく予定です。次は番外編として、Step 0・1 で Git につまずいたことをまとめます。
この記事は、シリーズ「組み込みエンジニアの Web 開発入門」の1本です。ほかの回は、シリーズの目次からどうぞ。



コメント