ポートフォリオ作成

# 【ポートフォリオ開発】完全ガイド - 土台づくりから実装まで

## はじめに

ポートフォリオ開発は、エンジニアとしてのスキルを証明する重要な手段です。
特に私は現在文系の大学4回生ということもあり、CS専攻ではないのでアピール材料としてPFが必要でした。
今回は私自身のポートフォリオ開発経験から、特に重要だと感じた「土台づくり」から実装までの全工程について共有します。



## ポートフォリオ開発の全体フロー

ポートフォリオ開発は以下の順序で進めることが最も効率的です：

1. **アイデア出し**
2. **機能洗い出し**
3. **ワイヤーフレーム作成**
4. **テーブル定義書作成**
5. **ER図作成**
6. **実装**

それぞれの段階について詳しく解説していきます。

## 1. アイデア出し - モチベーション維持の鍵

### なぜアイデア出しが重要なのか？

まず最初に行ったのが、**アイデア出し**です。こちらでどのようなアプリを作成していくかについて考えました。大事なのは**モチベーションを保ちながら開発を進めていかなければならない**ので、自分のモチベーションが上がるアイデアを採用することが大切だと思います。

私は野球部だったという経験から**野球に関連するアプリ**を作成していこうと考えました。

### アイデア選定のポイント

- **自分の興味・経験がある分野**を選ぶ
- **実際に使いたい**と思えるものにする
- **技術的な学習目標**と合致させる

## 2. 機能洗い出し - バージョン管理が成功の鍵

### 機能の優先順位付け

アイデアを出した後は、**機能洗い出し**の段階です。こちらではどのような機能が必要になってくるかについて考えます。

:::note warn
機能をたくさん付けすぎるとポートフォリオのレベルを超えてしまい、作成にかなりの時間がかかってしまうので注意が必要になります。（あくまでポートフォリオということを忘れずに！）
:::

付けたい機能は後からでも作成することができるので、**ver1として最低限の機能**を決めて、後から搭載予定の機能は**ver2**にしたりして区別しましょう！

### 機能管理表

以下が私が実際に作成した機能管理表です：

| 機能名 | バージョン | 完了 |
|:-------|:-----------|:----:|
| ユーザー新規登録 | ver.1 | ☑️ |
| ユーザー機能（登録・表示・編集・削除） | ver.1 | ☑️ |
| ログイン | ver.1 | ☑️ |
| ログアウト | ver.1 | ☑️ |
| プロフィール（登録・表示・編集） | ver.1 | ☑️ |
| パスワードリセット | ver.1 | ☑️ |
| 必須トレーニング追加（指導者側でのみ使用可能） | ver.1 | ☑️ |
| 日々の練習記録機能（ノート機能） | ver.1 | ☑️ |
| Difyを使用した機能 | ver.1 | ☑️ |
| コメント機能（ノート内） | ver.2 | - |
| チーム全体のチャット機能 | ver.2 | - |
| 通知機能 | ver.2 | - |
| 試合結果登録機能 | ver.3 | - |
| Google Maps API連携 | ver.3 | - |
| データ分析・可視化機能 | ver.3 | - |

### 機能洗い出しのコツ

```
例：野球トレーニング管理アプリの場合
- ユーザー登録/ログイン機能
- プロフィール編集機能
- トレーニングメニュー作成機能（コーチ用）
- トレーニング記録機能（選手用）
- コメント機能
- トレーニング実績集計機能
```

この段階でデータの関連性も考慮し、必要なカラムや名称も検討しておくとよいでしょう。

## 3. ワイヤーフレーム作成 - 具体的なイメージ化

### Figmaを活用した設計

機能洗い出しを考えた後は、**Figmaを使用してワイヤーフレーム**を作成してみましょう！

実際の画面を作成し、どのボタンを押したらどこの画面に遷移するかについて細かく記載することで、**記載し忘れていた機能が浮かんでくる**ことが多々あります。

### ワイヤーフレーム作成のポイント

- **各画面の要素を明確にする**
- **画面遷移を矢印などで表現する**
- **入力項目や表示項目を具体的に記載する**

イメージを形にしながら機能洗い出しの見直しも含めて作成していきましょう！

この段階で「実際にアプリを操作する」イメージを具体化できると、必要なデータ構造も見えてきます。

## 4. テーブル定義書作成 - 開発の要となる設計

### テーブル定義書の重要性

ワイヤーフレームを作成し、機能洗い出しも完了したら次に**テーブル定義書の作成**に入ります。

こちらではデータベースに実際のデータを保存する場所の形を定義します。

:::note alert
こちらを疎かにしてしまうと実装に入ってから痛い目を見ることになるので、細かく繊細に進めていきことが大切だと感じました。
:::

### 学習リソースの活用

わからない場合は**QiitaやZenn**で調べながら進めていくのがおすすめです！
特に**中間テーブルが絡んでくると難しくなってくる**ので、AIを使いながら頑張りましょう。

### 実際のテーブル定義書

以下が私が作成したテーブル定義書になります：

#### usersテーブル

| カラム名 | データ型 | 制約 | 説明 |
|:---------|:---------|:-----|:-----|
| id | UUID | PRIMARY KEY, INDEX | ユーザーID |
| firebase_uid | str | UNIQUE, NOT NULL | FirebaseのUID |
| password | VARCHAR(255) | NOT NULL | パスワード |
| email | VARCHAR(255) | NOT NULL, UNIQUE | メールアドレス |
| role | TINYINT | NOT NULL, DEFAULT('user') | 0: 選手、1: 指導者 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | レコード作成日時 |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | レコード更新日時 |
| deleted_at | TIMESTAMP | NULL | レコード削除日時 |

#### profilesテーブル

| カラム名 | データ型 | 制約 | 説明 |
|:---------|:---------|:-----|:-----|
| id | UUID | PRIMARY KEY | プロフィールID |
| user_id | UUID | FOREIGN KEY(users.id), NOT NULL | usersテーブルと紐づけ |
| name | VARCHAR(255) | NOT NULL | 名前 |
| team_name | VARCHAR(255) | NOT NULL | チーム名 |
| birthday | DATETIME | NOT NULL | 生年月日 |
| player_dominant | VARCHAR(20) | NOT NULL, CHECK制約 | 利き手（右投げ左打ちなど） |
| player_position | VARCHAR(20) | NOT NULL, CHECK制約 | ポジション |
| admired_player | VARCHAR(255) | NULL | 憧れの選手 |
| introduction | TEXT | NULL | 自己紹介 |
| image_path | STR | NULL | プロフィール画像 |

#### notesテーブル

| カラム名 | データ型 | 制約 | 説明 |
|:---------|:---------|:-----|:-----|
| id | UUID | PRIMARY KEY | ノートID |
| user_id | UUID | FOREIGN KEY(users.id), NOT NULL | usersテーブルと紐づけ |
| theme | VARCHAR(255) | NOT NULL | テーマ |
| assignment | TEXT | NOT NULL | 課題 |
| practice_video | VARCHAR(255) | NULL | 練習参考動画 |
| my_video | VARCHAR(255) | NULL | 自分の動画 |
| weight | DECIMAL(4, 1) | NOT NULL | 体重 |
| sleep | DECIMAL(3, 1) | NOT NULL | 睡眠時間 |
| looked_day | TEXT | NOT NULL | 1日の振り返り |
| practice | TEXT | NULL | その他練習 |

#### trainingsテーブル

| カラム名 | データ型 | 制約 | 説明 |
|:---------|:---------|:-----|:-----|
| id | UUID | PRIMARY KEY | トレーニングID |
| user_id | UUID | FOREIGN KEY(users.id), NOT NULL | usersテーブルと紐づけ |
| menu | TEXT | NOT NULL | トレーニングメニュー |

#### commentsテーブル

| カラム名 | データ型 | 制約 | 説明 |
|:---------|:---------|:-----|:-----|
| id | UUID | PRIMARY KEY | コメントID |
| user_id | UUID | FOREIGN KEY(users.id), NOT NULL | usersテーブルと紐づけ |
| note_id | UUID | FOREIGN KEY(notes.id), NOT NULL | notesテーブルと紐付け |
| content | TEXT | NOT NULL | コメント内容 |

#### training_notesテーブル（中間テーブル）

| カラム名 | データ型 | 制約 | 説明 |
|:---------|:---------|:-----|:-----|
| id | UUID | PRIMARY KEY, INDEX | ID（中間テーブル） |
| training_id | UUID | FOREIGN KEY(trainings.id), NOT NULL | training_idと紐付け |
| note_id | UUID | FOREIGN KEY(notes.id), NOT NULL | note_idと紐付け |
| count | int | NOT NULL | 回数 |

## 中間テーブルの重要性

### なぜ中間テーブルが必要なのか？

データベース設計において、多対多の関係を適切に管理することは非常に重要です。特に複数のエンティティ間の関連データを扱うアプリケーションでは、中間テーブルが必須となります。

今回は私が開発している野球トレーニング管理アプリを例に、中間テーブル`training_notes`の必要性とその役割について解説します。

#### 当初の設計の問題点

当初、私はトレーニングの実績データを直接`trainings`テーブルに保存しようと考えていました：

```sql
CREATE TABLE trainings (
  id UUID PRIMARY KEY,
  user_id UUID NOT NULL,
  menu TEXT NOT NULL,
  count int NULL, -- ここに回数を記録
  -- 他のカラム
);
```

しかし、この設計には以下の問題がありました：

:::note warn
**問題点1: データ入力のタイミングと責任者が異なる**
- `menu`: コーチが作成時に入力
- `count`: 選手が実施後に入力

これにより、レコード作成時に`count`が空になってしまいます。
:::

:::note warn
**問題点2: 同じメニューを複数選手が実施する場合の重複**
例えば「腹筋」というメニューを複数の選手が行う場合、同じメニュー情報が選手の数だけ重複して保存されてしまいます。
:::

:::note warn
**問題点3: 履歴管理ができない**
同じ選手が同じメニューを日付違いで行った場合の記録ができません。
:::

#### 中間テーブルによる解決

これらの問題を解決するために、`training_notes`という中間テーブルを導入しました。

:::note info
**メリット1: 責任の分離**
- コーチは`trainings`テーブルにメニューのみを登録
- 選手は`notes`テーブルに日々の記録を作成
- 実績データ（回数）は`training_notes`で関連付け
:::

:::note info
**メリット2: データの正規化**
- メニュー情報は`trainings`テーブルに一度だけ保存
- 実績データは`training_notes`テーブルに保存
:::

:::note info
**メリット3: 柔軟なデータ管理**
- 日付別・選手別の実績を個別に記録可能
- 同じメニューの履歴を時系列で管理可能
:::

## 5. ER図の作成 - 関係性の視覚化

テーブル定義書を作成したら、**実際のテーブル間の関係をER図に起こして**、明確にしていきましょう！

テーブル間の関係性を視覚化することにより、データの流れや参照関係が明確になります。

ここまで作成すれば、ポートフォリオの土台を作成することができましたので、後は実装を進めていくだけです。

## 6. 実装フェーズ - AIを活用した効率的な開発

### 生成AIの活用方法

私自身、実装に入って必要と感じたことは**生成AIをフル活用して進めていく**ことも大事で、かなりスムーズに進めていくことができます。

ただし、AIが作成するコードは私自身にとってはかなり複雑でレベルの高いコードだと感じました。私が未経験であるのが理由だと思いますが笑

### 実装時の心構え

私はAIに土台を作成してもらいながら、**より修正するときやバグが発生した時のために、より簡単に読むことができるコードを書く**ことを意識していました。

```javascript
// AIが生成した複雑なコード例
const processUserData = (users) => users
  .filter(user => user.status === 'active')
  .map(user => ({ ...user, fullName: `${user.firstName} ${user.lastName}` }))
  .sort((a, b) => a.createdAt - b.createdAt);

// 自分なりに理解しやすく書き直したコード例
const processUserData = (users) => {
  // アクティブなユーザーのみ抽出
  const activeUsers = users.filter(user => user.status === 'active');
  
  // フルネームを追加
  const usersWithFullName = activeUsers.map(user => {
    return {
      ...user,
      fullName: `${user.firstName} ${user.lastName}`
    };
  });
  
  // 作成日時でソート
  const sortedUsers = usersWithFullName.sort((a, b) => {
    return a.createdAt - b.createdAt;
  });
  
  return sortedUsers;
};
```

## 土台づくりのポイント

### 命名規則の重要性

テーブル名やカラム名の命名は非常に重要です。後からの変更が難しく、コード全体に影響するためです。

- 一貫性のある命名規則を決める
- 略語は最小限に
- 複数形/単数形の使い分けを統一する

### 正規化と非正規化のバランス

データベース設計では、正規化（重複の排除）と非正規化（パフォーマンス向上のための意図的な重複）のバランスが重要です。

- 基本は第3正規形まで正規化する
- 検索パフォーマンスが重要な場合は適切に非正規化を検討する
- 中間テーブルを活用して多対多関係を適切に管理する

### レビューを受ける

可能であれば、他の開発者にデータベース設計のレビューを依頼しましょう。自分では気づかない問題点を指摘してもらえることがあります。

（私は現役エンジニアの方にメンターとしてコードレビューをしていただき、修正を繰り返し行いました。）

## まとめ

ポートフォリオ開発において、土台づくりは手を抜いてはいけない最重要フェーズです。十分な時間をかけて以下のステップを確実に実施しましょう：

1. **アイデア出し** - モチベーション維持の源泉
2. **機能洗い出し** - バージョン管理による優先順位付け
3. **ワイヤーフレーム作成** - 具体的なイメージ化
4. **テーブル定義書作成** - データ構造の明確化
5. **ER図作成** - 関係性の視覚化
6. **実装** - AIを活用した効率的な開発

これらの準備が整ってからコーディングに入ることで、開発の効率が飛躍的に向上し、質の高いポートフォリオを完成させることができます。

私の経験では、土台づくりにかけた時間は決して無駄にはなりませんでした。むしろ、後の開発スピードを加速させ、修正の繰り返しを防ぐことができました。

皆さんもぜひポートフォリオ開発の際は、「見えない部分」である土台づくりに力を入れてみてください！

---

:::note info
この記事が皆さんのポートフォリオ開発の参考になれば幸いです。質問やフィードバックがあれば、コメントでお聞かせください！
:::

## 推奨タグ
`ポートフォリオ` `データベース設計` `新卒エンジニア` `就職活動` `設計` `初心者` `テーブル設計` `ER図` `野球` `個人開発`