はじめに
27卒でエンジニア就職を目指している大学生です!
大学では物理を専攻しており、これまで本格的なプログラミング経験はほとんどありませんでした。
社会人になる前にある程度開発経験を積みたいと思い、現在Web開発を中心に勉強しています。
アプリを作成した目的
Next.jsとSSR・SSG・CSRといったレンダリング方法を理解することを目的に作成しました。
「サーバーでHTMLを生成する」「ビルド時に生成する」といった概念は調べれば出てくるものの、実際にコードを書いて動かさないと腑に落ちないと感じていました。
そこで、レンダリング方式を意識的に使い分けられるアプリを自分で作ることにしました。
題材として掲示板を選んだのは、一覧・フォーム・アプリ概要という異なる性質のページが自然に揃うため、SSR・SSG・CSRを使い分ける練習に適していると考えたからです。
アプリの概要
投稿の作成やコメントの追加ができるログイン機能付きの掲示板アプリです。
↓アプリのリポジトリ
主な機能
- メール認証によるログイン
- 新規会員登録
- スレッド一覧の閲覧
- スレッドの作成
- スレッド詳細の閲覧
- コメント投稿
- 自分のコメント削除
- Aboutページの表示
画面一覧
| URL | 内容 |
|---|---|
/ |
認証判定してリダイレクト(ログイン済 → /threads、未ログイン → /login) |
/signup |
新規登録画面 |
/login |
ログイン画面 |
/threads |
スレッド一覧画面 |
/threads/:id |
スレッド詳細画面(コメント表示・コメント投稿) |
/threads/new |
新規スレッド作成画面 |
/about |
アプリの概要画面 |
【ログイン画面】
【スレッド一覧画面】

全ユーザーの作成したスレッドが一覧で表示されています。
スレッドをクリックするとスレッド詳細画面へ遷移します。
【スレッド詳細画面】

スレッド詳細画面では他のユーザーとコメント機能で会話をすることができます。
また、自分のコメントは後から削除することができます。
【新規スレッド作成画面】

ヘッダーのスレッド作成をクリックすると新規スレッド作成画面へ遷移します。
ここでは、新しくスレッドを作成することができます。ただし、タイトルは30文字以内、本文は100文字以内の制限をしています。
【アプリの概要画面】

ヘッダーのAboutをクリックするとアプリの概要を閲覧できます。
使用技術
- Next.js
- TypeScript
- Supabase
- Authentication
- Database
- React Hook Form
- Vitest
- React Testing Library
- ESLint
- Tailwind CSS
- GitHub Actions(CI/CD)
DB設計
threads テーブル
| カラム名 | 型 | 説明 |
|---|---|---|
| content | string | スレッド本文 |
| created_at | string | 作成日時 |
| id | string | 主キー |
| title | string | スレッドタイトル |
| updated_at | string | 更新日時 |
| user_id | string | ユーザーID |
comments テーブル
| カラム名 | 型 | 説明 |
|---|---|---|
| content | string | コメント内容 |
| created_at | string | 作成日時 |
| id | string | 主キー |
| thread_id | string | threads テーブルの外部キー |
| user_id | string | ユーザーID |
profiles テーブル
| カラム名 | 型 | 説明 |
|---|---|---|
| avatar_url | string | null | アバター画像URL |
| created_at | string | 作成日時 |
| id | string | 主キー |
| name | string | null | 表示名 |
レンダリング戦略
このアプリでは、SEOと初回表示速度の観点から内容を表示するページは SSR を基本とし、入力や認証のようにユーザー操作が中心のページは CSR を使い分けています。
また、認証状態に応じた遷移制御はレイアウトとルートページで行っています。
| ページ | 方式 | 理由 |
|---|---|---|
/about |
SSG | 内容が固定で、毎回サーバーで再計算する必要がないため |
/threads |
SSR | スレッド一覧をサーバー側で取得し、最新の内容を表示するため |
/threads/[id] |
SSR | コメントを含むスレッド詳細をサーバー側で取得し、最新状態を表示するため |
/threads/new |
CSR | フォーム入力と送信処理など、動的なユーザー操作が中心のため |
/login |
CSR | 認証フォームの入力・送信処理が必要なため |
/signup |
CSR | 新規登録フォームの入力・送信処理が必要なため |
学び・気づき
CSRとSSRの違い
挙動の違い
CSRはブラウザ側でHTMLを生成するのに対し、SSRはサーバー側で完成したHTMLを生成してからクライアントに渡します。完成したHTMLが最初から届く分、SSRの方がSEOに強いという特徴があります。
実装の違い
CSRではuseEffect内でデータを取得し、useStateで保持しながら実装します。一方SSRではフックを使わずasync/awaitで直接fetchして表示するだけです。ただしuseStateが使えないため、コメントの追加や削除をしても画面が自動で更新されません。そのためredirectを使ってページを再レンダリングする必要がありました。
テストの違い
CSRは従来のReactのテストと同様にrenderしてテストできます。SSRはコンポーネントがasync関数になるため、Promiseを解決してからrenderする必要があり、少し書き方が変わりました。
Providerパターンの理解
今回、認証情報の管理にProviderパターンを使って実装しました。以前は認証情報が必要なコンポーネントごとにgetUserを呼んでstateで管理していたため、コードが冗長になっていました。Providerにまとめることで、どのコンポーネントからでも認証情報を取得できるようになり、実装が大幅にシンプルになりました。
コロケーション
Next.jsでテストファイルをどこに置くべきか迷って調べたところ、コロケーションという考え方を知りました。「関係するファイルは使う場所の近くに置く」という思想で、page.tsxの隣にpage.test.tsxを置くのがその一例です。データ取得の設計にも同じ考え方が使われており、今後の開発でも意識していきたいと思います。
大変だったこと
サーバーコンポーネントとクライアントコンポーネントの使い分け
どのページをサーバーコンポーネントにして、どのページをクライアントコンポーネントにするかはなんとなく理解できてきました。一方で、Supabaseクライアントをどちらで使うべきかは最後まで悩みました。
クライアントコンポーネントからSupabaseを直接呼ぶのか、api route経由でfetchしてサーバー側でSupabaseクライアントを呼ぶのかが判断できず、実装方針が定まらない場面が何度かありました。またユーザー情報をリクエストボディで渡すのか、JWTで渡すのかといった認証まわりの設計も理解が浅いまま進めてしまった部分があります。この辺りは今後深掘りしていきたいです。
設計とエラーハンドリング
とりあえず動くコードは書けるようになってきましたが、きれいな設計ができているかというと自信がありません。エラーハンドリングについても、何をどこまでやればいいのかの基準がまだわかっておらず、現状は最低限の実装にとどまっています。
テスト
テストが通るようにコードを書いてしまっている感覚があり、本当に品質が担保されているのか怪しいと感じています。「何を検証するためのテストか」を意識できていない部分があったので、テストの目的から改めて学び直したいと思っています。
今後の展望
技術的な学習
JWT・認証
認証まわりの実装に迷う場面が多かったため、まずはJWTの仕組みから調べてみます。
設計・エラーハンドリング
とりあえず動くコードになってしまったので、今後作成するアプリでは設計原則やエラーハンドリングの手法を意識的に取り入れていきます。特に以下のキーワードを軸に学んでいく予定です。
Error境界 error.tsx Result型 早期リターン HTTPステータスコード ログ設計
テスト戦略
テストが通るように書いてしまっている感覚があったので、Testing TrophyとTDDについて調べ、何のためにテストを書くかを整理します。
このアプリの改善
エラーハンドリングの追加
現状はエラーが起きても何も表示されない箇所があるため、ユーザーに適切なフィードバックを返せるよう改善します。
UXの改善
ローディング完了から画面遷移完了までに時差が生じている問題と、コメント削除後の反映が遅い問題があるので解消しようと思います。
おわりに
JISOUに入る前にNext.jsを一度学んだことはあったのですが、あの時と見えてるものが全然違います。(特にルーティングやレンダリング方法)
Reactをある程度理解してからでないと、どこまでがReactで、どこからがNextの機能なのかわからず、詰まったところを検索するのも大変だったのではないかと思います。
ここまでReactとNextを勉強してみて、アウトプットの仕方や新しい技術の身につけ方をだんだん理解してきたので、今後も引き続き勉強頑張っていきたいです!
参考記事
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼
