📄
概念 📚 beginner-stepup

PDF ポートフォリオの自動生成(Month 6 Week 24)

README・checklist.yml・コミット履歴・本番 URL・スクリーンショットを 1 本の PDF にまとめ、修了記念としてアウトプット化する

Week 24 のカリキュラム最終週では、6 ヶ月間の学習成果を 1 つの PDF にまとめ、Menta 契約の Level C(最終修了評価)成果物として納品します。presentation-wataru-kinoshita の約束した「学んだ内容を体系的にまとめた PDF(記念品として進呈)」に相当します。

主旨: このドキュメントはメンターが PDF を生成する側のガイドです。受講者自身がコピペで使える手順にもなっています。


PDF の構成(Table of Contents)

  1. 表紙 — 受講者名 / 期間 / ロゴ
  2. エグゼクティブサマリー — 6 ヶ月で何を作り、何を学んだか(1 ページ)
  3. 完成したアプリ — CloudFront URL + 主要画面スクリーンショット(4〜6 枚)
  4. 要求チェックリスト達成度 — checklist.yml を表形式でレンダリング
  5. アーキテクチャ — インフラ構成図 + ER 図 + シーケンス図
  6. 学習ジャーニー(Month 別) — Tobe / 詰まったポイント / 成長記録
  7. Git 履歴のハイライト — 意味のあるコミット 10〜20 個
  8. AI ツール活用記録(N9) — どのプロンプトが効いたか
  9. 振り返りエッセイ — 受講者本人が書く(2〜3 ページ)
  10. 謝辞 / 今後の展望

アプローチ:Markdown → PDF

1 つの portfolio/portfolio.md を書き、Pandoc + LaTeX or Puppeteer で PDF 化します。Markdown をソースにする 理由:

  • Git で差分管理できる
  • VS Code で書ける
  • 画像を embed しやすい
  • checklist.yml / git log からスクリプトで生成しやすい

セットアップ(Pandoc + wkhtmltopdf 方式)

macOS

brew install pandoc wkhtmltopdf

Windows(Git Bash)

winget install JohnMacFarlane.Pandoc
winget install wkhtmltopdf.wkhtmltopdf

動作確認

echo "# Hello" | pandoc -o test.pdf --pdf-engine=wkhtmltopdf

ディレクトリ構成

sns_clone/
└── portfolio/
    ├── portfolio.md           ← ソース(このファイルを育てる)
    ├── screenshots/            ← アプリのスクリーンショット
    │   ├── home.png
    │   ├── login.png
    │   ├── post-detail.png
    │   └── ...
    ├── diagrams/               ← Mermaid エクスポート or SVG
    │   ├── infrastructure.svg
    │   └── er-diagram.svg
    ├── styles.css              ← PDF のスタイル
    ├── metadata.yaml           ← 表紙情報
    └── build.sh                ← 生成スクリプト

portfolio.md のひな型

---
title: "SWEスキル習得カリキュラム 修了ポートフォリオ"
author: "木下 航"
date: "2026-10-15"
toc: true
toc-depth: 2
---

# 1. エグゼクティブサマリー

2026年4月から10月の6ヶ月間で、React + Hono + AWS のフルスタックアプリを...

# 2. 完成したアプリ

- 本番 URL: https://kinoshita-app.cloudfront.net
- GitHub: https://github.com/subaru-hello/fullstack_typescript_curriculum/tree/kinoshita/main

![ホーム画面](screenshots/home.png)
![投稿詳細](screenshots/post-detail.png)

# 3. 要求チェックリスト達成度

<!-- scripts/render-checklist.mjs で自動生成される -->
{{CHECKLIST_TABLE}}

# 4. アーキテクチャ

![インフラ構成](diagrams/infrastructure.svg)
![ER 図](diagrams/er-diagram.svg)

# 5. 学習ジャーニー

## Month 1: 基礎ツアー(Week 1-4)
...

## Month 2: 要求定義(Week 5-8)
...

(以下 Month 6 まで)

# 6. Git 履歴のハイライト

<!-- scripts/render-git-log.mjs で自動生成 -->
{{GIT_LOG_HIGHLIGHTS}}

# 7. AI ツール活用記録(N9)

- Claude Code: カリキュラム全編で活用。特に...
- Claude の使い方で効いたプロンプト:
  - 「Week N の Tobe を達成するための最短手順を提示して」
  - ...

# 8. 振り返り(自由記述)

(受講者が自由に書く)

# 9. 今後の展望

(このプロジェクトの継続 or 次の挑戦)

自動埋め込みスクリプト

scripts/render-checklist.mjs

checklist.yml を Markdown 表に変換:

#!/usr/bin/env node
import { readFileSync, writeFileSync } from "node:fs";
import yaml from "js-yaml"; // pnpm add -D js-yaml

const checklist = yaml.load(readFileSync("checklist.yml", "utf8"));

function sectionToTable(title, items) {
  let md = `### ${title}\n\n| ID | 要求 | 判定 | 根拠 |\n|---|---|---|---|\n`;
  for (const [id, item] of Object.entries(items)) {
    const judgment = item.judgment === "CLEAR" ? "✅" : item.judgment === "PARTIAL" ? "⚠️" : "❌";
    md += `| ${id} | ${item.requirement} | ${judgment} | ${item.evidence || "-"} |\n`;
  }
  return md + "\n";
}

let out = "";
out += sectionToTable("Must 機能要求", checklist.must_functional);
out += sectionToTable("Must 非機能要求", checklist.must_non_functional);
out += sectionToTable("Nice-to-have", checklist.nice_to_have);

const portfolio = readFileSync("portfolio/portfolio.md", "utf8");
writeFileSync("portfolio/portfolio.md", portfolio.replace("{{CHECKLIST_TABLE}}", out));
console.log("checklist table injected");

scripts/render-git-log.mjs

git log から意味のあるコミットを抽出:

#!/usr/bin/env node
import { execSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";

// kinoshita/main の 6 ヶ月分のマージコミット(PR マージ単位)を取得
const log = execSync(
  `git log kinoshita/main --merges --pretty=format:"- **%ad** %s (%h)" --date=short`,
  { encoding: "utf8" },
);

const portfolio = readFileSync("portfolio/portfolio.md", "utf8");
writeFileSync("portfolio/portfolio.md", portfolio.replace("{{GIT_LOG_HIGHLIGHTS}}", log));
console.log("git log injected");

build.sh(生成スクリプト)

#!/usr/bin/env bash
set -euo pipefail

cd "$(dirname "$0")"

# 1. テンプレートをコピー(上書き)
cp portfolio.template.md portfolio.md

# 2. 自動埋め込み
node ../scripts/render-checklist.mjs
node ../scripts/render-git-log.mjs

# 3. PDF 生成
pandoc portfolio.md \
  -o portfolio.pdf \
  --pdf-engine=wkhtmltopdf \
  --metadata-file=metadata.yaml \
  --toc \
  --toc-depth=2 \
  --css=styles.css \
  --highlight-style=tango

echo "✅ portfolio.pdf generated ($(du -h portfolio.pdf | cut -f1))"

代替アプローチ:Puppeteer で HTML → PDF

Marp や Astro でスライド化したい場合は Puppeteer 経由がシンプル:

cd web
pnpm add -D puppeteer

scripts/build-portfolio-pdf.mjs

import puppeteer from "puppeteer";

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("http://localhost:3000/portfolio", { waitUntil: "networkidle0" });
await page.pdf({
  path: "portfolio.pdf",
  format: "A4",
  margin: { top: "20mm", right: "15mm", bottom: "20mm", left: "15mm" },
  printBackground: true,
});
await browser.close();

Web ページを 1 本作って、そこに Chart.js / Mermaid などインタラクティブ要素も含められる利点あり。


スクリーンショットの撮り方

Playwright で自動キャプチャ:

e2e/screenshots.spec.ts

import { test } from "@playwright/test";

test.use({ viewport: { width: 1280, height: 800 } });

test("capture screens for portfolio", async ({ page }) => {
  await page.goto("/");
  await page.screenshot({ path: "portfolio/screenshots/home.png" });

  await page.goto("/login");
  await page.screenshot({ path: "portfolio/screenshots/login.png" });

  await page.goto("/posts/sample-id");
  await page.screenshot({ path: "portfolio/screenshots/post-detail.png" });
});

pnpm exec playwright test e2e/screenshots.spec.ts で 6 画面が一気に撮れます。


受講者が書く部分(手動)

自動化できない(すべきでない)部分:

  • エグゼクティブサマリー: 自分の言葉で
  • 学習ジャーニーの詰まりポイント: 正直に書くほど資産になる
  • 振り返りエッセイ: 2〜3 ページ。「何ができるようになったか」「何が難しかったか」「次の挑戦」

テンプレートを用意:

## Month N でできるようになったこと
- 【具体例】Hono の middleware を自分で書けるようになった
- 【before / after】以前は try-catch だらけだったが、AppError + app.onError で...

## Month N で詰まったこと
- 【事象】CORS エラーが発生してフロントから API が叩けなかった
- 【原因】proxy 設定 vs CORS 設定の混乱
- 【学び】`vite.config.ts` の proxy と Hono の `cors()` は別の層...

## Month N で印象に残った瞬間
- (自由記述)

Week 24 のアウトプット

  • portfolio/ ディレクトリが sns_clone リポジトリに存在
  • portfolio.template.md に 9 セクション全てのひな型がある
  • scripts/render-checklist.mjs が checklist.yml を表に変換できる
  • scripts/render-git-log.mjs が意味のあるコミットを抽出できる
  • portfolio/screenshots/ に 4〜6 枚のスクリーンショット
  • build.sh を実行すると portfolio.pdf が A4 10〜20 ページで生成される
  • ☐ 受講者本人が「振り返りエッセイ」を手書きで加筆済み
  • ☐ PDF をメンター + 受講者本人が所持

修了の定義(Level C)

以下が全て揃った時点で 修了

  1. https://xxx.cloudfront.net が 24 時間稼働している
  2. checklist.yml の Must 項目が 80% 以上 CLEAR
  3. portfolio.pdf が完成している
  4. kinoshita/main の Git 履歴が 6 ヶ月分残っている
  5. 最終 15 分プレゼンを実施済み
  6. AI ツール活用記録(N9)が README に記載されている

よくある失敗

  • PDF 生成を最終週に始める: Month 5 の後半から portfolio.md を育て始めるとラク
  • 振り返りを「全部うまくいきました」で埋める: 将来の自分への資産性が低い。詰まったこと こそ価値
  • スクリーンショットが粗い: viewport: { width: 1280, height: 800 } 以上で撮影
  • Git log が意味不明: Week 22 までの PR ワークフローで良いコミット粒度を保っていれば、Week 24 は抽出するだけで済む

参考

これで beginner-stepup シリーズは完結です。お疲れさまでした。

生きているコード

本ドキュメントで扱ったパターンの完全な動作コードは、メンター側リポジトリの参照ブランチで確認できます。

  • 対応 Week: W24
  • 参照ブランチ:
  • W24: reference/week-24
  • 対応 checklist 項目: N9

ブランチの作り方・見方は b00-curriculum-map を参照してください。

  1. 1. 📄環境構築の段階的導入(macOS / Windows)
  2. 2. 📄SNSアプリの最終インフラ構成図
  3. 3. 📄SNSクローンの全体設計(Month 1 ゴール)
  4. 4. 📄カリキュラム全体マップ(Week × 教材 × 参照ブランチ × 要求チェックリスト)
  5. 5. 📄このカリキュラムの使い方(SQL・Python・Dify経験者向け)
  6. 6. 📄シェル・ターミナル基礎
  7. 7. 📄Windows で完全にゼロから始める開発環境構築(Week 1)
  8. 8. 📄Git基礎
  9. 9. 📄GitHubワークフロー
  10. 10. 📄パッケージ管理(pnpm workspace)
  11. 11. 📄Webアプリアーキテクチャ全体像
  12. 12. 📄要求ヒアリングとユーザーストーリー(Month 2 Week 5)
  13. 13. 📄画面ワイヤーフレームと画面遷移図(Month 2 Week 6)
  14. 14. 📄HTTP・REST API基礎
  15. 15. 📄ER 図の描き方(Month 2 Week 7)
  16. 16. 📄認証・認可のパターン
  17. 17. 📄REST API 仕様書の書き方(Month 2 Week 7)
  18. 18. 📄HTML/CSS基礎とレイアウト
  19. 19. 📄JavaScript基礎(Pythonとの対比)
  20. 20. 📄TypeScript基礎(型システムとPythonとの対比)
  21. 21. 📄Reactコンポーネント設計の基礎
  22. 22. 📄状態管理の概念
  23. 23. 📄フォーム検証(React Hook Form + Zod)(Month 3 Week 11)
  24. 24. 📄バックエンドAPI設計(Hono)
  25. 25. 📄ルーティング(React Router v7)
  26. 26. 📄Hono のエラーハンドリング(Month 4 Week 13)
  27. 27. 📄データベース設計・SQL→Drizzle ORM対応
  28. 28. 📄マイグレーションの考え方
  29. 29. 📄AWSインフラ基礎
  30. 30. 📄AWS Budget Alert の設定(Month 5 Week 17)
  31. 31. 📄環境変数管理
  32. 32. 📄Bastion EC2 と SSH ProxyJump(Month 5 Week 18)
  33. 33. 📄CI/CD基礎
  34. 34. 📄ECR への Docker イメージ push と App EC2 デプロイ(Month 5 Week 19)
  35. 35. 📄テスト設計の基本
  36. 36. 📄CloudFront + S3 + ALB で公開する(Month 5 Week 20)
  37. 37. 📄CLAUDE.md・プロジェクト設定
  38. 38. 📄PR レビュー 5 観点ルーブリック(全 Week 共通)
  39. 39. 📄タスク分解・仕様の書き方
  40. 40. 📄Playwright で E2E テスト(Month 6 Week 22)
  41. 41. 📄生成コードのレビュー・デバッグの勘所
  42. 42. 📄Trivy で脆弱性スキャン(Month 6 Week 23)
  43. 43. 📄CloudWatch Logs の読み方と運用(Month 6 Week 23)
  44. 44. 📄PDF ポートフォリオの自動生成(Month 6 Week 24)