132
96

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【個人開発/未経験】エモさたっぷりプロフィール帳アプリ開発してみた【Next.js + Rails API】

Last updated at Posted at 2023-08-16

はじめに

はじめまして、ミツと申します!
突然ですが、皆様、プロフィール帳ってご存知でしょうか!?
私が小学生の頃(約20年前)クラス替えや席替えのタイミングで、女子のクラスメートから「これ、書いて~」と1枚、かわいい用紙がよく渡されました。その紙に沿って自分のプロフィールを書き、持ち主に返すという流れがありました。

今回このプロフィール帳をモチーフにしたエモさたっぷりのアプリを開発したのでご紹介いたします!

image.png

※私は現在プログラミング学習中の実務未経験の者であり、技術的な内容などに誤りを含む可能性があります。不適切な記述などがありましたらコメント等で教えていただけると幸いです🙇‍♂️

作成したアプリ

アプリ名: りーどみー

top_ogp.png

サービスURL: https://readmeee.vercel.app
GitHub URL: https://github.com/mitsu30/Read_me

開発の背景

私が通っているプログラミングスクールでは、受講生同士が交流するコミュニティ活動が活発です。
プログラミング学習を教えあったり、モチベーションが下がったときに励ましあったりなど、私自身がコミュニティの強みを存分に活かして、楽しみながら勉強を継続することができています!

しかしながら、スクールに入学したばかりの方にはコミュニティに参加することに少しハードルがあったりします。自分の体験や周りの受講生のお話から、理由としては以下のような点が挙げれられます。

  • 最初の会話のきっかけがつかむことが難しい。
  • そもそも周りにどんな人がいるのか知らない。

せっかくコミュニティがあるのに活用できていないのはもったいない...!
そんな気持ちでこちらのサービスを開発しました!

このサービスではスクールの受講生がコミュニティにスムーズに参加することを手助けすることを目的としました!

もちろんスクール受講生外の方も使っていただけます!!(*一部機能がご利用いただけません。)

サービス概要と使い方

りーどみーについてざっくりと説明いたします!

プロフィール帳作成機能

①フォームにプロフィールを入力します。
②「つくる!」のボタンを押します。
③ちょっと待ちます。
プロフィール帳の完成です!
Image from Gyazo

X(Twitter)シェア機能

完成したプロフィール帳をX(Twitter)にシェアして自己紹介しましょう!!
作成した画像はシェア時のOGP画像に反映されます!
Image from Gyazo

ユーザー機能

ログインすると、以下の追加機能を利用できます

  • アプリ内で作成したプロフィール帳の保存
  • 別のデザインのプロフィール帳の作成
  • ユーザー登録しているスクール関係者のプロフィール帳の閲覧(*一般ユーザーの方はスクール関係者が登録したアバター画像が見えなくなっています)
ユーザー一覧ページ ユーザー詳細ページ
image.png image.png

ログイン方法

新規登録の手間を減らし、スクール関係者と一般の方を自動的に判別するため、Firebase AuthとGitHub Organization所属の確認を組み合わせたGitHubログインを導入しています。

ログインについては、詳しくは後述(工夫した点 ①UI・UX)します。

image.png

工夫した点

大きく分けて下記の3点を工夫しました!
image.png

①UI・UX

初学者なりにわかりやすいUIストレスフリーなUXを目指しました。

  • プレビュー機能
    プロフィール帳の楽しさは、作成の過程にもあります。記入した内容をすぐに確認できるよう、画面遷移なしで非同期のプレビューを実装しました。もし気に入らなければ、すぐに修正し、再度プレビューを確認することが可能です!

Image from Gyazo

  • ローディング画面の実装
    「つくる!」ボタンを押してからプロフィール帳が完成するまでの間、ユーザーが待ち時間をストレスなく楽しめるようなローディング画面を設けました。
    Image from Gyazo

バックエンドで生成した画像をフロントエンドで読み込む時間と、次の画面に遷移する時間に状態を持たせています。

ローディングコンポーネント
const [isLoading, setIsLoading] = useState(false);
const [isNavigating, setIsNavigating] = useState(false);

{isLoading && <Loading>まっててね</Loading>} 
{isNavigating && <Loading>もうちょっと</Loading>} 
  • 自動転記
    プロフィール帳の記入内容が多くなると入力の手間が増えます。そこで、ログインユーザーに対して、誕生日を基にした十二星座の自動判定や、一度登録したアバター画像の自動取得を実装しました。これにより、ユーザーの手間を軽減しました。

Image from Gyazo

画像と文字の合成画像のトリミング・リサイズ画像と画像の合成にはMiniMagickを用いています。
実装の際には以下の株式会社mofmofのsugiii8さんの記事を参考にしました。

どのようなアスペクト比(画像の横幅と縦幅の比率)の画像でも適切に中心部分を切り取るように以下のように実装しました。

画像のトリミング、リサイズ
avatar_image = MiniMagick::Image.open(avatar_path)
avatar_image.combine_options do |c|
  c.gravity "Center"
  shortest_side = [avatar_image.width, avatar_image.height].min
  c.crop "#{shortest_side}x#{shortest_side}+0+0"
  c.resize "200x200"
end

さらに、複数行のテキストを画像に合成する際、フロントエンドから送信された行数に応じてテキストを配置する座標を調整しています。具体的なコードは以下のようになっています。

複数行のテキストの画像合成
lines = image_text.message.split("\n")
lines.each_with_index do |line, index|
  text_position = case lines.size
  when 1
    "0,#{418 + index * 40}"
  when 2
    "0,#{396 + index * 40}"
  when 3
    "0,#{380 + index * 40}"
  end

  image.combine_options do |c|
    c.gravity 'North'
    c.pointsize '40'
    c.font Rails.root.join('public', 'fonts', 'Yomogi.ttf')
    c.fill '#666666'
    c.draw "text #{text_position} '#{line}'"
  end
end
  • ログイン機能
    Firebase AuthとGitHub Organization所属の確認を組み合わせたGitHubログインを実装し、スクール関係者と一般の方を自動的に判別し、新規登録の手間を減らしています

Image from Gyazo

ログイン機能の実装にはFirebase Authを選択しました。その理由は以下の通りです。

  • ユーザー体験を良くしたい。
  • ユーザー登録のハードルを下げたい。
  • セキュリティの知識に不安があるため、個人情報を極力保持しないようにしたい。

Firebase Authを通して、GitHubのアカウント名を取得することができます。

GitHubのアカウント名を取得(一部抜粋)
const loginWithFirebase = async (method) => {
  const getResult = () => {
      return signInWithPopup(auth, getProvider(method));
  };

  const result = await getResult();

  if (result) {
    const user = result.user;
    const details = getAdditionalUserInfo(result);
    return { user, details };
  }
};

const userDetails = {username: details.username};

さらに、ユーザーが特定のOrganizationに所属しているかどうかを確認するために、バックエンドではOctokitを使用しています。OctokitはGitHubのAPIを簡単に使用できるRubyのライブラリです。

Organization所属の確認
def self.organization_member?(github_user_id)
  client = Octokit::Client.new(access_token: ENV['GITHUB_ACCESS_TOKEN'])
  is_member = client.organization_member?('organization_name', github_user_id)
  is_member
end

②プロフィール帳の世界観

プロフィール帳のファンシーな世界観を表現するためにアプリ内のフォント配色には細心の注意を払いました。また、ユーザーが自己紹介する内容について、個人的な情報を公開する際の心理的なハードルを考慮に入れました。後述(開発期間 ①プレリリースまで(2023年5月17日〜2023年6月1日))しますが、プレリリース時のフィードバックをもとに、ユーザーがどの程度の情報を公開するのか考察を行い、反映させました。

現在使用できるプロフィール帳のテンプレートは4つになります。(*一般ユーザーの方は上2つのみになります。)

みにまむ ベーしっく
image.png image.png
すくーる あにばーさりー
使ってます!MVP用 (1200 × 630 px) (2).png 使ってます!MVP用 (1200 × 630 px) (3).png

(*大人の事情で一部モザイクが入っています)

  • フォント
    アプリのイメージとマッチしたYomogiを採用しました!
    Googleフォントを用いて簡単に導入できます。
<Head>
  <link
    href="https://fonts.googleapis.com/css2?family=Yomogi&display=swap"
    rel="stylesheet"
  />
</Head>
  • 配色
    アプリで使用する色を主に暖色系のパステルカラーを中心に選択しました。
    カラーコードはノグチデザインさん(@n_seitan)を参考にしました。
    また、#000000(真っ黒)の使用は避けました

  • プロフィール帳作成画面
    プロフィール帳の作成画面は、本のページをイメージしてデザインしました。
    image.png

  • 内容の方針
    パーソナルな部分を不特定多数の人に公開するため、心理的なハードルがあるかと思います。そこで、自己紹介はするけれども、個人を特定するような情報は載せないように内容に注意を払いました。

③プライバシー保護

ユーザーのプライバシーを尊重するため、アプリ内ではプロフィール帳の公開範囲を設定できます。公開を選ばないユーザーのプロフィール帳は外部から見えないようになっており、さらに動的OGPの表示もオフになる設計になっています。公開範囲の設定確認はマイページからできます。

公開範囲の設定 マイページで確認
image.png image.png

ユーザーの権限に応じて、適切な公開範囲のプロフィール帳を取得するように実装しました。
(冗長となってしまいましたが...)

一部抜粋
def build_profiles_data
  user_communities = @user.membered_communities.map(&:id) 
  @showed_user.profiles.with_attached_image.select do |profile|
    profile.privacy == '****' || (profile.privacy == '****' && (user_communities & profile.open_ranges.map(&:community_id)).any?)
  end.map do |p| 
    {
      id: p.id,
      uuid: p.uuid,
      image_url: p.image.url,
    }
  end
end

公開範囲を設定しているプロフィール帳をX(Twitter)にシェアする際にはアプリ内でデフォルトにしている画像がOGP画像に設定されるように実装しています。

Image from Gyazo

ユーザーがプロフィール帳に設定している公開範囲に応じて、X(Twitter)にクロールさせる画像のURLを変更させています。

動的OGPの表示のオンオフ
let imageUrl;
if (profileImage.privacy === '****' || profileImage.privacy === '****') {
  imageUrl = alternativeImageUrl;
} else {
  imageUrl = profileImage.image_url;
}

動的OGP設定についてはスクールの先輩が丁寧にまとめており、こちらを基に勉強しました!また、OGP画像の確認をするためのサイトもあわせて載せておきます!

主な使用技術

フロントエンド

  • Next.js 12.2.3
  • React 18.2.0

主要なライブラリ

  • @mui/icons-material & @mui/material 5.11.16 & 5.13.1
  • @emotion/react & @emotion/styled 11.11.0
  • axios 1.4.0
  • next-seo 6.0.0
  • nookies 2.5.2
  • notistack 3.0.1
  • react-paginate 8.2.0

技術選定理由

  • Next.js
    ストレスのないUXを目指し、サクサク応答するUIを実現したかったため、Next.jsを選択しました。また、メイン機能である非同期のプレビュー機能を実装できる、かつ、X(Twitter)シェア機能において、作成したプロフィール帳を動的にOGP画像に反映させるためにサーバーサイドレンダリング(SSR)をサポートしているNext.jsが最適かなと考えました。

  • MUI
    アプリの設計段階で、フロントの世界観が重要となってくると考えていました。そのため、豊富なデザインコンポーネントを提供し、比較的手軽に実装できそうかつ、カスタマイズの自由度が高そうなMUIを採用しました。

バックエンド

  • Rails(APIモード) 6.1.7
  • ruby 3.2.2
  • PostgreSQL

主要なgem

  • minimagick 4.12.0
  • rack-cors 2.0.1
  • jwt 2.7.0
  • aws-sdk-s3 1.122.0
  • octokit 6.1.1
  • kaminari 1.2.2

技術選定理由

  • Rails
    私が通っているプログラミングスクールでは、カリキュラムはRailsがメインとなっています。初心者ながらに使い慣れており、実装スピードを上げられると考え、採用しました。

  • PostgreSQL
    後述するFly.ioで無料で使用できたため、採用しました。

インフラ・その他

  • インフラ

    • Vercel
    • Fly.io
    • Amazon S3
  • その他

    • Firebase Authentication
    • GitHub API
    • cron-job.org

技術選定理由

  • Vercel
    Next.jsの開発者たちが作ったプラットフォームであり、Next.jsとの相性がいいと考え、採用しました。GitHubとの連携がとてもシンプルであり、pushしただけで自動的にデプロイされる点も初学者には易しかったです。ドキュメントもわかりやすく、トラブルが発生した際もスムーズに解決できました。

  • Fly.io
    無料で利用できるため採用しました。また、ドキュメントを確認すると手軽なコマンドが多数準備されており、初学者にもわかりやすかった点も理由の一つです。

  • Amazon S3
    Active Storageがデフォルトでサポートしている点と無料枠が大きかったので採用しました。

Fly.ioのスリープモードについて

  • cron-job.org
    Fly.ioの無料プランでは短時間でスリープモードに入ってしまうので下記のサイトからバックエンドにリクエストを送っています。JSONを返すだけのメソッドを設定しています。
 def wakeup
   render json: { message: 'Server is awake' }
 end

ER図

ER図は以下の通りです。
image.png

開発期間

開発を始める前に以下の技術のキャッチアップをそれぞれ1週間ずつ合計で約3週間行いました。

  • JavaScript   
  • React
  • Next.jsとフロントエンドとバックエンドの連携

アプリ全体の作成期間は約1ヶ月半、内訳としてはスクール内でのプレリリースまでが約2週間、そこから大型アップデートまでが約1ヶ月となっています。

①プレリリースまで(2023年5月17日〜2023年6月1日)

現場実装している「おためし」の機能(ユーザーと紐づかないプロフィール帳の作成とX(Twitter)シェア機能)のみを実装してX(Twitter)上で公開しました。

image.png

プレリリースまでで意識していたことは、最低限の機能のみを実装して早く人様に出せる形にすることでした。プレリリースを急いだ理由は以下です。

  • UI・UXに関してユーザーのフィードバックが欲しかったから。
    設計の段階からフロントの世界観が大事になってくるアプリと考えていました。自分なりには工夫しましたが、プロフィール帳の世界観を表現できているのか? 自己満足なUI・UXになっていないか? といったことがわからなかったたため、ユーザーの声を早めに確かめたい意図がありました。不評だったら実装の方向性を変えようと考えていました。幸いなことにプレリリース時のアンケート(以下の円グラフ)でたくさんの好評をいただき、方向性は変えずに実装を進めることにしました!
    特にデザインがかわいいという声をたくさんいただけて嬉しかったです!

image.png

  • ユーザーがどの程度を情報を公開するのか確認したかったから。
    自分のプロフィールを不特定多数の人に公開するという点に心理的なハードルがあるかと思います。プレリリース時のプロフィール帳には自由記入欄を設けており、ユーザーがどの程度の情報なら公開するのか確かめたい意図がありました。プレリリース時にユーザーに記入していただいた内容を考慮に入れて、追加のテンプレートに反映させています

②大型アップデートまで(2023年6月2日〜2023年7月5日)

「おためし」機能以外の機能をこの期間に実装しました!
実装中に特に苦労したのは上述したMiniMagickを用いた画像合成ロジックです。
参考文献や公式ドキュメントを読んで、一つ一つのコードが何の役割を果たしているのかを丁寧に理解していき、実装に結びつきました。
image.png

今後のりーどみー

課題点や今後実装したい点については以下のとおりです。

1. 認証周りの機能追加
退会機能が未実装なので、ユーザーに安心して利用していただけるように対応したいと考えています。

2. テストコードの追加
現状テストコードがほとんど書けていない状態ですので、順次追加していくつもりです。

3. 新たなプロフィール帳テンプレートの追加
一度使っていただいた方にも再度楽しんでもらうためにも、検討中です。

終わりに

これまで練習アプリの類すら作成したことがなく、今回初めてサービスを1から作りましたが、本当にたくさんの学びありました!
また、実装を急いでいたため緊張感がありましたが、楽しみながら開発できました!

特に反省した点が、アプリの設計があやふやなまま開発を始めてしまった点です。プレリリースまで約2週間かかっていますが、最初の1週間は実装に迷走しており、ほとんど進捗0でした。プロフィール帳の作成を別の方法で考えていたのですが、思い描いていた実装が全くできずに詰まっていました。スクールの校長や講師の方アドバイスを元に方針転向することができました。アプリ設計の時点でもう少し詳細に技術調査をして、実装のイメージが固めておくべきでした。

本記事が少しでも皆さんの参考になれば幸いです!
最後までご覧いただき本当にありがとうございました🙇‍♂️

132
96
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
132
96

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?