Claude CodeでElectronアプリを安全に作る:preloadとIPCの境界設計
Claude CodeでElectronデスクトップアプリを作った実体験。contextIsolation・preload・IPC・ファイル検証・自動更新まで、事故りやすい所を実例で解説。
「Electronでメモアプリ作って。ファイル開けて保存もできて、自動更新もよろしく」
僕がそう頼んだら、Claude Codeは30分でちゃんと動くアプリを出してきました。見た目も完璧。喜んでDevToolsを開いた瞬間、血の気が引きました。window.require('fs') が叩ける。つまり、ブラウザの開発者ツールから、僕のPCのどのファイルでも読み書きできる状態だったんです。
Electronはこれが怖い。Webと同じ感覚で書くと、いつの間にか「ブラウザでは絶対起きない事故」を抱え込みます。この記事は、Claude CodeにElectronアプリを任せながら、その事故を防いだ僕の手順です。コードは全部コピペで動く実物を載せます。
この記事の要点
- Electronの危険の9割はrendererがOS権限を持ってしまうこと。
contextIsolation: trueと preload 経由の「用途別API」で境界を切るのが今の正解。 - Claude Codeへの依頼は「全部作って」ではなく変更範囲・禁止事項・検証コマンドをセットで渡す。差分が追えるから事故を見つけられる。
- ファイルアクセスは、作るよりレビューが大事。拡張子・サイズ・許可済みルートを main 側で必ず検証する。
- 自動更新(autoUpdater)は macOS / Windows 向け。署名が前提で、開発終盤に着手すると詰む。最初に運用条件を決める。
- この記事の最小スターターは
npm run devで動く。preload 以外から Electron API を触っていないかを最後に必ず確認する。
なぜElectronはWebより危ないのか
普通のWebアプリは、ブラウザという頑丈な檻の中で動きます。勝手にファイルを消したり、OSのダイアログを開いたりはできません。檻が守ってくれる。
Electronは、その檻を自分で開けてしまえる技術です。ファイルを読める、書ける、OSの通知やメニューを触れる。便利な反面、設計を間違えると renderer(画面側のJavaScript)にOS権限が漏れます。冒頭の window.require('fs') が叩けた状態は、まさにこれでした。
ここで効くのが contextIsolation という設定です。これは「画面側のJavaScript」と「Node.jsやElectronの機能」を別世界に隔離する壁のこと。Electron公式によればContext IsolationはElectron 12以降デフォルトで有効です。なので新規プロジェクトなら最初からONなのですが、問題はONのまま、preloadで穴を開けてしまうパターン。Claude Codeが生成したコードでも、ここがよく崩れます。
正しい形はこうです。renderer はユーザー入力と表示だけに集中する。preload は「テキストを開く」「保存する」みたいな用途別の関数だけを橋渡しする。main process だけがファイルやOSに触る。この3層の境界を死守できているかが、Electronアプリの安全性をほぼ決めます。
まず決める3つの設計境界
コードを書く前に、役割を3つに分けます。これを紙に書いてからClaude Codeに頼むと、レビューが一気に楽になります。
| 層 | 役割 | Claude Codeに任せる粒度 | レビューで見る点 |
|---|---|---|---|
| main process | BrowserWindow、メニュー、OS API、ファイル操作、更新処理 | IPCハンドラを1機能ずつ追加 | 入力検証、権限、例外処理、パス制限 |
| preload | rendererに公開する最小API | contextBridgeで型付きAPIを作る | ipcRendererを丸ごと出していないか |
| renderer | React/Vue/素のDOMなどのUI | UI状態、ボタン、フォーム、表示 | OS権限を直接持っていないか |
依頼の粒度がすべてです。「Electronアプリを全部作って」は広すぎて、UI・IPC・保存形式・配布設定が一度に変わり、差分が追えなくなります。僕が落ち着いたのは、こういう書き方でした。
テキストファイルを開くIPCを1本追加して。preloadの公開API、rendererのボタン、main側の入力検証まで。既存の更新処理には触らないで。
変更範囲を1機能に絞ると、Claude Codeの出す差分が10〜30行で収まります。これくらいなら、僕の目でも境界が壊れていないか確認できます。
全体像:安全なElectronの流れ
文字より図のほうが早いので、データの流れを描いておきます。
flowchart LR
User["ユーザーがUI操作"] --> Renderer["Renderer\nHTML / React"]
Renderer --> Preload["Preload\ncontextBridge"]
Preload --> IPC["IPCチャンネル\n検証済みpayload"]
IPC --> Main["Main process\nOS・ファイルアクセス"]
Main --> Result["型付き結果"]
Result --> Renderer
Main --> Update["パッケージング / 自動更新"]
ポイントは1つ。rendererからファイルシステムへ直接の矢印が引かれていないことです。renderer は preload を通してしか外に出られない。preload は決まった関数だけを通す。だからDevToolsから任意のファイルを触る、という冒頭の事故が構造的に起きません。Claude Codeに実装させるときも、この矢印を増やしていないかをレビューします。
コピペで動く最小スターター
理屈はここまで。素のElectron + TypeScriptで、contextIsolation・preload・IPC・ファイルアクセスを全部確認できる最小構成を作ります。ReactやViteを足すのはこの後でいい。まずこの骨格を体に入れます。
mkdir electron-secure-notes
cd electron-secure-notes
npm init -y
npm install --save-dev electron typescript @types/node
npm pkg set main="dist/main.js"
npm pkg set scripts.dev="tsc -p tsconfig.json && electron ."
mkdir src
tsconfig.json を置きます。
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "Node",
"strict": true,
"esModuleInterop": true,
"outDir": "dist",
"rootDir": "src",
"types": ["node", "electron"]
},
"include": ["src/**/*.ts"]
}
main process です。BrowserWindow では contextIsolation: true、nodeIntegration: false、sandbox: true を明示します。デフォルトに任せず書くのは、後で誰か(未来の自分やClaude Code)が勝手に外さないための予防線です。preload のパスはビルド後の dist/preload.js を指します。
// src/main.ts
import { app, BrowserWindow, dialog, ipcMain, session } from "electron";
import fs from "node:fs/promises";
import path from "node:path";
type OpenedTextFile = {
filePath: string;
content: string;
};
type SaveTextRequest = {
filePath: string;
content: string;
};
// 扱ってよい拡張子。ここに無いものは門前払い
const allowedExtensions = new Set([".txt", ".md", ".json"]);
// ダイアログで開いたフォルダだけを「保存OKな場所」として記録する
const allowedRoots = new Set<string>();
function createWindow() {
const win = new BrowserWindow({
width: 1100,
height: 760,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
contextIsolation: true, // 画面側とNode.jsを隔離する壁
nodeIntegration: false, // rendererにNode.jsを持たせない
sandbox: true
}
});
win.loadFile(path.join(__dirname, "index.html"));
}
// CSPを付けて、外部スクリプトの読み込みを禁止する
function registerCsp() {
session.defaultSession.webRequest.onHeadersReceived((details, callback) => {
callback({
responseHeaders: {
...details.responseHeaders,
"Content-Security-Policy": [
"default-src 'self'; script-src 'self'; style-src 'self'"
]
}
});
});
}
// target が root フォルダの内側かどうか(外なら false)
function isInsideRoot(root: string, target: string) {
const relative = path.relative(root, target);
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
}
// 保存前の門番:拡張子と「開いた場所の中か」を両方チェックする
async function assertAllowedFile(filePath: string) {
const resolved = path.resolve(filePath);
const extension = path.extname(resolved).toLowerCase();
if (!allowedExtensions.has(extension)) {
throw new Error("txt / md / json 以外は保存できません。");
}
const insideAllowedRoot = Array.from(allowedRoots).some((root) =>
isInsideRoot(root, resolved)
);
if (!insideAllowedRoot) {
throw new Error("保存前に、まずダイアログでファイルを開いてください。");
}
return resolved;
}
ipcMain.handle("dialog:openTextFile", async (): Promise<OpenedTextFile | null> => {
const result = await dialog.showOpenDialog({
title: "テキストファイルを開く",
properties: ["openFile"],
filters: [{ name: "Text", extensions: ["txt", "md", "json"] }]
});
if (result.canceled || result.filePaths.length === 0) {
return null;
}
const filePath = path.resolve(result.filePaths[0]);
const safePath = await assertOpenedPath(filePath);
// 開いたファイルのフォルダを「保存してよい場所」に登録
allowedRoots.add(path.dirname(safePath));
return {
filePath: safePath,
content: await fs.readFile(safePath, "utf8")
};
});
// 開くときの拡張子チェック
async function assertOpenedPath(filePath: string) {
const extension = path.extname(filePath).toLowerCase();
if (!allowedExtensions.has(extension)) {
throw new Error("対応していないファイル形式です。");
}
return filePath;
}
ipcMain.handle("file:saveText", async (_event, payload: SaveTextRequest) => {
// rendererから来た値は信用しない。型・サイズを必ず確認する
if (
!payload ||
typeof payload.filePath !== "string" ||
typeof payload.content !== "string" ||
payload.content.length > 1_000_000
) {
throw new Error("保存リクエストが不正です。");
}
const filePath = await assertAllowedFile(payload.filePath);
await fs.writeFile(filePath, payload.content, "utf8");
return { ok: true };
});
app.whenReady().then(() => {
registerCsp();
createWindow();
});
app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
});
app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
ここで一番大事なのは assertAllowedFile です。renderer から渡されたパスをそのまま fs.writeFile に流したら、../../etc/passwd みたいな文字列でどこにでも書けてしまう。だから「ダイアログで実際に開いたフォルダの中か」を allowedRoots で照合します。ファイル選択ボタンしかUIに無くても、DevToolsやXSSで任意のIPCを叩かれる前提で組む。これがmain側検証の肝です。
preload では ipcRenderer をそのまま公開しません。公開するのは「開く」「保存する」という用途別の関数だけ。これが contextBridge の実務での使い方です。
// src/preload.ts
import { contextBridge, ipcRenderer } from "electron";
type OpenedTextFile = {
filePath: string;
content: string;
};
type SaveTextRequest = {
filePath: string;
content: string;
};
const desktopApi = {
openTextFile: () =>
ipcRenderer.invoke("dialog:openTextFile") as Promise<OpenedTextFile | null>,
saveTextFile: (payload: SaveTextRequest) => {
// preload側でも軽く弾く(多層防御)
if (
!payload ||
typeof payload.filePath !== "string" ||
typeof payload.content !== "string"
) {
throw new Error("保存データが不正です。");
}
return ipcRenderer.invoke("file:saveText", payload) as Promise<{ ok: boolean }>;
}
};
// window.desktop からだけ触れる、絞り込んだ窓口を公開
contextBridge.exposeInMainWorld("desktop", desktopApi);
renderer でTypeScriptの補完を効かせるため、Window 型を拡張しておきます。
// src/global.d.ts
export {};
declare global {
interface Window {
desktop: {
openTextFile: () => Promise<{ filePath: string; content: string } | null>;
saveTextFile: (payload: {
filePath: string;
content: string;
}) => Promise<{ ok: boolean }>;
};
}
}
UIは最小で十分です。<script> はHTMLに直書きせず、CSPに合わせて外部ファイルにします(インラインスクリプトはCSPで禁止しているため)。
<!-- src/index.html -->
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self'"
/>
<title>Secure Notes</title>
</head>
<body>
<main>
<h1>Secure Notes</h1>
<button id="open">開く</button>
<button id="save">保存</button>
<p id="status">ファイル未選択</p>
<textarea id="editor" rows="24" cols="90"></textarea>
</main>
<script src="./renderer.js"></script>
</body>
</html>
// src/renderer.ts
const openButton = document.querySelector<HTMLButtonElement>("#open");
const saveButton = document.querySelector<HTMLButtonElement>("#save");
const editor = document.querySelector<HTMLTextAreaElement>("#editor");
const status = document.querySelector<HTMLParagraphElement>("#status");
let currentFilePath: string | null = null;
if (!openButton || !saveButton || !editor || !status) {
throw new Error("必要なUI要素が見つかりません。");
}
openButton.addEventListener("click", async () => {
// window.desktop 経由でしかファイルに触れない
const file = await window.desktop.openTextFile();
if (!file) {
status.textContent = "開くのをキャンセルしました。";
return;
}
currentFilePath = file.filePath;
editor.value = file.content;
status.textContent = `開いた: ${file.filePath}`;
});
saveButton.addEventListener("click", async () => {
if (!currentFilePath) {
status.textContent = "先にファイルを開いてください。";
return;
}
await window.desktop.saveTextFile({
filePath: currentFilePath,
content: editor.value
});
status.textContent = `保存した: ${currentFilePath}`;
});
ここまで置いたら npm run dev で起動します。Claude Codeにこの構成を作らせたなら、最後に「npm run dev でTypeScriptビルドが通ること、起動時にDevToolsでCSP警告が増えていないことを確認して」と添えます。動作確認まで頼むと、握りつぶされたエラーに後で泣かされずに済みます。
Claude Codeに渡すプロンプトの型
機能を小さく切るのとセットで、僕がいつも使うプロンプトの型がこれです。変更範囲・禁止事項・完了条件の3点を必ず書きます。
Electron + TypeScriptの既存アプリに、Markdownファイルを開いて保存する機能を追加してください。
制約:
- rendererにNode.js APIを公開しない
- contextIsolation: true、nodeIntegration: false、sandbox: trueを維持
- preloadではwindow.desktop.openTextFile/saveTextFileだけを公開
- ipcRendererやipcMainを丸ごと公開しない
- ファイル拡張子はmd/txt/jsonに限定
- 既存のauto update設定には触らない
完了条件:
- npm run typecheckが通る
- main/preload/rendererの変更理由を短く説明する
- セキュリティ上の確認点を3つ挙げる
この粒度だと差分が追えます。逆に「Electronでメモアプリ作って。自動更新もよろしく」だと、UI・IPC・保存形式・配布設定が一度に変わって、レビュー不能になります。冒頭の事故は、まさにこの雑な頼み方が原因でした。
実例3つ:どこで境界を引くか
設計境界は、ユースケースごとに引く場所が変わります。僕が実際に作った(あるいは相談を受けた)3つを並べます。
1. 社内向けMarkdownメモアプリ
議事録や社内ルールをローカルMarkdownで編集するアプリ。「最近開いたファイル一覧」を保存したくなりますが、ここに罠があります。Claude Codeには「一覧は app.getPath("userData") 配下のJSONに保存して。ただし本文は保存しない」と頼みます。本文キャッシュまで残すと、機密メモがアプリ設定領域にこっそり居座る。しかもパスは信用せず、起動時に存在確認と拡張子チェックをやり直します。
2. サポートログビューア ユーザーから送られたログを整形して見るアプリ。ログは巨大で、個人情報やトークンを含みます。ここで「ログ全文を renderer に流し込む」と、UIが固まる。だから「100MB超のログを一括で renderer に渡さない。main側で行単位に読み、renderer にはページング済みデータだけ返す」と具体化します。パフォーマンス要件を文字にしないと、動くけど固まるアプリができあがります。コピー時にメールアドレスやBearerトークンをマスクする処理も入れておくと安心です。
3. デスクトップ版AIプロンプトワークベンチ プロンプト・テンプレート・ローカル資料・外部APIを組み合わせる作業台。Electronの便利さと危険さが同時に出ます。最優先はAPIキーを renderer に渡さないこと。外部API呼び出しは main で行い、renderer には結果だけ返す。Claude Codeには「APIキーは main でのみ読む。renderer には接続テストの boolean とエラーメッセージだけ返す」と指定します。この権限設計の考え方はClaude Codeセキュリティのベストプラクティスとも地続きです。
ファイルアクセスで必ず見る落とし穴
ファイルアクセスは、Electron記事で一番事故る部分です。実務でよく踏む4つを挙げます。
1つ目は、rendererから任意パスを渡すとmainがそのまま読む実装。UI上はボタンしか無くても、DevToolsやXSSで任意IPCを叩かれる前提で組む。拡張子・サイズ・許可済みルートをmain側で検証します(上のコードの assertAllowedFile がこれ)。
2つ目は、preloadで ipcRenderer.invoke をそのまま公開する実装。window.api.invoke(channel, payload) みたいな汎用口は、何でも叩けてレビューもしにくい。公式のIPCチュートリアルでも、Electron APIを丸ごと公開せず用途別の関数に絞ることが推奨されています。
3つ目は、開発時の相対パスがパッケージ後に壊れる問題。src/index.html を読みに行く、assets/icon.png の位置がずれる、asar内のファイルを書き換えようとする、など。実行時に書くデータは app.getPath("userData") 配下へ、同梱アセットは読み取り専用として扱います。
4つ目は、大きなファイルを一度にrendererへ渡すこと。IPCは便利ですが、巨大な文字列やバイナリを雑に渡すとUIが固まります。ログビューアやCSVビューアでは、ページング・ストリーム・Worker相当の分離を検討します。
自動更新とパッケージングは「最後」では遅い
配布は npm run make で終わり、ではありません。署名・インストーラー・更新フィード・ロールバック・初回起動の挙動を早めに設計します。僕はここを後回しにして、リリース前日に署名で丸一日溶かしたことがあります。
Electron公式のautoUpdaterはmacOSとWindows向けの機能です。Linuxはディストリのパッケージ管理に寄せるのが基本。Windowsはパッケージ形式でMSIXやSquirrel.Windowsの挙動が変わり、macOSは署名が自動更新の前提になります。ここを仕様書でぼかすと、リリース直前に必ず詰まります。
Electron Forgeを使う場合の最小設定です。署名情報・GitHubトークン・Apple Developer ID・Windows証明書はCIのsecretで扱い、リポジトリに直書きしません。
npm install --save-dev @electron-forge/cli @electron-forge/maker-squirrel @electron-forge/maker-zip @electron-forge/maker-deb @electron-forge/maker-rpm
npx electron-forge import
// forge.config.ts
import type { ForgeConfig } from "@electron-forge/shared-types";
const config: ForgeConfig = {
packagerConfig: {
asar: true,
icon: "assets/icon"
},
rebuildConfig: {},
makers: [
{
name: "@electron-forge/maker-squirrel",
config: {
name: "secure_notes"
}
},
{
name: "@electron-forge/maker-zip",
platforms: ["darwin"]
},
{
name: "@electron-forge/maker-deb",
config: {}
},
{
name: "@electron-forge/maker-rpm",
config: {}
}
]
};
export default config;
更新状態をUIに出すなら、mainからrendererへ通知します。ここでも event オブジェクトをそのまま渡さず、文字列や構造化した結果だけを渡すのが鉄則です。
// src/update.ts
import { app, autoUpdater, BrowserWindow } from "electron";
function sendStatus(win: BrowserWindow, status: string) {
win.webContents.send("update:status", status);
}
export function configureAutoUpdate(win: BrowserWindow) {
// 開発時は更新を完全に止める(事故防止)
if (!app.isPackaged) {
sendStatus(win, "updates disabled in development");
return;
}
// Linuxはパッケージ管理に任せる
if (process.platform === "linux") {
sendStatus(win, "use the distribution package manager on Linux");
return;
}
const owner = "YOUR_GITHUB_OWNER";
const repo = "YOUR_GITHUB_REPO";
const feedUrl = `https://update.electronjs.org/${owner}/${repo}/${process.platform}-${process.arch}/${app.getVersion()}`;
autoUpdater.setFeedURL({ url: feedUrl });
autoUpdater.on("checking-for-update", () => sendStatus(win, "checking"));
autoUpdater.on("update-available", () => sendStatus(win, "available"));
autoUpdater.on("update-not-available", () => sendStatus(win, "not available"));
autoUpdater.on("update-downloaded", () => sendStatus(win, "downloaded"));
autoUpdater.on("error", (error) => sendStatus(win, `error: ${error.message}`));
// 起動直後ではなく、少し待ってから確認する
setTimeout(() => {
try {
autoUpdater.checkForUpdates();
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
sendStatus(win, `error: ${message}`);
}
}, 10_000);
}
このコードは、更新サーバーとリリース運用が整って初めて意味を持ちます。Claude Codeには「自動更新を追加」だけでなく、「開発時は無効、Linuxは明示メッセージ、署名と更新フィードはREADMEに未設定として残す」と運用条件まで書かせます。
なお、Tauriとどっちを選ぶかで迷うならClaude CodeでTauri開発に比較観点をまとめてあります。型設計を詰めたいならClaude CodeでTypeScript開発を加速するTipsもどうぞ。
Claude Codeの差分を見る順番
Claude Codeが出した差分は、毎回この順番でチェックします。リスト化しておくと、レビュー漏れが減ります。
BrowserWindowでcontextIsolation・nodeIntegration・sandboxが期待どおりか- preloadが用途別APIだけを公開しているか
- IPCチャンネル名が機能別に分かれ、汎用
invokeになっていないか - main側で入力検証をしているか
- ファイルパス・拡張子・サイズ・許可済みルートを確認しているか
- CSPを緩めすぎていないか
- devでは動くがpackage後に壊れるパス参照がないか
- 署名・更新フィード・secretをコードに直書きしていないか
これはセキュリティのためだけじゃありません。「どこを人間が判断したか」を明確にしておくと、受託や相談につながったときに信頼されます。Claude Codeに任せる範囲と、人間が責任を持つ範囲を分けること自体が、実装支援の価値だと僕は思っています。
よくある質問
Q. contextIsolationはデフォルトで有効なら、自分で設定しなくていい?
A. 書いておくのを勧めます。Electron 12以降はデフォルトONですが、後から誰かが webPreferences をいじって外す事故が起きます。contextIsolation: true・nodeIntegration: false・sandbox: true を明示しておくと、レビューで一目で気づけます。
Q. preloadとIPCの違いがよく分かりません。
A. preloadは「rendererに何を見せるかを決める窓口」、IPCは「rendererとmainが会話するための通信路」です。preloadで window.desktop.openTextFile() という窓口を作り、その中身がIPCで dialog:openTextFile というチャンネルをmainに投げる、という関係です。
Q. nodeIntegrationをtrueにすれば楽なのでは?
A. 楽ですが、rendererがNode.jsの全機能を持つので、XSS一発でPC全体を触られます。冒頭の window.require('fs') 事故がこれです。便利さと引き換えに檻を外す設定なので、基本はfalseのままにします。
Q. 自動更新はLinuxでも動きますか?
A. Electron公式の autoUpdater はmacOSとWindows向けです。Linuxはapt/dnf/Flatpakなどディストリのパッケージ管理に寄せます。上の update.ts のように、Linuxでは明示メッセージを出して更新処理を止める形が安全です。
Q. Claude Codeに「全部作って」と頼んではいけない理由は? A. 差分が大きくなりすぎて、境界が壊れていても気づけないからです。1機能ずつ、変更範囲と禁止事項と検証コマンドをセットで頼む。そうすれば差分が数十行に収まり、人間の目でレビューできます。
実際に試した結果
冒頭の window.require('fs') 事故以来、僕はElectronを「賢く作る」より「境界を壊さない」を最優先にしました。やったことは地味です。contextIsolation を明示し、preloadを用途別関数だけに絞り、main側に assertAllowedFile の門番を1つ置く。それだけで、DevToolsから任意ファイルを触れる状態は消えました。
そして一番効いたのは、Claude Codeへの頼み方を変えたことです。「全部作って」をやめて「1機能・禁止事項・検証コマンド」のセットにしたら、出てくる差分が追える大きさになり、危ない実装をその場で弾けるようになりました。Electronは境界設計が9割。速く作る道具であるClaude Codeを、境界を守る道具として使うのがコツでした。
実際に試すときの最終チェックはこの5つです。npm run dev が通る/preload以外からElectron APIに触っていない/任意パスを読めない/package後にパスが壊れない/自動更新は署名とフィードが揃うまで本番扱いにしない。ここまで確認してからUIを増やすと、後戻りの少ないElectron開発になります。
デスクトップアプリのPoCを短期で形にしたい、既存ElectronアプリのIPCや更新設計を見直したい、という方は研修・相談からどうぞ。手を動かして学びたい方向けの教材一覧も用意しています。
無料PDF: Claude Code はじめてのチートシート
まずは無料PDFで基本コマンドと最初の使い方をまとめて確認してください。登録後はそのままテンプレート集や導入相談にも進めます。
スパムは送りません。登録情報は厳重に管理します。
Claude Codeを仕事で使える形にしませんか?
まず無料PDFで基本を固め、繰り返し使う作業はGumroad教材へ、チーム導入や権限設計は導入相談へ進めます。
この記事を書いた人
Masa
Claude Codeの実務活用、導入設計、収益導線改善を検証しているエンジニア。10言語の技術メディアを運営中。
関連書籍・参考図書
この記事のテーマに関連する書籍を楽天ブックスで探せます。
※ 当サイトは楽天市場のアフィリエイトプログラムに参加しています。上記リンクから商品をご購入いただくと、運営者に紹介料が支払われる場合があります。
関連記事
制作会社がClaude Codeに触らせる前に決める権限チェックリスト
クライアントサイトを壊さずにAI編集を使うための、制作会社向け権限と確認の型です。
SaaSサポートのバグ報告をClaude Codeで再現手順に変える実務フロー
問い合わせ文をそのまま開発へ投げず、再現手順、証拠、次の一手に整えるサポート向け手順です。
Obsidianの古いメモをClaude Codeの指示書に変える10分ルーチン
Obsidianに溜めたメモが毎回ゴミになる人へ。事実・決定・未確認に仕分けして、Claude Codeがそのまま動ける指示書に変える朝の10分の型を紹介します。