はじめまして
これまでこのシリーズでは、情報セキュリティやコンピューターの仕組みなど、「知っておくと得する知識」を中心にお届けしてきました。今回は少し趣向を変えて、私自身がプログラミングを一から学び、実際に1つのホームページを完成させて世界に公開するまでの記録をお届けします。
きっかけは単純で、「自分の手で仕事を受けられるくらいの技術を身につけたい、その先で海外でも使われるようなWebサービスを作れるようになりたい」という目標でした。とはいえ出発点は、HTMLを少し触ったことがある程度のほぼ初心者です。AI(Claude)を家庭教師役にして、マンツーマンの授業形式で学び始めました。
結論から言うと、最終的に「近鉄日本橋駅構内にできた架空のジュース屋『薫風屋』」というホームページを1つ完成させ、多言語対応(日本語・英語・中国語)まで実装し、友達に「近鉄日本橋に薫風屋さんができたらしいよ」とページを見てもらったところ、自作だと気づかれることなく好意的な反応をもらえるところまでたどり着きました。この記事では、そこに至るまでの約19回分の授業と、詰まった壁、乗り越え方、そして途中で得た「一生使えそうな考え方」を、できるだけ具体的にまとめます。
これからプログラミングを始めようとしている方、AIを使った学習に興味がある方の参考になれば嬉しいです。
出発点と、最初に決めた学習ルール
まず選んだ言語はJavaScriptでした。理由は大きく3つあります。Web制作の実務でそのまま使えること、Next.jsでWebサービスを作ったりReact Nativeでアプリを作ったりと、将来的に世界向けのプロダクトへとつながっていく道筋が見えていたこと、そして英語圏の情報量が圧倒的に多く、学習を進めるうえで有利だと判断したことです。最終的には、JavaScriptを土台にしたモダンな技術の組み合わせで、Webサービスそのものを作れるところまでを見据えています。
授業を始める前に、次の3つ(のちに4つ)のルールを決めました。これが後々ずっと自分を支えることになります。
コードは自分の手で打つ。コピペはしない。打ち間違いで出るエラーこそが教材だから
詰まったら15分後に質問する。5分では考える力が育たず、1時間は時間の無駄になる
「答え」ではなく「ヒント」を求めることを基本にする。丸ごと解説が欲しいときはそう伝える
意味の分からないコマンドは実行しない
もう1つ、学習の初期に気づいた大事な前提があります。それは「AIはチャットをまたぐと、前の会話を覚えていない」ということです。そこで、区切りのたびに学習内容をログとしてまとめ、次の会話の冒頭に貼り付けて引き継ぐ、という運用にしました。今回のこの記事の元になっているのも、まさにそのログです。振り返ってみると、これは「引き継ぎ書を書く」というエンジニアリングの実務そのものの練習にもなっていました。
基礎編:HTML11行から、JavaScriptの壁まで
最初の授業は、<!DOCTYPE html>から始まるたった11行のHTMLを手で打ち込み、ブラウザで開くところからでした。所要時間は30分。「タグは箱で、箱の中に箱を入れる」という入れ子構造を、頭でなく手で覚えていきました。
「動いているけど間違っている」の怖さ
自己紹介ページを作った回で、最初の大きな学びがありました。見出しタグ(h2)の中に本文まで全部入れてしまい、ページ全体が太字になってしまったのです。見出しタグは「ラベル」であって「箱」ではない、という区別を体で理解した瞬間でした。
さらに、文字コード指定でUTF-8とすべきところをUFT-8とタイプミスしていたのに、日本語は普通に表示されてしまう、という出来事もありました。ブラウザが親切に(?)推測して表示してくれていただけなのですが、ここで得た教訓は今でも大事にしています。「動いているけど間違っている」は、エラーが出て止まってくれるバグよりもずっと怖い、ということです。エラーは教えてくれますが、沈黙は何も教えてくれません。
「変化しない」は「働いていない」とは限らない
CSSでカード型のレイアウトを組んでいたとき、display: blockという指定を入れても外しても見た目が変わらないことに気づき、質問しました。調べてみると、実はレイアウトの幅(max-width)が狭かったせいで画像が折り返していただけで、その指定自体はちゃんと機能していたのです。見た目の変化がないからといって、その指定が「効いていない」とは限らない――これも、後々何度も助けられた考え方です。
人生初の本番環境デバッグ
コードを管理するサービスと、公開用のホスティングサービスを使って、初めて自分のページを世界に公開した回では、いきなり404エラーに遭遇しました。原因はフォルダごとアップロードしてしまい、index.htmlがリポジトリのルートに置かれていなかったことです。リポジトリを作り直すこと数回、ようやく開通したときの安堵感は今でも覚えています。地味ですが、これが人生初の「本番環境のデバッグ」でした。
「関数を作る」と「関数を呼ぶ」は別の行為
同じ処理を2箇所に書いてしまっていたコードを、updateDisplay()という1つの関数にまとめる課題で、最初の大きな壁にぶつかりました。「関数を作る」ことと「関数を呼ぶ」ことは別の行為だ、という感覚がなかなか掴めず、3回やり直しています。1回目は名前のない処理に名前を付けただけで重複は減らず、2回目は関数自体は作ったものの、どこからも呼び出されていない「誰も使わない工具」になっていました。3回目でようやく「これか!」と腑に落ちました。
文字列と、初めてのセキュリティ体験
オブジェクトを学んだ回では、foodと書くべきところをそのまま画面に表示しようとして[object Object]という文字列がそのまま表示されてしまったり、閉じタグの書き方を"</h3></p>"のようにスラッシュの位置を間違えてカード表示が丸ごと崩れたり、という失敗を重ねました。この混乱がきっかけで、バッククォートと${}を使う「テンプレートリテラル」を前倒しで教わることになり、文字列を+でつなぎ合わせることからようやく解放されました。
そしてJavaScript基礎編の卒業制作となった入力フォームの回では、思いがけず本物のセキュリティの実演をすることになりました。試しに入力欄へ<b>太字テスト</b>と打って追加してみたところ、タグの部分が消えて文字だけが太字で表示されてしまったのです。つまり、ユーザーの入力がただの文字列としてではなく、「命令」として実行されてしまっていたということです。これはまさにXSS(クロスサイトスクリプティング)と呼ばれる脆弱性の入り口で、createElementとtextContentを使う書き方に直すことで、同じ入力がタグごとそのまま表示される(=命令として実行されない)ことを確認しました。以前このシリーズでセキュリティの記事を書きましたが、まさかこんなに早く、自分のコードで「攻撃が刺さる瞬間」を体験することになるとは思っていませんでした。
実戦編:架空のジュース屋「薫風屋」ができるまで
基礎を一通り終えたところで、実在のランディングページを「同業者の目」で観察するところから実戦編が始まりました。観察して見えてきたのは、ヘッダー・ヒーロー・特徴・メニュー・お客様の声・FAQ・店舗情報・フッターという、業種を問わず共通する「黄金の骨格」でした。
そのうえで、自分で1つの店を設定しました。近鉄日本橋駅構内にある、架空のジュース屋「薫風屋」。ジュース8品(500〜800円)、日本語・英語・中国語対応、お知らせ・お客様の声・FAQ・店舗情報つき。目標は「近鉄日本橋に薫風屋ができたらしい」と友達に送っても違和感のないクオリティに仕上げることでした。振り返ってみると、この段階で自分なりに書き出した項目リストは、業界でいう「要件定義書」そのものだったと思います。
崖から、階段への切り直し
実戦編の中で一番印象に残っているのが、途中で一度大きく詰まった回です。骨格をゼロから作りながら、Webフォントの導入、ヘッダー、新しい技術、お知らせ帯までを一度にまとめて教わろうとしたところ、完全に処理落ちして2度目の「ギブアップ宣言」をすることになりました。
ただ、振り返ってみるとこれは自分の実力不足ではなく、単純に「段差の設計ミス」でした。1回で登ろうとした崖が高すぎただけだったのです。そこで課題を、ヒーロー画像の実装、Webフォントの導入、ヘッダーの実装、お知らせ帯の実装、デプロイ、という5つの小さな階段に切り分け直してもらったところ、そこから4回連続で一発合格になりました。「無理だ」と感じたときは、たいてい自分の限界ではなく、段差の設計のほうに問題がある――このときの経験は、その後も何度も自分を助けてくれる考え方になりました。
ちなみにヒーロー画像は、写真を全画面に敷いたうえでlinear-gradientという指定で暗い膜を1枚重ね、その上に白い文字を浮かせる、という組み方をしています。Webフォントで明朝体(Shippori Mincho)を当てた瞬間、それまで学習用のサンプルにしか見えなかったページが、急に「お店のホームページ」らしい佇まいに変わったのも、印象に残っている瞬間です。
苦労した仕組みが、そのまま店の顔になる瞬間
「こだわり」を紹介するカード3枚と、メニュー8品の実装では、それまで基礎編で苦労してきた技術がそのまま実を結びました。カード3枚は、ずっと前に「好きなものリスト」を並べる練習で組んだ構造がほぼそのまま使えましたし、メニュー8品は、商品名・価格・画像をオブジェクトの配列にまとめ、ループで1つずつカードを生成する仕組みで実装しています。かつて[object Object]と表示されて崩壊したあの回の苦労が、ここで「価格改定があっても直すのは配列のたった1文字だけ」という、実用的な強さに変わっていたことに気づいたときは、地味に感動しました。
多言語対応:仕組みだけで日本語・英語・中国語を切り替える
このホームページで一番気に入っている実装が、日本語・英語・中国語を切り替えられる多言語対応です。仕組みはシンプルで、HTMLの各要素にdata-i18n="キー名"という目印を付けておき、JavaScript側に「言語ごとの単語帳」をオブジェクトの中にオブジェクトとして持たせます。言語ボタンが押されると、選ばれた言語の単語帳をtexts[lang]という形で取り出し、querySelectorAll("[data-i18n]")で目印の付いた要素をまとめて書き換える、という流れです。ボタンの見た目の切り替えにはclassList.add/removeを使っています。特別なライブラリは使わず、それまでに習った道具だけで実装できました。
地味なところですが、営業時間などの数字の項目には、あえてこの目印を付けていません。数字は言語が変わっても翻訳する必要がない、という設計判断です。こういう「どこに手をかけて、どこは意図的に何もしないか」を自分で決められるようになったのも、実戦編で得られた収穫の1つでした。
スマホで開いたら、ヘッダーが縦に崩れていた
レスポンシブ対応の回では、スマートフォンで実際にページを開いてみたところ、ヘッダーの要素が縦に積み重なった状態で表示されて驚きました。原因は、Flexboxで横並びにしていた要素が、画面幅が足りないことで潰れてしまっていたことです。media (max-width: 768px)という指定を使って、画面幅が768px以下のときだけヘッダーを縦積みにする、メニューを4列から2列にする、お客様の声を2列から1列にする、といった細かい調整を積み重ねて解決しました。「スマホは、指定がないと自分のことを幅980pxのパソコンだと嘘をつく」ということを、身をもって確認した回でもあります。
仕上げ、そして友達判定
最後の仕上げとして、ページ内リンク(href="promise"とscroll-behavior: smoothによるなめらかなスクロール)と、深緑の地に白抜きで「薫」の字を置いたオリジナルのfaviconを追加しました。
そして迎えた最終テストが、友達に「近鉄日本橋に新しく薫風屋さんができたらしいよ」と伝えて、ページを見てもらう、というものです。結果は好意的な反応で、自作だと気づかれることはありませんでした。これが、今回の学習の一区切りになった瞬間です。
倒したバグたちと、そこから得た教訓
今回の制作を通じて向き合ったバグの中でも、特に印象に残っているものをいくつか紹介します。
こうして並べてみると、初心者のうちに遭遇しやすいバグには、ある共通点があることに気づきます。それは、画面に何のエラーも表示されない「沈黙型」のバグのほうが、赤字でエラーを吐いてくれる「騒ぐ型」のバグよりも、ずっと厄介だということです。エラーは「ここが変です」と教えてくれますが、沈黙は何も教えてくれません。だからこそ、見た目がおかしいと感じたら、まずCSSを疑う前に「実際にはどんなHTMLやテキストが組み上がっているのか」を、console.logなどで確かめてみる癖をつけることが、遠回りのようで一番の近道だと感じています。
教える側(AI)も、完璧ではなかった
今回はAIを家庭教師役にして学びましたが、教える側が常に完璧だったわけではありません。この点も記録として残しておく価値があると感じているので、正直に書きます。
たとえば、公開済みのはずのファイルを確認したときに、読み取りツールが空の結果を返しただけなのに、それを「ページが存在しない」と誤読し、実際にはもう公開されているものを「まだ公開されていない」と断定してしまったことがありました。指摘して訂正してもらいましたが、この一件は「やっこ 1 - 0 クラウド」という、ちょっとしたスコアボードとして記録に残っています。
また、CSSの中で画像を読み込む指定(url("images/hero.png"))が、実際にはcssフォルダを基準にした場所を指していたため、正しくは../images/と1つ上の階層を指定する必要がある、という単純な見落としもありました。
こうしたやり取りを通じて実感したのは、AIを使った学習であっても、出てきた説明やコードを鵜呑みにせず、実際に動かして確かめる姿勢が欠かせない、ということです。同時に、コードを渡してもらうときは「追加してほしいのか」「置き換えてほしいのか」を明確にしてもらうよう、こちらからもお願いするようにしました。実際、それが曖昧だったために、ボタンが意図せず6個に増殖してしまったこともあります。教える側も教わる側も、お互いに確認し合いながら進めるものなのだと感じました。
詰まったときに効いた考え方
最後に、今回の制作を通じて身についた、技術そのものよりも大事かもしれない考え方をいくつか紹介します。
「動く」ことと「正しい」ことは別物です。むしろ、たまたま動いてしまっているコードのほうが危険です。エラーには宛先があり、ブラウザのコンソールに出るものは自分宛てのメッセージ、エディタの出力パネルに出るものは道具同士のやり取りだと考えると、混乱しにくくなります。そして「もう無理だ」と感じたときは、たいてい自分の実力の限界ではなく、一度に登ろうとした段差の設計にこそ問題があります。1歩ずつに刻み直せば、たいていの崖は階段に変わります。
降参することは敗北ではなく、1つの手続きです。答えを教えてもらったとしても、それを自分の手で直し、自分の目で動作を確認するところまでやり切れば、そこにはちゃんと学びが残ります。そして、気づいていながら後回しにした小さな不具合(今回で言えば、コード中に残っているform-massageという綴りの誤りや、素材写真の透かしなど)は、忘れているのとは違います。認識したうえで優先順位をつけて後回しにするのも、立派な判断のひとつです。
おわりに:このあとの道
HTMLの11行から始まったこの学習は、今回「薫風屋」という1つのホームページの完成という形で、最初の区切りを迎えました。この先は、業種を変えた2本目のランディングページ(今度はコードを自分で書き、添削してもらう形式に挑戦する予定です)、これまで作ったものを並べるポートフォリオページ、そしてゆくゆくはココナラのようなプラットフォームへの出品も見据えています。そのさらに先には、モダンな技術スタックを使った、海外でも使われるようなWebサービス作りが待っています。
もしこれからプログラミングを学び始めようとしている方がこの記事を読んでいたら、伝えたいことが1つあります。エラーで止まってくれるバグよりも、黙って動き続けるバグのほうが怖いということ、そして「無理だ」と感じたときは、たいてい自分の限界ではなく段差の設計に問題があるということです。この2つさえ覚えておけば、11行のHTMLから始めても、思ったより遠くまで行けるのではないかと思います。
※本記事に登場する「薫風屋」は架空の店舗で、写真はフリー素材を使用しています。
※次回、番外編2になりますが、番外編2は記事の大半がコードで埋め尽くされており、そのコードの一部がココナラの禁止ワード扱いとなってしまったため、投稿ができない状況です。なので、番外編2はnoteの方で投稿させていただこうと思います。
アカウント名:Urunta
タイトル名:実践ITスキル講座 番外編2:完成した「薫風屋」のホームページを、コードから読み解く