1. はじめに
生成AIでアプリ開発する記事を見かけて、今更ながら生成AIによるコーディングに興味が湧きました。そこでChatGPTを使って簡単なTodoアプリの実装をすると、どのくらい時間がかかるのか、タイムアタックをしてみることにしました。FrontendはVue.js、BackendはGo、DatabaseはPostgreSQLで作成しました。
結論から言うと、大体5時間くらいで完成しました。完成品のクオリティを見て、「このクオリティに5時間かかったのか」と自分でも思う一方、不慣れなところが多く詰まりまくったにも関わらず5時間でできたことに驚いています。
完成品はこちらのレポジトリにあげてるので、興味あれば是非。
とりあえずやってみる
HTML、CSS、Java scriptのファイルが出力されたようです。回答の全文を確認したい場合は以下の折り畳み要素を開いて確認してください。
ChatGPTの回答全文
Todoアプリを作成するための基本的な手順を説明します。以下は、HTML、CSS、JavaScriptを使ったシンプルなTodoアプリの例です。
1. HTMLファイル(index.html)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todoアプリ</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>Todoリスト</h1>
<input type="text" id="todo-input" placeholder="タスクを追加...">
<button id="add-todo">追加</button>
<ul id="todo-list"></ul>
</div>
<script src="script.js"></script>
</body>
</html>
2. CSSファイル(styles.css)
body {
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 20px;
}
.container {
max-width: 400px;
margin: auto;
background: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 24px;
}
input {
width: calc(100% - 100px);
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
}
button {
padding: 10px;
margin-left: 10px;
background-color: #5cb85c;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
}
button:hover {
background-color: #4cae4c;
}
ul {
list-style-type: none;
padding: 0;
}
li {
padding: 10px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
}
li button {
background-color: #d9534f;
}
3. JavaScriptファイル(script.js)
document.getElementById('add-todo').addEventListener('click', function() {
const todoInput = document.getElementById('todo-input');
const todoText = todoInput.value.trim();
if (todoText) {
const todoList = document.getElementById('todo-list');
const listItem = document.createElement('li');
listItem.textContent = todoText;
const deleteButton = document.createElement('button');
deleteButton.textContent = '削除';
deleteButton.addEventListener('click', function() {
todoList.removeChild(listItem);
});
listItem.appendChild(deleteButton);
todoList.appendChild(listItem);
todoInput.value = '';
}
});
使い方
- 上記のコードをそれぞれ
index.html
,styles.css
,script.js
という名前のファイルに保存します。 -
index.html
をブラウザで開くと、Todoアプリが表示されます。 - タスクを入力して「追加」ボタンを押すと、タスクがリストに追加されます。
- 各タスクの「削除」ボタンを押すと、そのタスクが削除されます。
これで基本的なTodoアプリが完成です!質問や改良したい点があれば教えてください。
ここまでがChatGPTの回答です。
それぞれをコピペして動かしてみます。
問題なく動きました!これでTodoアプリ完成です!
...
これではあまりにも面白くないので、もう少しハードルを上げてみることにしました。
ゴールを設定する
少しハードルを上げて、BackendとFrontendを分けてSPAで作ってみることにしました。そのほかにもいくつか詳細を決めて、最終的に以下の条件を満たすTodoアプリを作ることをゴールとしました。
- TodoアプリはSPAで実装する。
- データはBackendのインメモリではなく、DBを使って永続化する。
- FrontendはVue.jsで、BackendはGolang、DBはPostgreSQLで実装する。
- CRUDの4機能を少なくともそれぞれ一つは持つ。
- DockerとDocker Composeを使って環境構築を行う。
ちなみにFrontend、Backend、DBの技術選定については、「最近触った技術だから」というだけで深い理由はないです。
いざタイムアタック
ゴールが決まったので、早速タイムアタックをしてみます。スタートしたのが昼過ぎで、今日中に完成させることが目標です。
Chat履歴を全て削除してしまったため、作成の過程を詳しく書けませんでした。代わりにやっていて詰まったポイントをメモしておきます
作成していて詰まったポイント
1. Golangがdocker build
できない
開発に使っていたPCがM1 Mac(Apple Silicon)だったことで、ビルドが失敗していました。基本的にDockerコンテナ内で実行されるには、Linux向けバイナリが必要です。そのため、goのビルド時に、Linux向けバイナリが作られる必要があるのですが、ビルド時に必ずしもLinux向けバイナリが生成されるわけではなく、デフォルト設定はホストOSの環境に依存します。使っていたPCがM1 Macだったので、Apple Silicon向けバイナリが生成されており、それが原因でその後の処理が失敗していたようです。
バイナリ生成時の設定を、デフォルトからLinuxに明示的に指定してあげることで解決しました。
RUN go build -o main .
この部分を以下のように変更する。
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main .
2. vue devtoolsが使えない
VueでFrontendを作成する場合、便利なデバッグツールとして、vue devtoolsがあります。しかしこの機能が、docker-composer up -build
を実行してFrontendを確認したところ使えませんでした。
productionモードでビルドしていたことが原因だったようです。productionモードでは、アプリケーションの最適化のために、開発ツールやデバッグ情報を無効かしているようです。
Dockerfileでは、この部分です。
RUN npm run build
ここを書き換えて開発モードにすれば一応vue devtoolsを確認できるようになるのですが、なんとなくdockerで配布する分に関してはプロダクションモードの方がよさそうという理由でここはそのままにしています。開発時は普通にローカルの環境で動かすという対応をとりました。
このタイミングで知ったのですが、開発時は逐一dockerを起動してbuildするのではなく、同じようにソースコードを動かせるローカル環境を作ってそちらで動かすのが一般的なようです。
ちなみに、productionモードでもvue devtoolsを動かせるようにする方法は調べてみると結構色々あったみたいです。
そして完成
僕の不手際で一番重要な制作過程を吹っ飛ばしてしまったので、もはや完成後の話について書こうと思いますが、一応
一応最後に完成品について少し説明をしていこうと思います。
レポジトリ構成
まずはレポジトリ構成から。
一つのレポジトリにfrontendもbackendも入っているモノレポという構成で作成しました。
frontendはnpm create
を実行して自動生成される一般的な構成になっています。backendはファイルが少ないこともあり、かなり簡素な構成です。もう少し複雑な構成になっていく場合は、Go standardsなるものがあるみたいなので、そちらに従って拡張していこうと思います。
todo-app/
├── backend/ # バックエンドのコード
│ ├── main.go # エントリーポイント
│ ├── handlers/ # APIハンドラー
│ │ └── todo.go # Todoに関するハンドラー
│ ├── models/ # Todoモデル
│ │ └── todo.go # Todoモデル定義
│ ├── database/ # データベース接続関連
│ │ └── db.go # データベース接続処理
│ └── Dockerfile # バックエンドのDockerfile
├── frontend/ # フロントエンドのコード
│ ├── src/ # Vue.jsのソースコード
│ │ ├── components/ # Vueコンポーネント
│ │ ├── views/ # ビュー
│ │ ├── App.vue # アプリケーションのメインコンポーネント
│ │ └── main.js # エントリーポイント
│ ├── public/ # 公開リソース(index.htmlなど)
│ │ └── index.html # アプリケーションのHTMLエントリーポイント
│ ├── nginx.conf # Nginxの設定ファイル
│ ├── package.json # プロジェクトの依存関係とスクリプトを管理するファイル
│ └── Dockerfile # フロントエンドのDockerfile
├── db/ # データベース設定(Docker Compose用)
│ └── init.sql # 初期データのSQLスクリプト
├── docker-compose.yml # Docker Compose設定ファイル
├── go.mod # go.mod
├── go.sum # go.sum
└── README.md # プロジェクトの説明書
frontendについて
ディレクトリは分かれていますが、コードを書き足したのはApp.vueのみです。全てApp.vueに書いてしまっても、量がそこまでないので、ChatGPTを使う際の都合の良さを優先して、そのままにしました。ファイルが一つだと、全選択してコピペすれば、全て質問に投げられて便利でした。
CSSについて、「この辺いい感じにして」みたいな雑なチャットを投げても、本当に思った通りいい感じにしてくれて結構感動しました。
Java scriptはちょっと読めるくらいなので、正解の形が分からず評価しづらいですが、できるだけコードの変更をしないで無理やりやりたい要件に合わせて関数を定義した部分があるので、もっと全体最適なコードを出力させたらどうだったのかなとは思いました。
見返すとちょいちょいデバッグ時に埋め込んだコードが残っていて、これは消すべきでした。次はこれをリファクタリングする予定なので、その時消します。
backendnについて
Todoアプリをできるだけ早く作ることが目的だったので、省略しましたが、せっかくだし簡単な単体テストのコードくらいは書いても良かったなと思いました。
全体的に大したコード量もないので、まあそうなるかとは思いつつ、database/db.goについて、connectionを確立する部分と、実際にDBを叩く部分くらいは分けた方がいいかなと思いました。
また、DBディレクトリが流石にinit.sql
だけなのは寂しいので、もう色々足していきたいです。
終わりに
勉強のアウトプットとして記事を書くことに元々懐疑的だったのですが、ものは試しとやってみたというのが、この記事を作成した背景なのですが、想像以上にアウトプットの効果を感じました。具体的には以下です。
- 学びを言語化することで、理解が深まる
- 理解が適当だったところを、改めてちゃんと調べる機会になる
- 次はもっとまともなものを作ろうと、勉強モチベが上がった
どれも「アウトプット メリット」で調べたら真っ先に出てくるようなものではありますが、実際にやってみて自分で感じると違うもので、今後も定期的に学びのアウトプットとして記事を書こうという気になりました。
タイトルと全然関係ない終わり方となってしまいましたが、ここまで読んでくださりありがとうございました。