はじめに
タイトルにある通り、プログラミング独学を始めてから5ヶ月ほど経った頃、友人からWebサイト作成の依頼がきた。オリジナルユニフォーム製作のサービスを始めるにあたり、顧客の窓口となるようなサイトが欲しいとのことだった。
Udemyの学習にてサンプルサイトをデプロイし、次はオリジナルのコンテンツを作りたいと思っていた自分にとっては絶好の機会だった。今回は初のWebサイト作成と、その公開に至るまでの記録を残しておく。同じ境遇の方などに、僕の経験が参考になれば嬉しい。
実際のサイト: AMBICIÓN
ソースコード: GitHub
ツール
スキルレベル
- Progate: Python, Html, Css, Javascriptを一通り
- Udemy: [100 Days of Code - Python] Day 70
- Google Associate Cloud Engineerの資格勉強(2週間ほど)
- プログラミング学習期間 約5ヶ月
使用した技術
コーディング
- python 3.9
- Flask 2.0.2
- Bootstrap5
- jQuery
- CSS
- HTML
ヴァージョン管理
- Git & GitHub
サーバー
- gunicorn
- Heroku
- Google Cloud Functions(gcp)
独自ドメイン取得
- お名前.com
DNS
- Cloudflare
目標と条件
依頼
- 友人のSNS等でサービスを知った方達の窓口となるサイトが欲しい。
友人からの最初の要望は以上である。非常に広い。
条件
- ランニングコストはできれば0円で。
無知な当時の僕の感想としては、「まあ、良くわからんけどタダで作る方法はあるだろう。」
まずはどんなサイトにするのか大まかなイメージ作り
情報収集
手始めにユニフォームを作る会社のホームページを一通りあたってみた。
デザインシミュレーターやテンプレートでユニフォームのデザインを決め、枚数やデザインをもとに見積もりを依頼(もしくはサイト上で自動計算)、注文。今までの製作事例やキャンペーン情報など多彩なコンテンツ。
さまざまなサイトを閲覧する中で、一つ共通する気になるポイントが見つかった。
何枚作っていくらかかるのか、一番知りたい情報を見つけるのに苦労する
最低でも2~3回のクリック(ページ遷移)やさまざまな選択を経なければ、はっきりとした情報に辿り着けないサイトが多かった。
どうすればお得なのか、損なのか、一枚あたりいくらで、自分の欲しい枚数だといくらかかるのか。
これから作るサイトではこの情報をできるだけ簡潔にユーザーに届けたいという気持ちが芽生えた。幸い友人のサービスでは、デザインによる価格の変動がなく、枚数当たりの値段もはっきりしていたので、実現は可能だと考えた。
現状の強みと弱み
友人は現役のプロサッカー選手であり、彼がスリランカのリーグでプレーしているときに現地のユニフォーム製作工場と繋がりができた。彼が日本のファン向けに所属クラブのレプリカユニフォームを作成する際に訪れたことがきっかけである。実際に公式試合で着ているユニフォームと遜色ないクオリティの高さ。さらには先方から提示された料金の安さに驚いたそうだ。プロ目線で高いと言える品質と低価格、この2点は強みになると考えた。
一方で、大抵のユニフォーム製作サイトにあるようなデザインシミュレーターを導入することは現状難しい。これではユニフォームのデザインを顧客に用意してもらう必要がある。そこでサイト内で注文を完結させることは今回は見送ることにした。
スタートは、デザインのやりとりなど少し手間がかかるけど、予算をできるだけ抑え、なおかつ品質の良いユニフォームを作りたい方向けのサービスとなる。
以上のことから、友人のバックグラウンド、料金の透明性、このあたりを軸にサイト作成を始めていくことにした。
サイトの構成
-
作成事例の写真、紹介動画、各ページへの入り口などのコンテンツを表示している。
-
メインとなるページ。料金自動計算システムを搭載。希望の枚数やユニフォームのフレームとなる要素を選択し、注文までのやりとりを開始することができる。
-
友人のバックグラウンド、サービスを始めた経緯、スリランカ現地の工場の様子などを見ることができる。
作成課題と達成方法
ここからは実際に作成中にこだわったポイント、発生した問題とその解決方法を記していく。一つ一つ書いていくと途方もなく長くなってしまうので、今回は料金計算システムのことについてのみ取り上げようと思う。
なぜこの機能を実装するに至ったか
サイトのイメージ作りのセクションでも触れたが、当初僕はユーザーが簡単にユニフォーム作成にかかる料金を確認できることに注力したいと考えていた。そこで手始めに友人から送られてきた詳細をもとに料金表を作成した。それがこちらである。
海外の工場からの輸入品という特性上、輸送料、関税などを含めユニフォーム代以外にいくらかかるのか、ユーザーが気になるであろう部分も全て一目でわかるようにした。税別、税込表記も忘れずに。
友人には、「これ以外にかかるお金はないね!?大丈夫ね!?」と何度も念を押して確認した。笑
しかしこれだけでは、ユーザーが正確な費用を確認するには紙とペンと電卓が必要だろう。
何か自動で計算してくれるモノがあればいいのになぁ、、、
プログラミングじゃん。
人間がやったらめんどくさいことを自動でやってくれる。まさにプログラミングで解決すべき課題と出会い、妙に嬉しくなった。
さてどのように解決しようか。学習中にFlaskでブログ作成を行なった経験から、
- ユーザーの入力をformで送信。
- POSTメソッドで値を受け取り、料金をpythonで自動計算。
- 結果の値を反映したページを返す。
と言う方法がまず浮かんだ。しかしこれではユーザーが値を変更するたびにformを送信し、ページをリロードしなければならない。とても快適とは言えない。
ユーザーが値を変えるたびにパッパ、パッパ計算結果が変わって〜みたいな感じにできないかな?
これはいわゆるフロントエンドの領域ってやつか?
僕はさっそく現在フロントエンドエンジニアとして働いている大学時代の先輩にコンタクトを取った。
僕 「なんかこう、値を入力するとパッパ、パッパ結果が返ってくる感じにしたいんですけど。これっていわゆるjavascriptってやつ使うんですか?」
先輩 「いわゆるjavascriptってやつ使うね。」
このど素人丸出しの質問にも丁寧に答えていただき、今回の僕のケースではBootstrapを使っていたこともあり、javascriptそのものではないが、jQueryで実装する方法を授かった。学習を始めてからずっと一人でコードを書き続けていたので、初めて誰かとあーだこーだいいながらコードをいじることの楽しさを実感した。基本的な知識を授かり、あとは自力でなんとかしてみることにした。先輩ありがとうございました。
jQueryを実際に書いてみると、python学習で得た考え方が応用できる部分が多々あり、なんとか実装することができた。
ユーザーの注文パターンにマッチした、しかも発生する費用を全て含めた一枚あたりの代金を一瞬で確認できる。コストパフォーマンスの高さをわかりやすくアピールできる形を生み出せたと思う。
(ちなみに僕が調べたさまざまなサービスのオリジナルユニフォーム上下の相場はだいたい8,000円~10,000円ほどだった。)
以下、本機能のソースコードを抜粋
1. 枚数による料金の変わり目をkeyにした辞書を作成。
const uni_dict = {
'default': {0: 0, 1: 0, 2: 0, 3: 0, 4: 0},
'set': {0: 5500, 1: 4500, 2: 4500, 3: 3500, 4: 3500},
'shirt': {0: 4000, 1: 3500, 2: 3500, 3: 3500, 4: 3500},
'pants': {0: 3000, 1: 2500, 2: 2500, 3: 2500, 4: 2500},
};
const socks_dict = {0: 1500, 1: 1000, 2: 1000, 3: 1000, 4: 1000};
const trans_dict = {1: 200, 2: 180, 3: 180, 4: 150};
const delivery_cost = 1000;
trans_dict(国際輸送量)は19枚以下で5000円に固定なので(ここだけ一枚あたりではない例外のため)、keyは"1"から始まっている。
2. 「ユーザーの入力した枚数に応じて、辞書から値段を取得し料金を計算、その結果を表示する」関数を作成。
const checkout = function() { //selectorの値が変わる毎に、.checkoutに代金を反映させる。
if (number <= 0) {
$('#uni-price').text('¥0');
$('#trans-price').text('¥0');
$('#delivery-cost').text('¥0');
$('#tax').text('¥0');
$('#with-tax').text('¥0(税込)');
$('#a-set-of-price').text('¥0(税込)');
}else{
if (number < 20) {
var amount = 0;
}else if (number < 40) {
var amount = 1;
}else if (number < 50) {
var amount = 2;
}else if (number < 60) {
var amount = 3;
}else{
var amount = 4; //「amount」は、枚数による代金の変わり目。
}
var uni_price = uni_dict[uniform][amount] * number; //ユニフォーム代金 現在の枚数における1枚あたりの値段 * 枚数
if (socks == 'socks') { //ソックスありの場合
uni_price += socks_dict[amount] * number; //ソックス代金 現在の枚数における1枚あたりの値段 * 枚数 を追加。
}
if (amount == 0) {
var transportation_price = 5000; //国際輸送料 19枚以下の時。
}else{
var transportation_price = trans_dict[amount] * number; //国際輸送料 現在の枚数における1枚あたりの送料 * 枚数
}
var without_tax = uni_price + transportation_price + delivery_cost; //税抜合計
var tax = without_tax * 0.1; //税
var with_tax = without_tax + tax; //税込合計
var a_set_of_price = Math.ceil(with_tax / number); //1枚あたり
$('#uni-price').text(`¥${uni_price.toLocaleString()}`);
$('#trans-price').text(`¥${transportation_price.toLocaleString()}`);
$('#delivery-cost').text(`¥${delivery_cost.toLocaleString()}`);
$('#tax').text(`¥${tax.toLocaleString()}`);
$('#with-tax').text(`¥${with_tax.toLocaleString()}(税込)`);
$('#a-set-of-price').text(`¥${a_set_of_price.toLocaleString()}(税込)`);
}
};
3. 値が変更されるたびに、変数に格納し、2.で作成した関数を実行。
$('#uniform').change(function() {
uniform = ($(this).val());
change_selected(id='uni-selected', selected=uniform);
checkout();
});
$('#socks').change(function() {
socks = ($(this).val());
change_selected(id='socks-selected', selected=socks);
checkout();
});
$('#number').change(function() {
number = ($(this).val());
$('#num-selected').text(`${number}枚`);
$('#num-selected-data').val(`${number}枚`);
checkout();
});
以上簡単にまとめてみたが、実際にサイトをいじって遊んでくれると嬉しい。
その他の課題と解決方法まとめ
上記以外につまづいた主なポイントについてまとめておく。使用したサービスのリンクも貼っておく。サイトの高速化、運用については最初の条件にあるように、できるだけ無料で達成することにこだわった。この縛りに向き合ったことは、今後別のサービスを自分で開発するときも、お金をかけずにこのくらいのことなら達成できるんだという一つの指標になってくれるので、とても有益な経験になった。
モバイルフレンドリー問題
これはかなり時間を要した問題であった。いわゆる「どんなデバイス、画面のサイズでも読みやすいデザインを維持する」ということだ。こだわればこだわるほど果てしなかった。。。
- htmlタグにvwを指定
- htmlより下の要素の単位はremで統一
- @media screen でデバイスごとの調整
でクリア。おかげでGoogle Search Consoleさんにも「モバイルフレンドリーですよ。」と言ってもらえた。ぜひみなさんも表示サイズをいじってレイアウトの変化を確認してみてほしい。
ページ遅すぎ問題
デプロイ後に見てもらった知人たちからほぼ100%もらった指摘。
- 画像のリサイズ - TinyPNG
- 画像の圧縮 - Bulk Resize Photos
- Cloudflareの圧縮機能
- Cloud Functionsの定期実行で平六(Heroku)を寝かせない(平六は30分間呼ばれないと勝手に眠ってしまう。)
で改善。友人曰く、100倍速くなった。
検索しても一向にサイトが現れない問題
やっとこさ完成したサイトの余韻に浸っているときに友人がサラっと言う。
友人 「え?検索しても出て来なくね?」
- お名前.comで独自ドメインを取得
- CloudflareをDNSに設定
- Google Search Consoleにインデックス登録
- HTMLにdescriptionを追加
すまん。1円(ドメイン取得代金 2年目からはきっちり1500円)かかった。しかし検索結果に現れた時は嬉しくて声が出た。
総括
サイトの構成 - 機能の立案・実装 - デザイン - デプロイ - 運用改善
振り返るとなかなか長い道のりだったが、問題を諦めずにひとつひとつ乗り越えていけば実現できるプログラミングの楽しさを味わえた。
そしてこれで終わりではなく、将来的にはシミュレーターの導入、注文システムの構築なども実現したい。
最後にこのようなチャレンジの機会を与えてくれた友人に感謝したい。ありがとう。