
AIだけでWEBサービスやアプリは作れる~AI使って知識0(ゼロ)から始めるWEBサービス・アプリ制作①~
初めまして。2024年2月にLLM(大規模言語学習モデル)を使い始め、プログラミングの知識0(ゼロ)から、2025年1月にAndroidアプリ「イチオシ!」をリリースするまでに至りました。
この記事ではLLM(一般的には生成AIで良いのかな)を駆使して、プログラミングの知識無しで様々なWEBサービスやAndroidアプリなどを作っていく過程を書いてみようと思います。
生成AIとの出会い
「え、これ本当にAIが書いたの!?」
思わず声に出してしまったあの日の午後。
いつものように従業員向けの通知文書と格闘していた私。「時間短縮できねーかなぁ」と思いながら話題になっていたChatGPTを使ってみるかと検索してみました。
「文書作成はGoogleのGeminiが優れているかも!?」
という記事を見かけ、Geminiを使ってみることに。
正直、半信半疑でした。だって、いつも30分はかかる文書作成を、AIに任せるなんて。でも、“使いもせず評価はできないし、何より時代の変化に対応できなくなる。”そう思い、Geminiを立ち上げ「従業員の皆さんに向けて、通知文書を作成してください。内容は・・・・・・・」キーボードをポチポチと叩き、送信ボタンを押した瞬間、目の前で起こった出来事は、まるでSFの世界でした。たった数秒で、画面に現れた文章の出来栄えに、コーヒーを飲みかけていた私は思わずごくりと生唾を飲みました。温かみのある言葉選び、分かりやすい説明、そして何より、読む人の気持ちに寄り添った内容。
「もう少しフレンドリーな感じで」
「具体例を入れてみて」
なんて注文をつけると、まるで優秀な後輩が隣にいるかのように、スパスパっと書き直してくれる。「これ、自分で書くより全然早い!!!」その日を境に、私の仕事の風景は一変しました。
AIという新しい「相棒」との共同作業は、まるで魔法使いの弟子になったような感覚。アイデアを出し合い、推敲を重ね、最後は人間である私が仕上げに手を入れる。でもね、面白いことに、AIは完璧な上司でも、言いなりの部下でもありません。時には「それは違うんじゃない?」と考えさせられることもあれば、思いもよらないアイデアで目から鱗が落ちることも。
半信半疑で使ってみた生成AI。実に使えるヤツです。何度やり直しを指示しても嫌な顔一つしません。そして完成までの時間がぐぐぐぐっと短縮されていくことを経験した私は生成AIの可能性をもっと深く知りたくなりました。
一度は学んでみたかったプログラミング
私はいわゆるファミコン世代。子供のころから普通にゲームがあった世代です。子供心に「一度はゲームを作ってみたい!」と思いながら、気が付けば40歳を過ぎてしまっていました。
仕事でエクセルのVBAをほんのちょっとだけ学んだことはありましたが、本格的なプログラミングの知識はありません。WEBサービスもHP(ホームページ)すら作成したことがなく、フリーソフトのFFFTPを使って、すでにあるHPを更新するくらいしか経験がありませんでした。
そんな私がアプリを制作できるに至ったのは生成AIの存在なくしてはあり得ませんでした。
生成AIが書くコードについては賛否両論あるとは思います。しかしながら、素人でもアプリを作ることができるところまで、生成AIは進化しているという現実は認めざるを得ないと思います。
さて、私が実際にプログラミングの知識を学んでいこうと描いた当初のロードマップがこちらです。
インターネットでHTML、CSS、JavaScriptを学ぶ
自分でHPを作成する
HPに何らかのWEBサービスを展開する
アプリを制作する
誰でも考え付きそうなありがちな内容ですよね。そして誰もが学んでみようかなと思うことでもあります。
しかし、このロードマップはperplexityとの出会いによって劇的に変わることになりました。
perplexityの良い所はなんといってもclaude3.5 sonnetが使える点にあります。また詳しく書こうと思いますが、2025年1月時点でプログラミングをやろうと思ったらclaude3.5 sonnetは最も優秀だと思います。
そしてこの出会いからロードマップは以下に変化しました。
展開したいWEBサービスを考える
WEBサービスのコードをclaude3.5 sonnetで記述してみる
記述されたコードを読めるようにする
ホスティングしてみる
例えば「画像をJPG←→PNGに変換するサービス」を作りたいと考えたとして、これをperplexityでclaude3.5 sonnetを使って、そのままプロンプト欄に打ち込みます。
そうすると、
//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>画像形式変換サービス</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<h1>画像形式変換サービス</h1>
<div id="image-converter">
<input type="file" id="image-input" accept="image/jpeg,image/jpg,image/jfif,image/png,image/webp,image/gif">
<label for="image-input" class="custom-file-upload">画像を選択</label>
<select id="output-format">
<option value="jpeg">JPEG</option>
<option value="png">PNG</option>
<option value="webp">WebP</option>
<option value="gif">GIF</option>
</select>
<button id="convert-button">変換</button>
<a id="download-link" style="display: none;">ダウンロード</a>
<progress id="progress-bar" value="0" max="100" style="display: none;"></progress>
</div>
<div id="preview">
<img id="preview-image" src="" alt="プレビュー" style="display: none;">
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/browser-image-conversion/dist/browser-image-conversion.min.js"></script>
<script src="script.js"></script>
</body>
</html>
//CSSのコード。styles.cssという名前で保存
body {
font-family: Arial, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
.container {
width: 80%;
margin: auto;
overflow: hidden;
padding: 20px;
}
h1 {
color: #333;
text-align: center;
}
#image-converter {
background: #fff;
padding: 20px;
margin-bottom: 20px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
#image-input {
display: none;
}
.custom-file-upload {
border: 1px solid #ccc;
display: inline-block;
padding: 6px 12px;
cursor: pointer;
background: #f8f8f8;
border-radius: 4px;
}
select, button {
padding: 10px;
margin: 10px 0;
border-radius: 4px;
border: 1px solid #ddd;
}
button {
background-color: #4CAF50;
color: white;
border: none;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#download-link {
display: inline-block;
padding: 10px;
background-color: #008CBA;
color: white;
text-decoration: none;
border-radius: 4px;
}
#preview {
text-align: center;
}
#preview-image {
max-width: 100%;
height: auto;
margin-top: 20px;
}
#pr/ogress-bar {
width: 100%;
height: 20px;
margin-top: 10px;
}
//JavaScriptのコード。script.jsという名前で保存
document.addEventListener('DOMContentLoaded', function() {
const imageInput = document.getElementById('image-input');
const outputFormat = document.getElementById('output-format');
const convertButton = document.getElementById('convert-button');
const downloadLink = document.getElementById('download-link');
const previewImage = document.getElementById('preview-image');
const progressBar = document.getElementById('progress-bar');
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
imageInput.addEventListener('change', function(e) {
const file = e.target.files[0];
if (file) {
if (file.size > MAX_FILE_SIZE) {
alert('ファイルサイズが大きすぎます。10MB以下のファイルを選択してください。');
this.value = ''; // 入力をリセット
previewImage.style.display = 'none';
return;
}
const reader = new FileReader();
reader.onload = function(e) {
previewImage.src = e.target.result;
previewImage.style.display = 'block';
}
reader.readAsDataURL(file);
}
});
convertButton.addEventListener('click', function() {
const file = imageInput.files[0];
if (!file) {
alert('画像ファイルを選択してください。');
return;
}
progressBar.style.display = 'block';
progressBar.value = 0;
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
const format = outputFormat.value;
// 進捗バーのアニメーション
let progress = 0;
const updateProgress = setInterval(() => {
progress += 10;
progressBar.value = Math.min(progress, 90);
if (progress >= 90) clearInterval(updateProgress);
}, 200);
canvas.toBlob(function(blob) {
clearInterval(updateProgress);
progressBar.value = 100;
const url = URL.createObjectURL(blob);
downloadLink.href = url;
downloadLink.download = `converted_image.${format}`;
downloadLink.style.display = 'inline';
downloadLink.textContent = `変換された${format.toUpperCase()}をダウンロード`;
setTimeout(() => {
progressBar.style.display = 'none';
}, 1000);
}, `image/${format}`);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
});
こんな感じでコードを書いてくれます。
このそれぞれのコードをテキストファイルにコピペし、
名前を付けて保存を選び、index.htmlのように拡張子をhtml、css、jsにして同じフォルダに保存します。

そしてこのindex.htmlをダブルクリックすると・・・

こんな感じでアプリができてしまうわけです。
ここからそれぞれのコードの意味をまたperplexityに入力し、学習するという流れが私の基本スタンスになったというわけです。
長くなったので、続きはまた次回・・・