Higu`s diary

新米データサイエンティストのブログ。技術についてゆるく書きます〜

【個人開発】Railsで近くのラーメンを1タップで探せるiOSアプリ「ちかめん」のAPIを作った話

まえがき

近くのラーメンを1タップで探せるiOSアプリ「ちかめん」を作っています💪
まだまだ友人:iOS, 自分:バックエンドと2人で協力して制作中ですが
一旦、本記事では自分の担当であるバックエンド側のAPI開発備忘録を紹介します。
個人開発をしてみたいと思っている方の参考になればと思います!

想定している読者

  • プログラミングはやったことあるけどWeb開発はやったことない人
  • これからWebアプリケーション作成に挑戦してみたい人

どんなAPI

下記のようにデータを返します(Gifクリックで拡大します)

Image from Gyazo

URLは下記のような構成になっています。

           Prefix Verb    URI Pattern                                                                              Controller#Action
        api_v1_shops_near GET    /api/v1/shops/near(.:format)                                                             api/v1/shops#sort_by_near
      api_v1_shop_reviews GET    /api/v1/shops/:shop_id/reviews(.:format)                                                 api/v1/reviews#index
                          POST   /api/v1/shops/:shop_id/reviews(.:format)                                                 api/v1/reviews#create
       api_v1_shop_review GET    /api/v1/shops/:shop_id/reviews/:id(.:format)                                             api/v1/reviews#show
                          PATCH  /api/v1/shops/:shop_id/reviews/:id(.:format)                                             api/v1/reviews#update
                          PUT    /api/v1/shops/:shop_id/reviews/:id(.:format)                                             api/v1/reviews#update
                          DELETE /api/v1/shops/:shop_id/reviews/:id(.:format)                                             api/v1/reviews#destroy
    api_v1_shop_addresses GET    /api/v1/shops/:shop_id/addresses(.:format)                                               api/v1/addresses#index
                          POST   /api/v1/shops/:shop_id/addresses(.:format)                                               api/v1/addresses#create
      api_v1_shop_address GET    /api/v1/shops/:shop_id/addresses/:id(.:format)                                           api/v1/addresses#show
                          PATCH  /api/v1/shops/:shop_id/addresses/:id(.:format)                                           api/v1/addresses#update
                          PUT    /api/v1/shops/:shop_id/addresses/:id(.:format)                                           api/v1/addresses#update
                          DELETE /api/v1/shops/:shop_id/addresses/:id(.:format)                                           api/v1/addresses#destroy
       api_v1_shop_photos GET    /api/v1/shops/:shop_id/photos(.:format)                                                  api/v1/photos#index
                          POST   /api/v1/shops/:shop_id/photos(.:format)                                                  api/v1/photos#create
        api_v1_shop_photo GET    /api/v1/shops/:shop_id/photos/:id(.:format)                                              api/v1/photos#show
                          PATCH  /api/v1/shops/:shop_id/photos/:id(.:format)                                              api/v1/photos#update
                          PUT    /api/v1/shops/:shop_id/photos/:id(.:format)                                              api/v1/photos#update
                          DELETE /api/v1/shops/:shop_id/photos/:id(.:format)                                              api/v1/photos#destroy
             api_v1_shops GET    /api/v1/shops(.:format)                                                                  api/v1/shops#index
                          POST   /api/v1/shops(.:format)                                                                  api/v1/shops#create
              api_v1_shop GET    /api/v1/shops/:id(.:format)                                                              api/v1/shops#show
                          PATCH  /api/v1/shops/:id(.:format)                                                              api/v1/shops#update
                          PUT    /api/v1/shops/:id(.:format)                                                              api/v1/shops#update
                          DELETE /api/v1/shops/:id(.:format)                                                              api/v1/shops#destroy

主なURLと機能はこんな感じです

  • 緯度経度を入力すると近くのラーメンデータをDBに保存して返す near URL
  • nearで保存したリソースを返すshop, reviews , photos , addresses URL
  • 過去にアクセスされた緯度経度を保存し、再び近隣でアクセスした場合はDBからキャッシュを返す

データ取得はGoogle Map APIから行っています。
このAPIは月々2万円分は無料で使用できるため、必要なデータだけ取得し、
再度必要になる場合は保存したデータから返すような機構にして、リクエスト数を抑えています。

なんで作ったか

RailsやWebアプリケーションの基礎を身につけ、社内でのコミニュケーションを取りやすくするためです。
自分はWeb企業にデータサイエンティストとして入社したのですが、Webアプリの基礎知識がないと、 MLモデル導入のインタフェースなど業務上で齟齬が生まれると感じたからです。
また、社内の勉強会や雑談はWebの知識を前提としていることが多く、そこで話が通じるようになりたいと思っていました。
具体的には下記のような知識を得られると思い作成しました。

  • RailsなどのWebフレームワークの使い方
  • Webアプリケーション開発の全体像
  • GCP, AWSなどのクラウドの使い方
  • チーム開発におけるコミニュケーション方法(Pull Request, issueの作り方等)

どんな実装になってるか

(雑ですが)全体像を示します。
f:id:zerebom:20210602083135p:plain

サーバーをAWSにアップロードしており、URLにアクセスすると必要に応じてGCPまたはDBからデータを取得するようになっています。

DBには下記のような構成でデータが格納されており、上に載せたようなURLでアクセスできます。 f:id:zerebom:20210531085624p:plain

必要なデータはGoogle MAP APIから取得して、これをDBに整形してから格納しています。 このAPIは月2万円分までは無料で使えるため、その限度を超えないようにデータを保存しています。
サーバーの構成などは殆ど下記のURLを参考に作成しました。

作成の流れ

2020/11/10から作成しはじめました。
自分達と友人が使えるアプリがいいねということで、何個か案をだし、つくば市(当時住んでいた)のラーメンを探せるアプリ「つくめん」を作ることにしました。 最初にAPIのURL設計とデータの形式を決め、モックを作ってそれぞれ個別に作業を進めました。
お互い引っ越すタイミングで、「来年つくばにいなくない?」ということに気付き、現在地から近辺のデータを取得する「ちかめん」に変更しました。

実働は30~50時間くらいで、コミットログからどの時期にどれくらい作業していたかがわかります。 f:id:zerebom:20210602075333p:plain

一通り実装が終わったので友人に共有したところ、データが意図しない型、重複、nullになっていたりと穴だらけだったので、
一旦はもっとシンプルな「つくめん」としてアプリをリリースしようと現在作業を進めています。
ちかめんのリリースは少し時間がかかりそうなので、今までやったことを忘れないようにブログを書いた次第です。

どうやって知識をキャッチアップしたか

Railsは日本語で無料の良質な情報がWeb上にたくさんあるので、知りたい情報がなくて困ることはなかったです。

  • Rails tutorialを雑にやる
  • Railsリファレンスを使いながら調べながらすすめる
  • 必要に応じて書籍も確認する

という感じで進めました。
自分のような初心者の方がコードを書く場合、良質な教材がある・気軽に聞ける人がいる言語で書き始めるとよいのかなーと思いました。

得られた知識

得られた知識はこんな感じです。

  • Rails, Rubyの基本文法とそれぞれの強み
  • RSpecを使ったTDD開発
  • クラウド上にサーバーをデプロイするノウハウ

また、自分がバックエンドエンジニアとして働くには以下の事を更に勉強する必要がありそうだと実感できました。

  • 複雑性を避ける設計
  • DB・サーバー間の分離などのインフラ構成・通信
  • データの信頼度の担保
  • デバッグ・追加検証しやすいログの設計

得られた体験

機能追加を話すのは楽しい。実装は大変。

なんてことない機能も、実際に動くものを作るのは想像より遥かに大変でした。
飽きないように、そしてちゃんとユーザーに使ってもらえるように、届けたい価値はなにかを考えてMVPで実装することが大事だと感じました。
このあたりをちゃんと考えると実務のプロダクト開発にも活かせると思うので次作るときは、 下記の本とか参考に意識したいです。🤲

フレームワークの恩恵を得られる構成で作るとラク

RailsActiveRecordにより、基本的なCRUD機能やルーティングを少量のコードで実装することが出来ます。
フレームワークの特性を知り、自分たちが提供したいアプリの機能をそこにマッピングしていくとコスパよく実装できると感じました。

リリースできる品質にするのは難しい

GCPからデータを取ってきてDBに入れるだけでも、データの重複、欠損値などをvalidateするのに苦労しました。
またデプロイ時には開発環境の差異で落ちたり、ネットワークの知識が足りずポートが開いていなかったりといろいろ大変でした汗
事業に成り立たせるにはサービスを落とさないようにしたり、大量のデータをさばいたりと更に考えることがいっぱいなのだなと実感し、世の中のエンジニアに敬意を払いたいと思いました...笑

お世話になったサイト・書籍

railstutorial.jp

prog-8.com

railsguides.jp

qiita.com

終わり

やっぱり動くものができあがるのはすごく楽しいと感じました。
これからWeb開発をしたい!という人の参考になったらうれしいです!
アプリをリリースできたらまた記事を書きたいと思います! では〜

google-site-verification: google1c6f931fc8723fac.html