先日、ひとり旅のための宿検索サイト「ソロ旅ねっと」というサービスを公開しました。
ソロ旅ねっとは、複数地域、複数の宿泊予定日を自由に組み合わせて、一人宿泊が可能な旅館、ホテルを検索できるサービスです。
「どこかの金曜日に有給を取って北の方の温泉地に泊りで行きたい」、みたいな条件で、旅館、ホテルを簡単に検索できるようなサービスを目指しています。
このサービスをまじめに考えだしたのは2018年の12月初旬で、ソースを書き始めたのは同年12月の下旬頃です。
ソースコードの最初のコミットから3か月、最初のリリースからも約2か月が経ったので、これまでを振り返りつつ、宣伝もかねて、今回は、サービスを作ろうと思った経緯、サービスの中身の話などを、サービスの紹介と合わせてお話ししたいと思います。
なぜ作ろうと思ったか
僕は割と一人での旅行(特に温泉地)が好きで、年に数回、どこかの温泉地に赴き、1~2泊で、ひたすらダラダラするのを生きがいの一つとしております。
泊りで行くので宿泊場所は事前に取らなきゃアカンのですが、既存の宿泊施設検索サイトで宿探しをするのはとてもとても使いづらい&面倒くさいのです。
そこで、僕が使いやすいサービスを作ってみようと思ったのがきっかけです。
僕の使い方だと、今ある宿検索サイト全部がとてもとても使いづらい&宿探しが面倒くさい
「来週か再来週あたりの金曜日に、北の方の温泉地に行ってみたい」という条件で、宿泊施設を探そうとすると、既存の宿検索サイトはとんでもなく使いづらいです。
宿泊施設検索サイトは、楽天トラベル、じゃらん、JTB、など、いくつもありますが、どのサイトも目的地をきちんと入力しないと検索できないようになっております。
しかも、「北の方にある温泉地」などという曖昧な表現ではだめで、○○県××市、だとか ○○温泉、などという感じで目的地をかなり正確に限定する必要があります。
なぜ、こういう仕様なのか
僕は旅館・ホテル検索サービスを運営しているようなところで働いた経験がないので完全な憶測になりますが、そのようなサイト設計になっている原因はいくつか推測できます。
そういう需要はそもそも無い
検索サービスに入ってくる人の大半は、観光で行きたい場所などの目的地は完全に決まっている。
この場合、わざわざ広範囲での宿泊施設検索を用意する必要はありません。
システム設計の都合
内部管理している宿泊施設データが、地域をキーとして検索されることを前提として設計されている。
複数地域をまたいだ検索を実装した場合、クエリーが複雑になったり、サーバに負荷がかかったり、十分な応答性能が出せない可能性があり、簡単に実装することができない。
サービス公開後のシステム設計の変更ってとんでもなく大変なのです。
いい感じのUI/UXが思いつかない、もしくは実現が難しい
大雑把な地域でも細かい地域でも検索できる、見栄えが良く、使い勝手の良いUI/UXを提供するのは確かに難しいと思います。
複数の地域を色々選べるようにしても、エンドユーザさんが使いこなせなかったり、条件指定がめんどくさくて離脱してしまっては元も子もありません。
地域ごとに設定されている広告枠や検索結果上位表示枠の都合
宿泊施設から掲載料とは別にお金をもらって、地域ページのサイドバーに「おすすめの宿泊施設」として特定の施設を常時表示させたり(広告)、お金をたくさん支払ってくれた宿泊施設を検索結果の上位に表示させたりといった施策がとられていることもあると思います。
しかし、それゆえに複数地域をまたいだ検索コンテンツは提供しづらい(サイドバーのおすすめに何を出すか、表示順位をどうするかなどの問題)。
SEOがらみの都合
この辺あまり詳しくないのですが、サイトトップ > 都道府県 > 市区町村 > ホテル一覧 みたいなきれいな構造になっていた方がSEO上有利、なのかもしれません。
ただ、エンドユーザさんの使い勝手を犠牲にしてでも、検索エンジンには気を使うという設計方針が選ばれているんだとしたら少し残念と感じます。
自分の望む形のものがないならば・・・
いよいよ年末の2018年の12月初旬、お正月明けの温泉旅行プランを妄想しつつ、使いづらいと思いながらも楽天トラベルで宿検索をしておりました。その時に、作れそうなら、自分が使いやすいと思うサービスを作ってみてもいいかな、と思い、調査を始めました。
実現手段を考える
楽天が開発者向けに楽天市場の商品検索APIを公開しているのは知っていたので、楽天トラベルの情報もとれるのではないかと思い調べてみました。
楽天Webサービス:API一覧
ドキュメントを確認したところ、楽天トラベルの施設検索、空室検索もAPIとして提供されていました。
そこで、今回はこちらを利用させていただいて、一人旅専用の旅館、ホテルの検索サービスを作ってみることにしました。
なお、あとで知ったのですが、同様のサービスがじゃらんからも提供されているようです。
要件定義
サイトにどんな機能を盛り込むか
楽天が提供しているAPIを利用させていただくため、楽天が提供していない機能、規約違反になってしまうような機能は提供できません。
それを踏まえたうえで、APIドキュメントを読みつつ、どれくらいのことができそうかを決めていきます。
できそうなこと
- 複数地域をまたいだ一人で泊まれる旅館・ホテルの検索
- 1回のAPI呼び出しで取得できるのは、1つの地域の宿泊施設の結果のみ。
- 事前に全施設の情報を取得して、ソロ旅ねっと管理のデータベースに蓄積。
- エンドユーザさんが検索したら、楽天にクエリーを投げるのではなく、 ソロ旅ねっとのデータベースに問い合わせを行う。
- 1回のAPI呼び出しで取得できるのは、1つの地域の宿泊施設の結果のみ。
- 駅近く、温泉宿などでの絞り込み条件
- 温泉付き施設、朝食付きプランあり、夕食付プランあり、禁煙部屋の有り無し、といった絞り込み条件は APIで提供されています。
- また、鉄道駅が近くにあるか、という情報は、緯度経度付きの駅データを別途用意できれば実現可能
- 宿泊施設の緯度経度情報も必要だが、こちらは楽天トラベルの宿泊施設情報から取得できる
- 旅館、ホテルが提供しているプラン、価格を表示
- APIで、宿泊プラン(最大3件)、プランごとの価格情報は取得できるようになっている。
- ただ、最大3件までしか取得できないため、全プラン&それぞれの価格を提示することは不可能。
- 同じプランでも土日祝日などは少し高めの値段を設定している宿泊施設もそれなりにあるのですが、その情報もちゃんと取得できるようになっている。えらい。
- 当日から数えて1か月程度の空室状況の確認
- API単発の呼び出しでは、〇月〇日に大人1人で宿泊可能な、○○(地域、緯度経度半径xキロ以内 など)に存在する宿泊施設を検索ということしかできない
- 複数の宿泊日での検索を行うためには、APIで事前にクロール&データ取得しておく必要がある。
- 空室状況のデータは生ものなので、ある程度の頻度で常に更新しないといけない。
- 検索結果ページからの楽天トラベルの宿泊施設ページへのアフィリエイトリンク
- こちらは、APIで容易に取得できるようになっている
実現できそうにないこと
- 宿泊人数を自由に指定して検索できるようにする
- APIとして提供されている機能の制限、時間当たりに呼び出せるAPIの回数などの制限のため難しい
- 楽天が提供しているすべての絞り込み条件に対応する
- APIの検索条件としては利用できるのですが、API戻り値の仕様や、時間当たりに呼び出せるAPIの回数などの制限のため難しい
- 僕が絶対はずしたくなかった、夕食付プラン、温泉付き施設の2つと、別途用意した駅データを利用した駅近くの施設、の3つの絞り込み条件に対応することにした
- 駅近くの宿泊施設の場合、最寄り駅の時刻表サイトや、路線検索サイトへのリンクを張る
- APIの利用規約に、「APIを利用したページから楽天以外のサイトにリンクしてはならない」という利用規約があるため不可能。
- おそらく、楽天以外の予約サイトに飛ばしたり、amazonなどの競合他社サイトへの流出を抑制したいという意図があるのだと思いますが、ここは非常に残念なところ。
- そのため、アドセンス広告なども貼れない。悲しい。
- 施設周辺の地図を出したい
- google map APIの利用が有料化され、しかも結構なお値段がするので断念。
- 周辺地図として宿泊施設提供の地図を暫定的に表示中
- 並行してleafletの利用を検討中
「できそうなこと」でも書いていますが、このサービスを実現するには、宿泊施設の情報、空室状況を、常に更新していく必要があります。そして、それを実現するには結構な回数のAPIコールが必要です。
例えば、空室状況の更新を10日に1回とした場合、データ取得&検索タイミングによっては、すでに予約でいっぱいな施設だらけになってしまうかもしれません。
最終的には、
- どれくらい先までの空室状況を取得したいか
- 空室状況の鮮度はどれくらいにするか
- 検索条件として指定できるオプションで外したくないものはどれか
- DBに保存するデータ量はどのくらいになるか
- 現実的なAPI呼び出し回数で実現可能か
- 運用費用(サーバ費用)はどんなもんになるか
などを考えつつ、システム仕様に落とし込んでいきます。
ちなみに、ソロ旅ねっとでは、約5週間後までの空室状況を1日~数日おきの頻度で更新をしています。
平日の巡回頻度は少なめに、休日、特に連休日の更新頻度は多めにする等の、調整を行うことで、できるだけ長期間の空室状況を取得できるようにしています。
また、楽天トラベルには一人宿泊が可能な宿泊施設が1万~2万件ほど登録されています(事前に1回クロールして調べた)。
これを踏まえると、空室状況を各施設に設定されているプラン毎に保存する場合、テーブルのレコード数は100万以上になる可能性があります。
宿泊施設データ(約1万~2万)はともかく、空室状況データの100万レコードを1つのテーブルに格納するのは多少サーバにお金をかけないと厳しいかもと思ったので、空室状況テーブルのデータはパーティショニングした方がよさそうです。
開発言語、利用技術の選定
要件が固まってきたので、言語は何にするか、DBはどうするかということの検討に移ります。
- DBの選定
- 以下の理由でMySQLは除外
- シーケンシャルな検索はMySQLの方が性能面では優秀らしいが、特に性能を意識した場合に集計クエリーのSQLが書きづらい
- with句が少し前まで使えなかった。
- 配列が使えない
- 等
- シーケンシャルな検索はMySQLの方が性能面では優秀らしいが、特に性能を意識した場合に集計クエリーのSQLが書きづらい
- ElasticSearchは、低スぺサーバーでアプリと一緒に動かすのは多分無謀すぎるのでこれも除外
- AmazonElasticsearchServiceとか別で建てるのは、お金を極力使いたくない身としては無しで
- 割と消去法ですが、DBはpostgreSQLに決定
- もともとバージョンは9.6か10系にする予定でしたが、上で少し書いた通り、hashパーティショニングの機能が欲しかったため11に決定
- PostgreSQL11の難点が1つ。Windowsから安定してつながるクライアントアプリがない。
- pgAdmin3が完全サポート外(一応つながるけど、エラーがバンバン出る)。
- 今は、VisualStudio codeのプラグイン[ms-ossdata.vscode-postgresql] を利用。
- テーブル一覧などの機能はないが、クエリー投げて結果をテーブル表示する程度の用途であれば使いやすい。
- 以下の理由でMySQLは除外
- 開発言語の選定
- Java
- JVMってメモリー食いそう
- 前々職の天才中国人エンジニアが作ったasta4dというフレームワークを使おうかとも思ったけど、データをキャッシュして性能を稼ぐような設計なので、低スぺサーバーで動かすにはちょっと不安
- PHP
- 現職で使ってるけど、とにかく楽。
- 人をダメにする開発言語なんじゃないかって思うくらいとにかく楽。
- よく言えば、アルゴリズム以外で詰まることが何もない。
- 言語仕様とか、内包関数の仕様にFA?ってなることはたまにあるけど。。。
- Ruby
- 昔は好きだったけどRailsで嫌いになった。
- なんか、プログラムコードを書いてるっていう気になれなかったので。
- でも slim(テンプレートエンジン)は好き。
- 昔は好きだったけどRailsで嫌いになった。
- Goとか、Rustとか
- 勉強しながら作るのも楽しいのですが、今回はそれよりも早く作ってサービス公開したい
- お作法などがわかってきたらPHPから乗り換えるかも
- 最終的にはPHPを選択
- 実装開始から公開まで最短で進めたかったため
- Java
どれだけのお金がかかるか
利用規約に、「一般に公開されていないサイトでの利用は不可」との記述があるため、僕専用サービスとして、ずっとlocalhostで動かし続けるわけにはいきません。
また、収益化できているというのも、開発モチベーションとしてはとても大事なことです。
現状の収益化手段は、楽天アフィリエイトプログラムです。
運よく利用者が現れて、サイト経由で予約が成立すれば、楽天アフィリエイトプログラムにより収益が発生するようになっています。
が、現状では、需要があるか、人が来るか等、まったくわからないため、コストはできる限り抑えたいです。
とはいえ、ある程度のレスポンス速度はやっぱり必要なので、その辺のバランスを考えて、ソロ旅ねっとは今のところ以下のような構成で運用しています。
- ドメイン管理
- お名前.com
- 年間1000円程度
- お名前.com
- サーバー
- AWS
- ec2 instance
- 米国バージニア州 t3-micro 3年物Reserved instance
- 日本から接続した場合、東京リージョンよりも若干レスポンスに時間がかかるという欠点はありますが、東京リージョンよりも安いです。
- ubuntu 18.04
- 利用したいミドルウェア(Postgres11)のパッケージが用意されていなかったため、amazon linuxは候補から除外。
- Webはnginx/php7.2
- DBはpostgresql 11(RDSを立てるということはせず、1つのインスタンスにWebとDBを共存させています。理由はもちろん費用を抑えるため)
- 米国バージニア州 t3-micro 3年物Reserved instance
- この構成で3年間で140ドルほど
- ec2-instance の費用が3年間で110ドル。ほかにebsの費用が1日3セントほど。データ転送の費用は別途。
- ec2 instance
- AWS
サービス運営に直接かかった費用だけを考えれば、損益分岐点は3年で1万5千円~1万8千円程度となります。
予約1件あたりの収益を100円(楽天アフィリエイトの収益は、予約金額の1%)程度で計算しても、少なくとも送客数150人から180人は必要です。
過疎ったら確実に達成できない。過疎らなくても難易度は割とハードな気がします。
少なくとも、確定申告が必要になるくらい稼ぐのは難しそう。
自分が欲しいからという動機づけがなければ、この時点で計画倒れとなっていたと思います。
サイト名をどうするか
gitリポジトリ名をどうするか、という問題もあったため、サイト名は最初に決めました。
名前を決めるために考えたことは以下の4つです。
- 言いやすく、覚えやすいこと
- ドメインが取られていないこと
- 同名のサービスが存在しないこと
- 商標登録されていないこと
特に商標は、権利者がいた場合とんでもなく面倒くさいことになりそうな気もしますし、事前に確認するのは大事だと思います。
ちなみに登録済の商標は 特許情報プラットパット、で簡単に検索することができます。
実装開始から公開まで
gitリポジトリへの最初のcommitが2018年の12月20日、1stリリースが2019年1月27日なので、開発期間は1か月ちょっとでした。開発は、会社の昼休み、平日帰宅後にすこし、休日予定がなかった時に進めていました。
業務時間中も、どんな感じで実装していこうかっていう妄想くらいはしていたかも。
サーバ側の実装に関して
サーバ側の実装に関しては、今更困るようなことは特にありませんでした。
slim(フレームワーク)を使おうかとも思ったけど、完全に理解しているわけでもないし、ハマった時の調査とか面倒くさかったので、お手製のMVCフレームワークもどきを作成&使用しています。
なお、外部のライブラリ、モジュールを全く使っていないわけではなく、例えば、template engine として twig を利用しております。
DB接続まわりについては、PDOを直接利用することにしました。Lalavel等を組み入れてもよかったのですが、slimと同様の理由で使用していません。
画面デザイン
デザインセンスがあるわけでもなく、cssにさほど詳しいわけでもなかったため、ここはかなり試行錯誤で進めていました。
- 最低限の見た目は担保
- 初回リリースは、いかにもbootstrapで作りました的なサイト。
- ただ、12分割グリッドは使いやすいようで細かい制御はやりずらい、等、様々な理由により、初回リリース後の機能拡張、デザイン変更、などのフェーズで徐々に依存度は下がってます。
- 旧式ブラウザ対応で苦労はしたくない
- cssであまりトリッキーなこととか難しいことはやりたくない
- Firefoxとchromeで意図通りに見られて、Edge最新版でもとりあえずは不自由なく見られればOKとしました。
- 最初のリリースでは、IE11でもとりあえず見られる形にはなっていたんだけど、現在公開されているバージョンでは表示が乱れまくっています。
- 旧世代IEでの表示確認はやってません
- flexboxが神過ぎる。
- flexboxが存在しなかった時代、デザイナーはどうやって横並びのコンテンツをきれいに配置していたのか全く分からない
- cssであまりトリッキーなこととか難しいことはやりたくない
- 画面レイアウト、デザイン設計は基本だけは押さえる
- サイトのデザインは以下の5点だけ気を付けました
- 色を使いすぎない
- 最初のリリース時は結構ゴチャゴチャしてたのですが、今は3色+白+黒に抑えています。
- メインカラー(緑系)+アクセントカラー(メインカラーの反対色)+補助色(青)
- メインカラーの緑はなんとなく決めた
- カレンダー表示で、土曜日(青)、日曜日(赤)を使っていたので、それをそのままアクセントカラー、補助色として採用
- アクセントカラーとして赤色、リンクなどで利用する色として青を割り当て
- 色コードは、最初はマテリアルデザインの標準カラーを適当に選んで使用していましたが、現在は日本の伝統色 和色大辞典というサイトに載っている色から、自分の気に入った色を選んで使用しています。
- 余白を適切にとる
- これは特に説明の必要ないかもしれませんが、適切に行間を設けるだとか、各コンテンツ間に適切な余白を入れる等のことです。
- 余白の大きさは統一する
- 例えば「3つ横並びのコンテンツ」があったとき1つ目と2つ目の隙間は15px、2つ目と3つ目の隙間は18px のようなことはせず、ページ全体を通して、余白のサイズはなるべくそろえています。
- テキストの開始位置(右詰めの場合は終了位置)をそろえる
- タイトル+リード文みたいなリストがあったとき、リード文部分は16px下げる、みたいなことを僕もよくやっていたのですが、端をそろえた方が、安定した感じになり、見た目も綺麗になると思います。
- borderを使いすぎない。その代わり、コンテンツの境目は色の違いで表現する。
- 現在のデザインでもカレンダー部分や一部のボタンでborder を使っていますが、むやみに使うとゴチャゴチャしてしまうので、極力使わないようにしています。
- デザインについては、前々職のデザイン社内勉強会で聞いた「見た目最低のパワポプレゼンをいい感じに改善する手法」という題目の話がとても参考になっています。
- 大体同様の内容が、やってはいけないデザイン という書籍にとても丁寧に書かれています(勉強会の人と本の著者は全然別の人です。念のため)。
- まじめな遊び心ということで、他のサイトではあまり見かけないデザインも取り入れてみる
- せっかくのオレオレサービスなので、少し変わったこともやってみました。
- ページネーション、検索結果ヘッダ部分(〇ページ目/〇件の結果中)の部分は、画面をスクロールさせても常に表示されるようにしています。
- あまり深い意図があったわけではなく、なんとなく、他のサイトと差別化したかったから。
- 圧迫感が出ないような調整をするのは結構大変でした。
- せっかくのオレオレサービスなので、少し変わったこともやってみました。
- 課題
- スマホで見ると文字が小さい。タップ領域も小さいので操作性も悪い。
- サーチコンソールにも指摘されている。
そしてサービス公開
サービスを公開したのは、金曜日深夜(土曜日早朝近く)、仕事では絶対に選びたくない曜日&時間でした。
割とぎりぎりまでコードを書いていて、公開直前に、awsのインスタンスの購入やドメイン取得&設定を行いました。
この時点まで、Linux環境で全く動かしていなかった(Windowsのローカル環境使ってました)ため、ハマったらその日の公開はとりあえず諦めるつもりでいましたが、特にトラブルもなく、無事に公開までこぎつけることができました。
公開からこれまで
サービスの公開以降は、ユーザさんの反応やGAなどを確認しつつ、改善していくのがセオリーだと思うのですが、PVが全然ないし、自分が作りたいと思ったこともまだまだできていないので、現在は、その辺の実装もやりつつ、最低限の環境整備的なことをやっています。
機能追加、性能改善以外では、以下のようなことをやっていました。
テスト環境の準備
さすがにテスト環境も必要だと思い、本公開後に準備しました。
といっても、テスト時だけ立ち上がるinstanceなどを用意したわけではなく、本番環境の別portがテスト環境になっています。
会社で同じことやったら、方々からアホといわれること間違いなしです。
デプロイ手順の簡略化
初回リリース直後は、毎回WinSCPを立ち上げてファイルのコピーというとても残念な運用をしていました。
さすがにそれはやめましたが、今でも割と残念な運用をしています。
CI使って、テストにpassしたらデプロイするとかの仕組みを作るのが割と当たり前なんでしょうけど、個人で作ったオレオレサービスだし、いろいろ設定するのも面倒くさい、とはいえ scpで毎回アップロードするのも面倒くさいので、rsyncするだけのスクリプトを組んで、リリース時はそれを流しています。
#!/bin/bash
cp .key/solotabiproduction.pem ~/
chmod 0600 ~/solotabiproduction.pem
composer update
rsync -arvz --exclude 'composer.*' --exclude='.*' --exclude '*.bat' --exclude='environment' --exclude '*.sh' --exclude 'cache' --delete --partial --progress -e "ssh -i ~/solotabiproduction.pem" ./ ubuntu@ec2-**-**-**-**.compute-1.amazonaws.com:~/webroot/
rm ~/solotabiproduction.pem
開発環境はWindowsなので、別途以下のような deploy.batを用意し、実際のリリース時にはこちらのバッチをたたいています
(上のスクリプトでわざわざ .pemを HOMEにコピーしているのもWSL使っているため)
wsl bash ./deploy.sh
お手軽ですが、このやり方ではリリース中にサーバのソースファイルが中途半端な状態になり、更新中にサイトが500になる可能性が非常に高いです。
instanceをRoute53にぶら下げて、リリース時に向き先のインスタンスを切り替えるとか、サーバの別ディレクトリに全ファイルをアップロードし、サーバ側でリネームする、などで、ダウンタイムをなくす or 減らすことはできますが、そういうのは人がたくさん来るようになったら考えることにしています。
なお、stage.batというバッチファイルもあり、これを使えば(以下略
今やってること
- 検索結果の宿泊施設の情報を整理&地図表示
- → やった
- 都道府県よりももう少し細かい範囲での地域検索
- UIをどうするかで悩み中
- → やった
- Aタグで検索結果ページに遷移する導線が全くないので、一応それくらいは用意しようかと(SEO)
- → ちょっとやった
- バス停留所情報の追加
- → まだ → 都市部以外のデータ精度が低すぎて断念
- 宿泊プラン毎の空室状況の表示
- → まだ
- → 提供されている全プランの情報が取得できないため断念
今後やらなきゃなー、と思っていること
- スマホ対応(できればレスポンシブで)
- → やった
- 宿泊金額によるフィルタ
- やった
- ソート順
- 最初のソートキーが都道府県コードなので、全国から検索すると毎回北海道の宿が上位に出てしまう
- → まだ
- 最初のソートキーが都道府県コードなので、全国から検索すると毎回北海道の宿が上位に出てしまう
- 検索結果のフィルターとか
- → 地域の絞り込みとオプション条件の追加&削除はやった
- お気に入り機能
- → とりあえずcookieに保存する仕組みで検討中
- → やった
- twitter認証で簡易ユーザ登録的なもの用意するかも併せて検討
- → やった
サービスの内部、裏側を一切抜きにした紹介記事(はてなブログ)
一応、はてなブログにも記事書いてみました。
一人旅愛好家のための宿検索サイトを作ってみた
コピペ記事ではないです。ちゃんと完全書下ろし。
総括
- アイデア出すのはとても大変。思いついたアイデアは大切に。
- 技術的な裏付けができていて、細かい部分にあまり神経質にならなければ、サービス開発&リリースは意外と簡単
- ただし、収益的に成功する可能性のあるサービスを企画するのは大変
力尽きたので、この辺で終了です。
それでは、ごきげんよう。