LLMを使い倒してオフラインでも動く表変換ツールを作った

# 制作物

11種類のフォーマットに対応した高機能テーブル変換・編集ツール

## Quick Table Converter

![Quick Table Converter のスクリーンショット](https://storage.googleapis.com/zenn-user-upload/88e44fcba0d4-20250603.png)

**11種類のフォーマットに相互変換**
![デモ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4108352/7d452468-21f4-45a8-b8b1-181310384ee5.png)

**編集結果は出力結果に即時反映**
![カンタンな編集.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4108352/e2876e13-f2c6-49bd-ae25-77308fe91e74.png)

以下からデモをお試しあれ！
サンプルボタンを押して簡単に動作を試せます

https://quick-table-converter.vercel.app/

## 機能
1. **CSV, TSV, HTML, Excel, Markdown, LaTeX, SQL, JSON, YAML, XML, ASCII**  
   ── 合計 11 種類の表フォーマットを相互変換
2. 行・列の追加、ソート、フィルタなど簡易編集
3. 完全にオフラインでも動作。**ユーザのブラウザで完結して動く**
   ※ 操作ログ（ボタン押下など）のみ最小限収集し、表データは送信しません

## 製作モチベーション

### ブラウザで完結して動く表変換ツールが欲しかった

会社で表の形式をマークダウンやHTMLから変換したいけど、Webで公開されてるツールはデータを流出させそうで怖い。仮に変換できても、その後ちょっとだけ修正したい。完全にオフラインでも動く変換ツールが欲しい、と思ったことから自分で表変換ツールを作成しようと思いました。

### Next.jsで何か作りたい + Vibe Codingしたい
ちょうど業務でNext.jsを使い始めて、何か作ってみたかったのとLLMを活かした、いわゆるVibe Codingってどんな感じなんだろうと体験したかったので、Next.jsとLLMをフル活用して作ってみた。なお、AIエージェントは学習するという意味も兼ねて使用しなかった。



### :exclamation:注意
上記のサイトはすでに	Vercel v0 Communityに筆者がとしたEasy Table Converterに修正を加えたものになります。よかったらこっちもチェックしてみてください！

👉 [Easy Table Converter (旧版)](https://v0.dev/community/easy-table-converter-yot2pHymqAQ)

# 使用した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のみで書いて
(要件)
```

でてきたアウトプットの一部

![初期モックのスクリーンショット](https://storage.googleapis.com/zenn-user-upload/592fb1ae6555-20250603.png)

初期のモックでも十分使用できるレベルだった

**Step 2 – React で機能追加**  
HTMLができたらReactの関数をつけて実際に使用して使用感を確かめた

**Step 3 – コンポーネントの分割** 
o3に作ってもらったコードを手作業で別ファイルに移していった。AIエージェントならこのへんを自動でやってくれそうだけど、今回は勉強を兼ねていたのでこのような形になった

**Step 4 – ファイル構成を LLM とレビュー**  
分割後の構成・命名が Next.js のベストプラクティスに沿っているか o3 に相談し、Gemini／Perplexity でクロスチェック。最終的に以下のようなファイル構成がベストプラクティスなんじゃないかという結論にたどり着いた

<details> <summary>ディレクトリ構成</summary>

```
.
├── .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 設定 (利用する場合)
```

<<details><summary>details</summary>

</details>


    

上記の1-4を繰り返して作成したのが以下

![初期モックのスクリーンショット1](https://storage.googleapis.com/zenn-user-upload/0aec1711b557-20250603.png)

![初期モックのスクリーンショット2](https://storage.googleapis.com/zenn-user-upload/bb001d6e51a2-20250603.png)

上記は上記でシンプルで好きだったので、これができた時点でコミュニティに投稿しようと思っていたのだが...

## 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段階で使用方法を説明
```

すると以下のページが出力された

![Vercel v0 の出力1](https://storage.googleapis.com/zenn-user-upload/2c6734d94405-20250603.png)

![Vercel v0 の出力2](https://storage.googleapis.com/zenn-user-upload/47dadc1220a5-20250603.png)


上記の結果には少し驚いた。o3に同じプロンプトを与えて出てきたのは以下だったからだ

![ChatGPT o3の出力](https://storage.googleapis.com/zenn-user-upload/21fbf674e319-20250603.png)

え！？なんか一発で思ったようなの出てきた...!!
ついでに千行くらいコード書いてくれてる（o3だと200行を超えるソースを出力させると出力結果が悪くなった）

この時点からv0の方がNext.jsのプロジェクトではこーでぃんぐ能力が高いんじゃないかと思い始めて、v0で作成し始めた

次にv0にCSSで装飾するように依頼した。できたのが以下

![すばらしいCSS装飾](https://storage.googleapis.com/zenn-user-upload/800b54d0f437-20250603.png)


え！？見やすい...!?俺の今までの一か月はなんだったの...?そんな感じがして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が発展していくにつ入れてこのような問題は過去のものになっていくのかもしれないが、現時点では人間の手による補助は必須で、まだ人の手がいるんだなと感じた。