スタックごとのトークンコスト:Next.js・素のTypeScript・Tauri/Rustを自分のClaude Codeログで比較する
リード:AI開発では技術スタックの違いがコストに表れるのか
Claude Code(Anthropic社が提供するターミナル型のAIコーディングツール)は、やり取りごとの入力・出力トークン数を、利用者のPC内にJSONL形式(1行に1件の記録を書き並べたテキストファイル)のログとして残している。つまり、AIにどれだけ作業させたかは、あとから自分で集計できる。
トークンは、API課金や利用上限の単位である。Excelに例えるなら「計算量に応じて課金されるセルの数」に近い指標と言える。
本記事の問いは、開発に使う技術によってこの消費量に差が出るのかどうかである。比較するのは次の3つのスタックである。
- Next.js(React系のWeb開発フレームワーク)
- 素のTypeScript(型を付けられるJavaScript。フレームワークなしの構成)
- Tauri/Rust(Rust製のデスクトップアプリ基盤。画面側はWeb技術で作る)
ただし、対象は筆者環境の実ログに限られる。プロジェクトの規模、作業内容、使用モデルはそろっておらず、結果をそのまま一般化することはできない。本稿は「こう測れば自分の環境でも確かめられる」という手順の提示と、読み取れる傾向の整理を目的としている。
数値欄について:本文中の表では、実測値を記入する欄を【 】で示している。数値は各自のログから集計した値を入れる前提であり、本稿では推測で数値を補っていない。
そもそもトークンとは何で、なぜコストになるのか
トークンの定義
トークンとは、LLM(大規模言語モデル)が文章を処理する際の最小単位である。英単語1つが1トークンとは限らず、単語が複数に分割されることもあれば、記号1つが1トークンになることもある。日本語は文字数とトークン数が一定の比率で対応しないため、「1,000文字だから何トークン」とは単純に言えない。
課金の内訳
Claude Codeのログでは、トークンは主に次の4種類に分けて記録される。
| 種類 | 意味 | 課金上の特徴 |
|---|---|---|
| 入力 | 今回新たにモデルへ送った内容 | 標準単価 |
| 出力 | モデルが生成した内容 | 一般に入力より高単価 |
| キャッシュ書き込み | 再利用のために保存した内容 | 標準より割高になる場合がある |
| キャッシュ読み取り | 保存済みの内容を再利用した分 | 標準より大幅に割安 |
単価は時期やモデルによって変わるため、実際の金額は各自で公式の料金表を確認する必要がある。本稿ではトークン数そのものを主な指標とする。
コンテキストウィンドウが消費を押し上げる仕組み
コンテキストウィンドウ(モデルが一度に扱える情報量の上限)には、会話の履歴、読み込んだファイル、コマンドの出力などが蓄積されていく。やり取りのたびにこの蓄積分が再びモデルへ渡されるため、作業が長引くほど1回あたりの処理量は増える。キャッシュはこの再送分のコストを抑える仕組みだが、効き方は作業の進め方に左右される。
コード生成での主要因
コード生成では、AIが生成するコードの量よりも、読み込むファイルやエラー出力の量が消費の主因になりやすいと見られる。したがって、スタックの違いが消費に影響するとすれば、「ファイルの多さ」と「エラーメッセージの長さ」を通じて表れる可能性が高い。
Claude Codeのログから何をどう測ったのか
ログの保存場所と構造
Claude Codeのログは、通常ホームディレクトリ配下の ~/.claude/projects/ に、プロジェクト(作業フォルダ)ごとのサブフォルダとして保存される。その中にセッション単位の .jsonl ファイルが並ぶ。保存先や形式はバージョンによって変わる可能性があるため、自分の環境で一度中身を確認することが前提になる。
集計に使った項目は次のとおりである。
input_tokens(入力)output_tokens(出力)cache_creation_input_tokens(キャッシュ書き込み)cache_read_input_tokens(キャッシュ読み取り)- セッション単位(ファイル単位)とプロジェクト単位(フォルダ単位)の区分
集計方法
手順は次の3段階である。
- プロジェクトを「Next.js」「素のTypeScript」「Tauri/Rust」に手作業で分類する
- スクリプトでログを読み、セッションごとのトークン数をCSVに書き出す
- CSVをスプレッドシートに取り込み、ピボットテーブルでスタック別に合計・平均を出す
スクリプトの例を示す(Python)。
import json, csv
from pathlib import Path
root = Path.home() / ".claude" / "projects"
rows = []
for f in root.glob("*/*.jsonl"):
seen = set()
total = dict(inp=0, out=0, c_write=0, c_read=0)
for line in f.open(encoding="utf-8"):
try:
d = json.loads(line)
except json.JSONDecodeError:
continue
msg = d.get("message") or {}
u = msg.get("usage")
if not u:
continue
# 同じ応答が複数行に記録される場合に備えた重複除外
key = (msg.get("id"), d.get("requestId"))
if key in seen:
continue
seen.add(key)
total["inp"] += u.get("input_tokens", 0)
total["out"] += u.get("output_tokens", 0)
total["c_write"] += u.get("cache_creation_input_tokens", 0)
total["c_read"] += u.get("cache_read_input_tokens", 0)
rows.append([f.parent.name, f.stem, *total.values()])
with open("tokens.csv", "w", newline="", encoding="utf-8") as o:
w = csv.writer(o)
w.writerow(["project", "session", "input", "output", "cache_write", "cache_read"])
w.writerows(rows)
同一の応答が複数行に記録されるケースがあり得るため、重複を除く処理を入れている。この点を確認せずに合計すると、実際より多く見積もる恐れがある。
比較指標の候補
- 1セッションあたりのトークン:最も単純だが、セッションの長さに左右される
- 1タスクあたりのトークン:作業単位で比べられるが、タスクの切り分けは手作業になる
- 出力に対する入力の比率:AIが「読む量」と「書く量」のどちらに偏っているかを示す
測定上の限界
結果には次の要素が混入し得る。
- タスク難易度の差:新規機能の追加と軽微な修正では、消費量が大きく異なる
- プロジェクト規模の差:ファイル数が多いほど読み込み量は増える
- モデルのバージョン差:モデルによって出力の傾向や冗長さが異なる
このため、以降の数値は「スタックの純粋な差」ではなく、「筆者の使い方の中でスタックごとに表れた差」として読む必要がある。
Next.jsのプロジェクトはトークンを多く使うのか
計測結果
| 指標 | Next.js |
|---|---|
| 対象セッション数 | 【 】 |
| 入力トークン合計 | 【 】 |
| 出力トークン合計 | 【 】 |
| キャッシュ読み取り合計 | 【 】 |
| 1セッション平均(全種類合計) | 【 】 |
| 出力に対する入力の比率 | 【 】 |
グラフは、スタック別に「入力・出力・キャッシュ書き込み・キャッシュ読み取り」を積み上げた棒グラフにすると、内訳の違いが見えやすい。スプレッドシートのピボットテーブルから、そのまま作成できる。
消費に影響し得る要因
Next.jsで消費が増える可能性がある要因は、主に3つある。
- ファイル数の多さ:ページ、レイアウト、コンポーネント、APIルートが分散し、1つの変更で複数ファイルを読む場面が増える
- 規約依存の構成:App RouterやServer Components(サーバー側で描画される部品)では、ファイルの置き場所や名前が動作を決める。AIが規約を確認するため、周辺ファイルを読みにいく可能性がある
- 設定ファイルの量:
next.config、tsconfig、ESLint、Tailwindなどの設定が、状況把握のために読まれることがある
ビルドエラーや型エラーのログ
Next.jsのビルドでは、エラー箇所の前後や、サーバー側とクライアント側の境界に関する警告が長く出力されることがある。そのログがAIに読み込まれると、入力トークンに加算される。長いエラー出力を含むセッションが多いかどうかは、セッション単位の入力量の分布を見れば確認できる。
規約による出力の節約
一方で、規約が決まっていることは、AIが細部を書かずに済むという利点にもなり得る。ルーティングを設定ファイルに書く必要がなく、フォルダを作ればよいため、生成するコード量は少なくなる余地がある。入力が増える一方で出力が減るのであれば、「出力に対する入力の比率」が高めに出ると見られる。実際にそうなっているかは、上記の表で確認する。
素のTypeScriptは軽量といえるのか
計測結果と差分の整理
| 指標 | 素のTypeScript | Next.jsとの差 |
|---|---|---|
| 対象セッション数 | 【 】 | 【 】 |
| 入力トークン合計 | 【 】 | 【 】 |
| 出力トークン合計 | 【 】 | 【 】 |
| キャッシュ読み取り合計 | 【 】 | 【 】 |
| 1セッション平均 | 【 】 | 【 】 |
| 出力に対する入力の比率 | 【 】 | 【 】 |
差は絶対値だけでなく、比率でも見る必要がある。セッション数が違う場合、合計値の比較は意味を持たないためである。
入力が抑えられる可能性
構成が単純であれば、規約や設定を確認するための読み込みが少なく済む。また、ファイル数が少なければ、変更の影響範囲を把握するための探索も短くなる。この場合は入力トークン、とくにセッションあたりの平均が低めに出ると見られる。
対立仮説:自前実装による出力の増加
ただし、フレームワークが提供する機能(ルーティング、ビルド設定、状態管理など)を自前で実装することになれば、AIが生成するコード量は増える。入力が減る代わりに出力が増えるなら、全体の差は小さくなる可能性がある。出力は一般に入力より単価が高いため、トークン数が同じでも金額では逆転することもあり得る。
型定義と手戻り
TypeScriptの型定義は、AIが修正する際の手がかりになる。型エラーが出れば、実行前に誤りを検出できるため、動かして初めて不具合に気づく往復が減る可能性がある。ただし、型定義が複雑になるほど、エラーメッセージも長くなる傾向がある。型が手戻りを減らす効果と、長いエラー出力による入力増のどちらが大きいかは、セッションの往復回数と入力量を合わせて見ることで検討できる。
Tauri/Rustではコンパイルエラーがコストを押し上げるのか
計測結果とTypeScript系との比較
| 指標 | Tauri/Rust | Next.js比 | 素のTS比 |
|---|---|---|---|
| 対象セッション数 | 【 】 | 【 】 | 【 】 |
| 入力トークン合計 | 【 】 | 【 】 | 【 】 |
| 出力トークン合計 | 【 】 | 【 】 | 【 】 |
| キャッシュ読み取り合計 | 【 】 | 【 】 | 【 】 |
| 1セッション平均 | 【 】 | 【 】 | 【 】 |
| 出力に対する入力の比率 | 【 】 | 【 】 | 【 】 |
コンパイラメッセージの長さ
Rustのコンパイラは、所有権(値を誰が持っているかを管理する仕組み)やライフタイム(参照が有効な期間)に関して、原因の説明や修正案を含む詳細な診断を出力する。人間にとっては親切な設計だが、AIにとっては入力トークンが増える要因になり得る。コンパイルと修正を繰り返すセッションでは、この出力が毎回コンテキストに積み上がる。
二層構造による文脈量の増加
Tauriのアプリは、画面を担うフロントエンド(Web側)と、OS機能を扱うバックエンド(Rust側)の二層で構成される。両者の間では、コマンド名や受け渡すデータの型をそろえる必要がある。そのため、1つの機能追加でも両方のコードを読む場面が多くなり、文脈量が増えると見られる。ここではRust側のエラーが多いのか、二層を行き来する読み込みが多いのかを切り分けるため、エラー出力を含むセッションとそうでないセッションを分けて集計する方法が考えられる。
厳格な型システムと修正往復
一方で、Rustの厳格な型システムは、実行時に初めて表れるバグを減らす方向に働くという見方もできる。コンパイルが通った時点で、一定の誤りが排除されているためである。この場合、コンパイル段階の往復は増えても、動作確認後の修正往復は減る可能性がある。セッション全体のトークンで見たときに、この二つが相殺されているかどうかが確認点になる。
3つのスタックの結果から、どのような傾向が読み取れるのか
比較表
| 指標 | Next.js | 素のTypeScript | Tauri/Rust |
|---|---|---|---|
| トークン総量 | 【 】 | 【 】 | 【 】 |
| 1セッション平均 | 【 】 | 【 】 | 【 】 |
| 1タスク平均 | 【 】 | 【 】 | 【 】 |
| キャッシュ利用率※ | 【 】 | 【 】 | 【 】 |
※キャッシュ利用率は、キャッシュ読み取り ÷(入力+キャッシュ書き込み+キャッシュ読み取り)で算出する方法が一例である。
差の主因の切り分け
差が出た場合、その原因は少なくとも次の2系統に分けられる。
- 言語・フレームワークそのもの:ファイル構成、規約、エラー出力の長さ
- 作業内容・プロジェクト規模:新規開発か保守か、機能数、使用期間
たとえば、あるスタックのプロジェクトが大規模で、別のスタックが小規模なツールだった場合、差の大半は規模で説明できてしまう。プロジェクトのファイル数やコード行数を別の列として加え、規模で割った値も併せて確認することが望ましい。
傾向の提示にとどまる点
サンプル数が限られているため、ここで言えるのは傾向までである。「このスタックはトークンを多く使う」という因果の断定は、現時点のデータでは根拠が足りない。1つのセッションが極端に長い場合、平均値が大きく動くことにも注意が必要で、中央値も併記したほうが実態に近い。
追試の方法
条件を統一した比較として、次のような案が考えられる。
- 同じ仕様の小さなアプリ(例:ToDo管理)を3つのスタックで作る
- 同じモデル、同じ指示文、同じ初期状態から開始する
- 完成までの総トークンと往復回数を記録する
- 可能なら複数回繰り返し、ばらつきを確認する
この方法なら、作業内容の差をかなり抑えられる。ただし、実際の業務では「同じアプリをどのスタックでも作る」場面は少なく、実務との距離が残る点には留意が必要である。
トークン消費を抑えるために実務でできることは何か
スタックを変えずに実施でき、効果が見込める対策は次の4つである。
1. 指示ファイルで前提情報を整理する
プロジェクトの目的、ディレクトリ構成、よく使うコマンド、避けるべき操作を CLAUDE.md などの指示ファイルに記載しておく。AIが毎回ファイルを探索して状況を把握する手間が減り、不要なファイルの読み込みも抑えられる。ただし、指示ファイル自体が毎回読み込まれるため、肥大化させないことが前提になる。
2. セッションを適切に区切る
1つのセッションを長く続けると、蓄積された履歴が毎回再送され、コンテキストが膨らむ。作業の区切りでセッションを新しくする、あるいは履歴を要約して引き継ぐ運用が有効と見られる。Excelで言えば、1つのブックにシートを増やし続けるより、用途ごとにブックを分けるのに近い。
3. エラーログは要点だけを渡す
ビルドやテストの出力が数百行に及ぶ場合、全文を渡すよりも、エラーが発生した箇所と原因に関わる数十行に絞るほうが、入力を抑えられる。とくにRustのコンパイルエラーのように出力が長くなりやすい環境では、効果が出やすいと考えられる。
4. 自分のログを定期的に集計する
月に1回でも、前掲のスクリプトで集計し、スプレッドシートで推移を見る習慣は有効と言える。どのプロジェクトで、どの種類のトークンが増えているかを把握できれば、対策の優先順位をつけやすくなる。感覚ではなく数値で、無駄な消費の兆候を見つけられる点が利点である。
まとめ:技術選定にトークンコストをどう織り込むべきか
実測から見られた傾向の要約
本稿の枠組みで読み取るべき点は、次の3つに整理できる。
- スタックごとの差は、入力(読み込み量)、出力(生成量)、キャッシュの効き方の3つに分けて見る必要がある
- 差が出た場合でも、言語・フレームワークの影響と、規模・作業内容の影響は切り分けて考える必要がある
- 具体的な数値は、前掲の各表に各自の集計結果を記入して評価する
唯一の基準ではないという位置づけ
トークンコストは、技術選定の唯一の基準ではない。開発速度、保守のしやすさ、配布形態(Webで公開するのか、デスクトップアプリとして配るのか)と併せて判断すべき指標と位置づけられる。トークンが多少多くても、要件に合うスタックを選ぶ価値が上回る場面は十分にある。
自分のログを測ることの意味
一般論として「Rustはトークンを食う」「素のTypeScriptは軽い」と語られることがあっても、作業内容やプロジェクト規模、AIとの付き合い方によって結果は変わる。そのため、自分のログを測定することが、一般論よりも信頼できる判断材料になると見られる。ログはすでに手元にあり、追加のツールがなくても集計は始められる。
今後の課題
モデルの更新や料金改定によって、結果は変動する可能性がある。モデルの性能が上がれば、同じ作業に必要な往復が減るかもしれない。キャッシュの料金体系が変われば、キャッシュ利用率の意味合いも変わる。したがって、一度測って終わりにせず、一定期間ごとに再計測し、傾向の変化を追うことが求められる。