プログラミング学習3か月で大学の講義をレビューするアプリを作成しました。【Next.js Prisma Supabase 】
はじめに
私は2024/8/15より、独学で学習を始めた大学3年生です。
今回はNext.js、TypeScriptなどを使い、私の大学の講義をレビューできるようなアプリを作りました。制作期間は2週間で毎日8時間近く孤独に制作したので、アプリについてアドバイスなど何かしら反応を頂けたら嬉しいです。
制作目的
- 1つ目は私の大学の学部では、CSなどの講義からデザイン、芸術、音楽など幅広い分野の講義を自分で選択して履修しなければなりません。しかし大学あるあるで、講義名だけ見ても、どんな雰囲気で何を学んでいるかを知ることが出来ないので、実際に講義を受けた人からリアルな講義のレビューが見れるアプリがあればいいなと思ったからです。
- 2つ目は自分がプログラミング学習を初めて、3か月間インプットし続け、とにかくこれまで学んできたことで、身の回りの課題解決をするアプリを作る過程でアウトプットしながら自走できる力を身に着けたかったからです。
サービス概要
ソースコード
アプリURL
ホーム
ユーザー情報がない状態で、"マイページ"や"今すぐ始める"を押すとログインを促される仕組みにしました。Googleアカウントで誰でもログインすることが出来ます。
全体的にグラフィック的なデザインやフォントにこだわって、親近感や使いやすさを重視したポップなデザインにしました。背景は大学ノートを模したデザインです。
アプリ構想段階で作ったWebデザインはFigmaで作成しました。
講義一覧ページ
このページでは、すべての講義を一覧することが出来ます。RD,D1,D2,D3,D4という5つのカテゴリーが視覚的にわかるような講義カードを作成しました。
各講義カードには講師名と何限の講義かが見えます。また講義一覧ページでは、検索機能とカテゴリーごとの絞り込み機能があります。
検索機能
検索機能では、講義名を含んだ文字を打つことで講義カードを検索することができます。
カテゴリーの絞り込み
絞り込みでは、5つのカテゴリーが入ったドロップダウンメニューを押すことで、それぞれのカテゴリーに絞り込まれた講義の一覧が表示できるようなページを作成しました。
講義詳細&レビューページ
講義一覧ページからレビューしたい講義を押すと、講義詳細&レビューページに遷移します。
このページでは、講義の詳しい内容(講師名、時間、コース、評価方法、達成目標)を確認することが出来ます。またその下には、星評価と口コミで講義をレビューできる機能を実装しました。
実際にレビューしてみる
レビュー機能には雰囲気、単位取得さ、将来性の3つを5段階評価する仕組みになっています。
また、口コミも投稿することが出来ます。下にある、レビューはあらかじめ私の友達がこの講義についてレビューしたものです。
では投稿してみます
実際に投稿すると下に誰がどんな口コミを投稿したのかが、一覧として表示されます。
今回はレビュー一覧の中に私が投稿したレビューが動的に入ってきています。
また、星の評価は雰囲気、単位取得さ、将来性の3つの平均値が入ってくる仕組みにしました。
また、講義一覧ページの星は、その講義をレビューしたユーザーの合計星評価÷レビューしたユーザーの数を表しており、相対的な講義の評価を見ることが出来ます。
マイページ
マイページでは、自分のGoogleアカウント情報が入ってきます。
またその下には、そのユーザーがレビューした講義名とレビュー内容が入ってくる仕組みにしました。
私は先ほど"色彩論"という講義をレビューしたので、その内容が入ってきています。
レビュー削除
右側のごみ箱ボタンを押すと自分がしたレビューを削除することが出来ます。
レスポンシブ対応
スマホでも機能します
技術スタック図
バージョン
Next.js:15.0.3,React:18.3.1
その他
prettier, eslint
ER図
工夫した点
PrismaとSupabaseを組み合わせたエンドポイントの作成
マイページ機能を実装する際、paramsにuserIdを含むページを作成し、そのユーザーが投稿したレビューを一覧表示できるようにしました。しかし、レビューのスキーマ定義にはlecture_nameを含んでいなく、ユーザーが何の講義をレビューしたかが分かるように以下の工夫を行いました。
- 1.Prismaを利用してデータベースから該当ユーザーのレビュー情報を取得。
- 2.取得したレビューのlectureIdを基に、Supabaseから対応するlecture_nameを取得。
- 3.PrismaとSupabaseのデータを組み合わせて、最終的なレスポンスをクライアントに返すエンドポイントを実装。
export async function GET( request:Request,{params}:{params:Promise<{userId:string}>}){
const {userId} = await params
try{
const getReview = await prisma.review.findMany({
where:{userId:userId},
});
const reviewWithLectureName = await Promise.all(//Promise.allで全ての処理が完了するまで待機し、全ての結果を配列で返す
getReview.map(async (review) =>{
if(!review.lectureId){
return {...review,lecture_name:""}
}
const {data:lecture,error} = await supabase
.from("lecture")
.select("lecture_name")
.eq("id",review.lectureId)//review.lectureIdと同じidのみ取得
if(error){
return {...review,lecture_name:""}
}
//reviewの配列の中にlecture_nameという要素を追加
return{...review,lecture_name:lecture[0].lecture_name}
})
//以下省略
絞り込み機能
上記で紹介した5つのカテゴリでの絞り込み機能ですが、実装当時は講義一覧ページの中で絞り込み機能を作る方法が思い浮かびませんでした。なので以下のような自分の実力内で実装できそうなアイデアを考えました。
- 1.カテゴリごとのエンドポイント作成
Supabaseのlectureテーブルからcategoryをparamsとして受け取り、それに応じたデータを返すエンドポイントを作成しました(category/[category]/route.ts)。そして[category]>page.tsxでカテゴリーを受け取り、そのカテゴリーの一覧表示ができる仕組みを作りを作りました。 - 2.カテゴリーボタンでの遷移
講義一覧ページにドロップダウンボタンを設置し、カテゴリを選択するとそのカテゴリのページに遷移するよう、Linkコンポーネントで各エンドポイントを囲みました。
export async function GET(
request: Request,
{ params }: { params: Promise<{ category: string }> } // Promiseでラップ
) {
try {
const { category } = await params; // await で params を解決
const { data, error } = await supabase
.from("lecture")
.select("*")
.eq("category", category);
//以下省略
ベストプラクティスではないと思いますが、考えてもわからない実装を自分の実力内で作ることが出来たのは成長が見えてうれしかったです。
ページの高速化の仕組み
このアプリを作り始めた時はSSRとCSRが何かなど、理解せず"use client"を多用していました。しかし、CSRを過度に使用することがページの表示速度やSEOに悪影響を与えることを学び、ページ構成を以下のように見直しました。
親ページと子コンポーネントの役割分担
useStateなどのクライアントサイドを使用する場合でも、親ページと子コンポーネントを明確に分離しました。親ページではSSRでfetchのデータ取得を中心に記述、子コンポーネントではCSRで動的なUIの更新を中心に記述しました。
useSWRの活用
ちょうどShincodeさんのYoutubeでuseSWRの存在を知り、useEffectの記述をuseSWRに書き換えました。これにより自動再検証、再レンダリングの負荷を軽減することができ、高速化につなげることが出来ました。
おかげでPageSpeed Insightsでよいスコアをとることが出来ました。
良くない点/改善点
講義APIの作成
このアプリは、大学の講義情報をAPIとして取得することを前提に設計していました。しかし、大学側が講義内容をAPIで提供していなかったため、別の方法を検討する必要がありました。まず、講義一覧ができる大学のサイトからスクレイピングしてそれをAPIとして使おうとしましたが、この手法はグレーゾーンなので辞めました。結局SupabaseのTable Editorに直接講義内容を手書きしました。正直エンジニア志望としてよくない手法ですが、とにかく自分の考えたアプリを作りながらアウトプットしたい想いが強かったので、その手法で行いました。
セキュリティ面
このアプリは現在、Googleアカウントを持っている人ならば誰でも講義をレビューできてしまいます。初めは私の大学専用のメールアドレスのみでログインできるような仕組みを作るために以下のような取り組みをしました。
Azureを使用したAuth認証
私の大学メールアドレスはmicrosoftのメールアドレスだったため、AzurePortalでアプリ登録し、認証機能を実装しようとしましたが、大学用アカウントには制限があり、アプリ登録はできませんでした。
個人用アカウントでの認証
次に個人用のmicrosoftアカウントでアプリ登録し、認証機能を実装しました。これで大学用アカウントでのログインを試みましたが、これも制限によりブロックされてしまいました。
この問題を解決するために、大学側に取り合うか、もっと調べて試行錯誤してみたいと思います。
機能一覧
- Googleログイン、ログアウト
- 講義一覧表示
- 検索機能
- 絞り込み機能(カテゴリー)
- 講義詳細表示
- レビュー機能(星評価、口コミ)
- レビュー情報保持
- レビュー一覧
- マイページ
- マイレビュー一覧
- レスポンシブ対応
今後追加するべき機能
- お気に入り登録
- 評価順絞り込み機能
- 大学用メールアドレス限定のログイン認証
- 他学部、他大学ごとに講義レビューができるような拡大版
感想
これまでUdemyで学習を続けて、自分で考えたアイデアを形にするのは初めなので、わからないことばかりでした。しかし制作期間の2週間でプログラミングが楽しいと感じ始め、人生で一度も机に向かったことがない私でも毎日8時間近く制作に費やすことになりました。これは、プログラミングを「学習」としてではなく、趣味の延長で「学ぶ」という感覚に変わったことが大きいと思います。
この2週間の成長速度を実感し、改めて、アウトプットの重要性に気づくことが出来ました。この制作で特に以下の点の理解を深めることが出来ました。
- 非同期処理
- エンドポイント作成
- Atomic Designの考え方
- TailwindCSS(マークアップ全般)
正直大学3年生の就活が始まっている時期なので焦りはありますが、チーム、中規模開発に興味が湧いたので、それが出来る環境に飛び込んでみようと思います。
おまけ
私がこの3ヶ月で何を学習したかは以下の記事を見てください。
Discussion