はじめに
初めまして!新卒一年目のけに、後藤、塚崎です。
本記事では、私達が参加したエンジニア定例合宿で開発した飲食店に関する投稿を行うサービス「ふーどぴあ」について紹介します。
エンジニア定例合宿の詳細につきましては以下の記事をご覧ください。
チームメンバー紹介
- けに
- 今回の担当:バックエンド全般、DB構築、ログイン処理
- 一言:お金が貯まりません
- 後藤
- 今回の担当:API実装、SQL文作成
- 一言:散歩マスターになりました。
- 塚崎
- 今回の担当:フロントエンド全般、画像アップロード機能
- 一言:早朝、散歩で海に行くのが健康的で良かったです。
プロダクト紹介
概要
私たちが開発したのは、食べ物に関する投稿を行うサービスになります。
食べたいものや条件に見合う店舗を検索して探すようなサービスではなく、タイムラインに流れてきた投稿を眺めていて食べたいと思った店を見つけ、実際に訪れる。そんなサービスがあると面白いのではないかと思い、作成しました。
実装した主な機能としては以下になります。
- 各ユーザーが投稿したレビューを見ることができる
- 閲覧するだけではログインは不要
- レビューを投稿することができる
- 投稿する際はログインが必要
- 画像の投稿も可能
使い方
まずはホーム画面です。ここには、ユーザーの投稿が一覧で表示されています。
この画面から、ログインや各投稿の詳細ページへ遷移することができます。

アカウントはユーザー名とパスワードを設定することで作成できます。

ログインボタンからログインを行うことができます。

また、投稿をクリックすると各投稿の詳細を見ることができます。
※ コメントはダミーのものを入れています。

使用技術
開発言語はフロントエンドにはTypeScript、Next.jsを選択し、CSS FrameworkにはTailwind CSSとshadcn/uiを採用しました。
バックエンドはPythonを選択し、FrameworkにはFast APIを採用しました。
ユーザー情報や投稿内容を保存するDBにはPostgreSQLを採用し、写真データを保存するストレージにはMinIOを利用しました。
その他に使った技術スタックは以下になります。
開発環境 | コード管理 | Git / GitHub |
---|---|---|
コンテナ環境 | Docker | |
フロントエンド | 言語 | TypeScript |
Framework | Next.js | |
CSS Framework | Tailwind CSS shadcn/ui | |
Linter / Formatter | Prettier | |
Package Manager | pnpm | |
バックエンド | 言語 | Python |
Framework | FastAPI | |
Linter / Formatter | ruff | |
Package Manager | uv | |
DB | PostgreSQL | |
オブジェクトストレージ | MinIO |
開発の流れ
0日目:事前準備
- アイデアソン
- 実装概要検討
- DB設計
1日目:環境構築、実装
- 開発環境構築
- 投稿一覧の表示画面、APIの実装
2日目:実装
- 午前
- 投稿機能系をメインに実装しつつ、ユーザー管理機能も実装
- 午後
- 画像投稿処理の実装
3日目:最終調整・成果発表
- バグ修正
- コメント投稿APIの実装
- 成果発表資料作成
工夫したこと
役割分担を明確にしたこと
私たちのチームではフロントエンドとバックエンドを分離して開発を行いました。
完全に分離することでそれぞれが得意な領域を担当することができ、効率良く開発を進めることができました。また、開発を進めていてコンフリクトが発生することもなかったので、そこもフロントエンドとバックエンドを分離する大きなメリットだと感じました。
フロントエンド
今回はフロントエンドの実装をするにあたって、Tailwind CSSやshadcn/uiなどのCSSフレームワークを採用しました。一からCSSを書いてスタイリングするとなるとかなり時間が掛かってしまいますが、今回はCSSフレームワークを用いることで短時間でUIを構築することができました。特にTailwind CSSはCSSのクラス名を考える時間が不要になるので、こういったハッカソンにおいては最適なフレームワークだったなと感じました。
バックエンド
今回はレイヤードアーキテクチャを採用しました。レイヤー毎に責務を分離したおかげで、細かい仕様変更やCRUD系機能の追加の際にスムーズに実装を行うことができました。
また今回はFast APIを採用していたこともあり、レスポンススキーマなどのバリデーションをpydanticを用いて入念に行いました。そのおかげで、レイヤー間のデータの受け渡しやレスポンスの型で戸惑うことがなく実装を進めることができました。
画像のアップロード機能
今回は画像をAPI経由で送信して、オブジェクトストレージに保存するように実装しました。
一般的にはオブジェクトストレージにAWS S3を採用することが多いと思いますが、今回はシステムをローカルで動作させている都合上、Dockerを用いてローカル環境にMinIOを建て画像を保存する方針としました。
MinIOはAmazon S3とAPI互換性を持っている点、既存のS3クライアントやアプリケーションをそのまま利用できる点、ローカルで構築できる点から採用しました。

今回実装した画像アップロード機能は以下の流れになっています。
- フロント側でユーザーからアップロードされた画像データをbase64形式でエンコードし、文字列にする
- エンコードした文字列をバックエンドのAPIに送信する
- バックエンド側で受け取った文字列をデコードして画像データに戻す
- 画像データをMinIOのAPIを使って、MinIOに保存する
- MinIO側で画像にアクセスするURLが発行される

本来は画像サイズの制限、拡張子の判定などについても考慮する必要がありますが、時間制限の都合上、本開発ではこの形での実装となりました。
様々なサービスで実装されている機能のため簡単に実装できるものだと思っていましたが、実際に一から実装してみることで、その大変さを痛感しました。
学んだこと・成長したこと
良かった点
仕様策定、DB設計、API設計を事前に行った
開発が始まっていきなり実装に入るのではなく、細かいところまで事前に方針を決めてから実装に入りました。そのおかげで、DBで必要なカラムが足りなかったなどのトラブルは起きず、手戻りすることなく開発を進めることができました。
仕様変更にも柔軟に対応
仕様の検討段階では、AWS ECS上で動作させる予定で、WebSocketなど双方向通信を利用したシステムの開発を行う予定でした。しかし、実際に開発を行っていく中で進捗と期限を勘案すると間に合わないのではないかという話になり、ローカルで動作するものを完成させることに専念しました。実際にAWS上での動作を継続していたら完成が間に合わなかった可能性があったので、切り替える判断がすぐにできたのは良かったと思います。
また画像アップロード機能を開発するにあたって、画像ファイルの置き場所を検討した際に認証関係のリソースの作成を行うよりも、MinIOを利用してローカルにストレージを建てる方針を取りました。画像保存の方法については事前に話し合えていなかったのですが、開発の中で話し合って方針を修正することができました。
よくなかった点
実装コストの見積もりが甘かった
設計の段階ではコメント機能や投稿検索機能なども実装する予定でしたが、想定よりも時間が足りず、実装できなかった機能が多数ありました。例えば、コメント機能ではAPIの実装はできましたが、フロントの実装が間に合わなかったというケースがありました。
また、実装することを優先して急いでしまったためにレビューの時間が全くとれませんでした。
ログイン認証や画像アップロードなど想定より時間がかかってしまった機能などもあったため、事前の設計段階でもう少し作業時間を考慮できればよかったなと思いました。
開発の優先度
例えば、ログイン認証系の機能については、事前に機能として入れる前提のDB設計をしていました。しかし実際にログイン機能を実装するとなると、今までの経験上ある程度コストがかかってしまうなと感じていました。
ログイン機能は必須とはせずに匿名でも投稿できるような仕様にしておけば、その時間を別の実装にあてることもできたので、開発の優先度を考慮不足だったと感じました。実際に最終日にコメント機能のAPIは完成していたので、時間があればフロントからの投稿処理も可能でした。
今回の経験を通して、短い期間では開発コストを考慮して優先度を決めていくのはやはり重要だと感じました。
まとめ
今回のエンジニア定例合宿では、研修やOJTで培った知識と経験を活かし、効率的な開発を進めることができました。事前の仕様検討や明確な役割分担はいい方向に働く結果となりました。
また、限られた時間の中でタスクの管理や優先順位付けを行うなど、今後の業務でも発生しうる課題に直面しました。これらの一端をハッカソンで感じることができたのは今後の業務でも生きるいい経験になりました。
今回の合宿を通じて各自発見や課題が浮かび上がったと思うので、この経験を糧に更なる成長を目指していきます。