LLMを使い倒してオフラインでも動く表変換ツールを作った
制作物
11種類のフォーマットに対応した高機能テーブル変換・編集ツール
Quick Table Converter
11種類のフォーマットに相互変換
編集結果は出力結果に即時反映
以下からデモをお試しあれ!
サンプルボタンを押して簡単に動作を試せます
機能
-
CSV, TSV, HTML, Excel, Markdown, LaTeX, SQL, JSON, YAML, XML, ASCII
── 合計 11 種類の表フォーマットを相互変換 - 行・列の追加、ソート、フィルタなど簡易編集
- 完全にオフラインでも動作。ユーザのブラウザで完結して動く
※ 操作ログ(ボタン押下など)のみ最小限収集し、表データは送信しません
製作モチベーション
ブラウザで完結して動く表変換ツールが欲しかった
会社で表の形式をマークダウンやHTMLから変換したいけど、Webで公開されてるツールはデータを流出させそうで怖い。仮に変換できても、その後ちょっとだけ修正したい。完全にオフラインでも動く変換ツールが欲しい、と思ったことから自分で表変換ツールを作成しようと思いました。
Next.jsで何か作りたい + Vibe Codingしたい
ちょうど業務でNext.jsを使い始めて、何か作ってみたかったのとLLMを活かした、いわゆるVibe Codingってどんな感じなんだろうと体験したかったので、Next.jsとLLMをフル活用して作ってみた。なお、AIエージェントは学習するという意味も兼ねて使用しなかった。
使用したLLM
ChatGPT系
筆者はChatGPT Plusユーザのためo3を使用できます(2025/6/3現在)
ChatGPT 4o
- Plus ではほぼ無制限で呼び出せるため、簡単な調べ物や下調べに活用
- 速度が速く、一次情報を確認する用途に便利
ChatGPT o3
- 現在存在するLLMで最高クラス(この頃claude sonnet 4とか出てきて怪しくなった)の性能
- コーディングや画像の読み取り、作成に強みがある
- 主にこのモデルを使用してVibe Codingした
ChatGPT o4-mini-high
- o3 が利用制限に達したとき、または深い推論を要しないが 4o では不安な場面で使用
- 4o ほどライトではなく、o3 ほど重くない処理ができる “中間” の位置づけ
Gemini系
Gemini 2.5 Pro Preview 05-06
- o3 のハルシネーション検証やデザイン案へのフィードバックで使用
- ChatGPT系は2025/5時点では忖度が激しくて、こちらの提案をなんでもよいといって逆に参考にならなかった。特にChatGPT自身が作成したデザインだと自画自賛し始めるため、ほかのモデルでの検証が必要だったため採用
Perplexity系
- 他モデルの回答検証や、引用 URL が必要な調査で利用
- 速度が速く無料枠でも十分。ただし Vercel 関連の情報は GitHub Issue を自分であたる方が確実だった
Claude系
- Claude Sonent 4: 2025/6現在でコーディング性能ならトップと言われているモデル
無料枠でも、十分使える - 本当に雑にNext.jsのコードを渡しても、背後にある親コンポーネントの存在を見越してコードを作成してくれたりして神
- 課金してフルパワーで使用してみたいが、従量課金が怖くて使っていない。
vercel系
v0
- 当初は使う予定がなかったが、Markdown で書いた要件を丸ごと渡すと 1000 行超のコードを一発生成でき驚愕
- CSS 装飾を依頼した際、即座に現行デザイン相当をアウトプットした
→ 今後は言語・ドメイン特化型 LLM がますます存在感を増すのではないかと思った
使用したフレームワーク
今Web開発をやるならこれで外れはないでしょう、という選定。本当はReact Routerとか流行ってるから使いたかったけど、業務で使用することを見越して練習を兼ねて下記のような構成になった
Next.js
15.3.2
React
19.0.0
TypeScript
5.8.3
クラウドプラットフォーム
vercel
- Next.jsのポテンシャルをフルに開放できるプラットフォーム。GitHubと連携すればGitHubにpushするだけで、「即デプロイ->世界中に公開」ができる優れもの
- 無料枠ではサイトの一時停止や個別 URL のアクセス制御ができない点に注意。
- 課金するか、自分でミドルウェアを書きましょう。
制作の流れ
Step 1 – モック作成 (HTML/CSS)
開発初期はo3や4oを使用していたため、以下のようなプロンプトを書いて、出来をChatGPTのプレビュー機能で確認していた
以下の条件を満たすWebサイトをHTMLとCSSのみで書いて
(要件)
でてきたアウトプットの一部
初期のモックでも十分使用できるレベルだった
Step 2 – React で機能追加
HTMLができたらReactの関数をつけて実際に使用して使用感を確かめた
Step 3 – コンポーネントの分割
o3に作ってもらったコードを手作業で別ファイルに移していった。AIエージェントならこのへんを自動でやってくれそうだけど、今回は勉強を兼ねていたのでこのような形になった
Step 4 – ファイル構成を LLM とレビュー
分割後の構成・命名が Next.js のベストプラクティスに沿っているか o3 に相談し、Gemini/Perplexity でクロスチェック。最終的に以下のようなファイル構成がベストプラクティスなんじゃないかという結論にたどり着いた
ディレクトリ構成
.
├── .env.local # 環境変数 (ローカル、Git管理外)
├── .eslintrc.json # ESLint 設定
├── .gitignore # Git 管理対象外ファイル設定
├── .prettierrc.json # Prettier 設定 (任意)
├── components/ # ★ (非推奨 - src/components を推奨)
├── lib/ # ★ (非推奨 - src/lib を推奨)
├── middleware.ts # ルートまたは src/ 直下の Middleware
├── next.config.mjs # Next.js 設定 (ESM推奨)
├── next-env.d.ts # Next.js 型定義 (自動生成)
├── node_modules/ # 依存パッケージ
├── package.json # プロジェクト情報・依存関係
├── postcss.config.js # PostCSS 設定 (Tailwind CSS等で利用)
├── public/ # 静的ファイル (画像、フォントなど)
│ ├── favicon.ico
│ └── images/
├── README.md # プロジェクト説明
├── src/ # ★ アプリケーションソースコード (推奨)
│ ├── app/ # ★ App Router コアディレクトリ
│ │ ├── (auth)/ # (例) ルートグループ (認証関連ページ)
│ │ │ ├── login/
│ │ │ │ └── page.tsx # /login ページ
│ │ │ └── layout.tsx # (auth) グループ用レイアウト (任意)
│ │ ├── (main)/ # (例) ルートグループ (メイン機能)
│ │ │ ├── dashboard/
│ │ │ │ ├── loading.tsx # /dashboard ローディングUI
│ │ │ │ ├── page.tsx # /dashboard ページ
│ │ │ │ └── layout.tsx # /dashboard セクション用レイアウト (任意)
│ │ │ ├── settings/
│ │ │ │ └── page.tsx # /settings ページ
│ │ │ └── layout.tsx # (main) グループ用レイアウト (任意)
│ │ ├── api/ # ★ API ルート
│ │ │ ├── posts/
│ │ │ │ ├── route.ts # /api/posts エンドポイント (GET, POST など)
│ │ │ │ └── [postId]/
│ │ │ │ └── route.ts # /api/posts/:postId エンドポイント (GET, PUT, DELETE など)
│ │ │ └── users/
│ │ │ └── route.ts # /api/users エンドポイント
│ │ ├── [username]/ # (例) 動的ルート
│ │ │ └── page.tsx # /[username] ページ
│ │ ├── _components/ # (例) app/ 内でのみ使われるコンポーネント (慣習)
│ │ │ └── HeaderNav.tsx
│ │ ├── error.tsx # グローバルエラーUI
│ │ ├── global.css # グローバルスタイル (layout.tsx で import)
│ │ ├── layout.tsx # ★ ルートレイアウト (必須)
│ │ ├── loading.tsx # グローバルローディングUI
│ │ ├── not-found.tsx # グローバル Not Found UI
│ │ └── page.tsx # ★ ルートページ (必須, ホームページ)
│ ├── components/ # ★ 共有UIコンポーネント
│ │ ├── ui/ # (例) デザインシステム・汎用コンポーネント
│ │ │ ├── Button/
│ │ │ │ ├── index.tsx # Button コンポーネント本体
│ │ │ │ ├── Button.module.css # CSS Modules スタイル (任意)
│ │ │ │ └── Button.test.tsx # ★ Button コンポーネントテスト (Colocation)
│ │ │ ├── Card.tsx
│ │ │ └── Input.tsx
│ │ ├── features/ # (例) 特定機能に紐づく複合コンポーネント
│ │ │ ├── ArticleCard.tsx
│ │ │ └── UserProfile.tsx
│ │ └── layout/ # (例) サイト共通のヘッダー・フッターなど
│ │ ├── Header.tsx
│ │ └── Footer.tsx
│ ├── constants/ # 定数 (URL、設定値など)
│ │ └── index.ts
│ ├── hooks/ # カスタム React Hooks
│ │ └── useAuth.ts
│ ├── lib/ # ★ ユーティリティ、ヘルパー関数、外部APIクライアントなど
│ │ ├── api.ts # (例) 外部APIフェッチ関数
│ │ ├── db.ts # (例) データベースクライアント初期化
│ │ ├── utils.ts # 汎用ユーティリティ関数
│ │ └── validators.ts # (例) Zodなどのバリデーションスキーマ
│ ├── mocks/ # ★ 開発・テスト用のモックデータ、サンプルデータ
│ │ ├── users.ts # ユーザーデータのモック例.ts
│ │ └── posts.ts # 投稿データのモック例
│ ├── services/ # (例) ビジネスロジック (APIルートやサーバーコンポーネントから利用)
│ │ ├── userService.ts
│ │ └── postService.ts
│ ├── styles/ # グローバルなスタイル、テーマ (global.css以外)
│ │ └── theme.ts
│ └── types/ # TypeScript 型定義
│ ├── api.ts
│ └── index.ts
├── tests/ # ★ E2Eテスト、インテグレーションテスト用 (トップレベル)
│ ├── e2e/
│ │ └── example.spec.ts # (例) Playwright / Cypress テスト
│ └── integration/
│ └── auth.spec.ts # (例) 複数コンポーネント連携テスト
├── tsconfig.json # TypeScript 設定
└── tailwind.config.ts # Tailwind CSS 設定 (利用する場合)
上記の1-4を繰り返して作成したのが以下
上記は上記でシンプルで好きだったので、これができた時点でコミュニティに投稿しようと思っていたのだが...
vercel v0の衝撃
上記についての記事を執筆していて、vercelのv0というLLMがデザインについて強いという投稿を目にした。せっかくならリサーチもかねてやってみるかと思い、上記のツールの機能をマークダウンで記したプロンプトをv0に与えてみた
与えたプロンプト
以下の要件を満たすWebツールのモックを作成して
# 概要
CSV,TSV,HTMLなど11種の表フォーマットを相互変換・編集できるWebツール
# 機能
## 機能に対するヘルプ
以下の文言のインフォマークにホバーすると文言が出る
1. 11フォーマット対応
2. 行・列ソート&並べ替え
3. 全文検索・フィルタ
## step bar
1. 入力に何もない初期状態で①Table貼り付け/ファイル選択
2. テーブルを張り付けると②変換に移動
3. 編集/プレビュータブでモードを切り替えると③編集/プレビューで出力を編集に移動
## モード選択タブ
1. 入力タブ
- Formatセレクトボックス
- 選択したフォーマットのサンプルがの種類を選ぶ
- サンプルボタン
- Formatで選択したフォーマットのサンプルが入力される
- ファイル選択
- ファイルを選択し、読み込む
- クリア
- TextAreaの内容をクリアする
- TextArea
- 変換する元のテーブルのテキストを貼り付ける
2. 編集/プレビュータブ
- 入力がない時は非活性。入力されたら活性。
- TextAreaに対して入力されたテーブルのプレビューが表示される
- テーブルの行・列をドラッグ・ドロップで移動できる
- Filterでテーブルの内容のフィルターができる
## 変換結果
1. excel,markdown,csv,tsv,html,latex,sql,json,yaml,xml,ascii
- 上記のフォーマットに変換された結果がそれぞれのタブをクリックすると表示される
2. コピーボタンで出力をコピー
3. ダウンロードで出力をファイル形式でダウンロード
## HowToUse
ページ下部で3段階で使用方法を説明
すると以下のページが出力された
上記の結果には少し驚いた。o3に同じプロンプトを与えて出てきたのは以下だったからだ
え!?なんか一発で思ったようなの出てきた...!!
この時点からv0の方がNext.jsのプロジェクトではコーディング能力が高いんじゃないかと思い始めて、v0で作成し始めた
次にv0にCSSで装飾するように依頼した。できたのが以下
え!?見やすい...!?俺の今までの一か月はなんだったの...?そんな感じがして1日ほど寝込んだ
が、良いものができるんだったらそれいいじゃんと開き直りv0で作成した方を公開するようにした
v0でできたこと、できなかったこと
基本的にv0は万能で大体のことを実現してくれた。プレビューのテーブルの行・列の追加機能や拡大機能など、o3に実装してもらおうとするとpit of deathに陥ってしまうことも難なくやってのけた。この辺にコーディング特化モデルの強みがあるのかもしれない
でも、多言語対応で今まで作成したファイルの多岐にわたる修正になると途端にバグってしまった。このバグはどうしてもv0で解消できずに無料分のクレジットを使い切ってしまった(今世紀最大の無駄遣い)。仕方ないので、ここの変更は手動で行った
最後の仕上げとテスト
v0が作成してくれたものに対してo3で作成した成果物に対するテストを行った。すると案外ボロが出た。LaTeXに変換できていなかったり、フィルター機能を使用すると出力が変更されなかったり、細かい所で抜けがあってそこはo3やClaude Sonnet 4で修正した。
以上で現在に至る
Vibe Codingした感想
新規開発においてはスピードが爆速になったと感じる。10倍から20倍の速さで開発できるようになったのではないかと思う
ただ、LLMにコードを書かせていても、思わぬところでpit of deathにハマってしまうことが分かった。巷では3回リトライして直らなかったら人間が直した方が良いと言われているが、これは結構その通りで、一度ハマると修正できましたと言い張ってバグが残ったものを提出してくるようになる。こうなると厄介でプロンプトが悪いのか、そもそものLLMの処理能力を超えてるのか判断がつかなくなってくる。LLMは非決定的に生成するから、運が悪かっただけかも? とかいろいろ考えられる。実際に筆者は5回目のリトライで成功したことがあるのだが、それ以外は成功しなかったので3回以上のリトライはお勧めしない。理由がはっきりとわからないエラーに苦しむのはなかなかにしんどいものがある。
結局pit of deathを乗り越えるために人間の手で修正しなくてはならなくなってくるし、修正しているうちにコードの内容が分かってきて、自分でコードが書けるようになってくる、いろいろな実装方法を知ることができた、という副次的な作用もあった。一人で書いていたのでは到達するのに時間がかかっていたであろう実装方法もすぐに学ぶことができた。
今後LLMが発展していくにつ入れてこのような問題は過去のものになっていくのかもしれないが、現時点では人間の手による補助は必須で、まだ人の手がいるんだなと感じた
Discussion