（未経験）技術士1次試験の過去問演習アプリつくった [Rails API + Next.js]

## はじめに

はじめまして！
エンジニア転職を目指してプログラミングスクール[RUNTEQ](https://runteq.jp/)で学習中の[けーちゃん](https://twitter.com/mimi048599)と申します。
2023年1月より、**完全未経験**から**Ruby on Rails**を中心に学習し、この度、ポートフォリオとして「[技術士1次試験 基礎•適性科目 過去問ドリル](https://www.proengineer1exam.com/)」をリリースしたので、ご紹介させていただきます！

https://www.proengineer1exam.com/

## 注意事項

:::note warn
私はプログラミング学習中で、**初学者**です。
内容に**誤り**がある場合があります。
コメント等で教えていただけると幸甚です。
:::

## 作成したアプリ
アプリ名： [技術士1次試験 基礎•適性科目 過去問ドリル](https://www.proengineer1exam.com/)

![opengraph-image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3332611/244503e0-b7fa-9e5a-2158-39fd98eae937.png)

サービス URL： [https://www.proengineer1exam.com](https://www.proengineer1exam.com/)
GitHub URL: [https://github.com/K-chan41/pro_engineer_1st_exam_app](https://github.com/K-chan41/pro_engineer_1st_exam_app)

## サービスの概要
[公益社団法人日本技術士会](https://www.engineer.or.jp/)が年1回開催する**技術士1次試験の基礎科目と適性科目の過去問対策**ができるサービスです。
過去問を実際に**解き**、**解答を選択**し、**正答、解説を確認**できます。
ユーザー登録をしていただくと、問題に**フラグ**を立てたり、間違えた問題を**復習**することができます。

## 作った理由
私自身が当該の試験を受験した際に、完全解説つき学習アプリがなく、過去問解説は書籍ばかりで、**スマホで過去問対策ができず困った経験**があり、**これから受験する方に向けて**制作しました。
また、せっかくサービスをつくるのであれば、「万人受けするおもしろいサービスを作る」よりは、「多少ニッチでも、**必要な人に確実に使っていただけるような実用的なサービス**を作りたい」との思いがありました。一過性のブームで終わるよりも、**長く愛好してくれる方がいるようなサービスにしたい**と思って作成しました。
とくに、ITパスポートや基本情報、FP、宅建などで知られる[過去問道場](https://twitter.com/kakomon_doujou)さんのように、**必要な人に必要とされるサービスにしたい**との思いが強くありました。

## サービスの機能

### 過去問演習機能
令和2年度から令和5年度の基礎科目と適性科目、**全180問**を収録しており、演習することができます。
| 問題選択 | 問題演習 | 
|:-----------:|:------------:|
|[![Image from Gyazo](https://i.gyazo.com/7147173fea3ced5245115f6b4ce28afc.gif)](https://gyazo.com/7147173fea3ced5245115f6b4ce28afc)|[![Image from Gyazo](https://i.gyazo.com/628ebdbfcd6a61fcc20c76a818cf2ebb.gif)](https://gyazo.com/628ebdbfcd6a61fcc20c76a818cf2ebb)|
|過去4年分の演習が可能です|1問ごとに正誤と解説を確認できます|

※掲載している技術士試験の過去問題データは公益社団法人日本技術士会から**利用許諾**を得て使用しています。

### フラグ機能、マイページ
登録ユーザーの方は、問題に**フラグを立てる**ことができます。フラグを立てた問題は**マイページで確認**できます。

| フラグ機能 | マイページ | 
|:-----------:|:------------:|
|<img src="https://i.gyazo.com/c1c9bbfbb7a054ac93205afa56cdf775.png" width="80%">|[![Image from Gyazo](https://i.gyazo.com/eddf16465ccfbd4c9b3694cd84daa5ee.gif)](https://gyazo.com/eddf16465ccfbd4c9b3694cd84daa5ee)|
|不安な問題や間違えた問題にフラグを立てることができます|フラグした問題を確認、再出題することができます（10問）|

マイページではそのほかに、**ランダムに出題**したり、**直近間違えた問題を確認、再出題**することができます（10問）。

### ログイン/新規登録、お問い合わせフォーム
お名前、メールアドレス、パスワードで**新規登録**できます。
お名前とメールアドレスで**お問い合わせフォーム送信**できます。
（**PWA導入**してます）

| ログイン/新規登録 | お問い合わせフォーム | 
|:-----------:|:------------:|
|[![Image from Gyazo](https://i.gyazo.com/5f0b364a7895fabd788c22abfb3f72eb.jpg)](https://gyazo.com/5f0b364a7895fabd788c22abfb3f72eb)|[![Image from Gyazo](https://i.gyazo.com/6ef400255087a2d9be01b28b04fdf0f0.jpg)](https://gyazo.com/6ef400255087a2d9be01b28b04fdf0f0)|

## 工夫したところ
### 見ため
**React**のUIライブラリ、[Mantine](https://mantine.dev/)を使用しました。
私自身デザインができるわけでもないのに、HTML、CSSを自分で書くのは絶対に危険だと思い、なんとしても**コンポーネントで開発**を行いたく、React学習を始めました。
また、絶対に**スマホでも快適に表示**させたかったので、**レスポンシブ対応**がすぐにできるようなものでやりたかった思いもありました。

TOPページでサーバー側からデータを取ってきている部分などにloadingのコンポーネントを入れたりして、**ユーザーのストレスを軽減**しようとしました。

[![Image from Gyazo](https://i.gyazo.com/2720db5fda9187ec96708a2863e08447.png)](https://gyazo.com/2720db5fda9187ec96708a2863e08447)

### Latex形式のコンパイル
問題や、解説のデータはChatGPTを使いつつ、自力で作成しました。
数式については、**Latexの形式で保存**し、API側から下記のような感じででてきたものを**フロント側でレンダリング**しています。
```JSON:APIからのレスポンスボディの一部
{
    "id": "93",
    "type": "question",
    "attributes": {
    ~省略~
        "commentary": "この問題では、引張力 \\( Fa \\) と圧縮力 \\( Fb \\) がそれぞれ正規分布に従っているとされています。合力 \\( F = Fa - Fb \\) が200N以上の引張力となる確率を求める問題です。\n\nまず、合力 \\( F \\) の平均と標準偏差を求めます。\n\n- \\( F \\) の平均\\( \\mu_F = \\mu_{Fa} - \\mu_{Fb} = 300N - 200N = 100N \\)\n- \\( F \\) の標準偏差 \\( \\sigma_F = \\sqrt{\\sigma_{Fa}^2 + \\sigma_{Fb}^2} = \\sqrt{30^2 + 40^2} = \\sqrt{900 + 1600} = \\sqrt{2500} = 50N \\)\n\n次に、合力 \\( F \\) が200N以上となる確率を求めます。これは、\\( F \\) が \\( \\mu_F + 2\\sigma_F \\) 以上となる確率に相当します。\n\n- \\( \\mu_F + 2\\sigma_F = 100N + 2 \\times 50N = 200N \\)\n\nこの値が \\( z = 2.0 \\) に相当するので、\\( z \\) 以上となる確率は2.28%です。\n\nしたがって、棒部材の合力が200N以上の引張力となる確率は2.28%となります。",
  ~省略~
    },
```

上記のなかの`\\(` と `\\)`で囲まれた文字等が、下記のように**きれいな数式**で表示されます。
[![Image from Gyazo](https://i.gyazo.com/e6ca60079e3a600bed8f6168b7ea429d.png)](https://gyazo.com/e6ca60079e3a600bed8f6168b7ea429d)

JavaScriptライブラリの[Katex](https://katex.org/)を使用しました（下記、一部抜粋）。
**Katexでレンダリング**→**マークダウンでレンダリング**→**表示**

```JSX:src/app/questions/page.tsx
import katex from 'katex';
import { marked } from 'marked';


const renderMarkdownAndLatex = async (markdownText: string) => {
    // 1. LaTeX数式のレンダリング
    let latexProcessedText = markdownText.replace(/\\\((.*?)\\\)/g, (match, formula) => {
      return katex.renderToString(formula, { throwOnError: false, displayMode: false });
    }).replace(/\\\[(.*?)\\\]/g, (match: any, formula: string) => {
      return katex.renderToString(formula, { throwOnError: false, displayMode: true });
    });
    
    // 2. Markdownのレンダリング
    return marked(latexProcessedText);
};
```

### csvからseedを使ってデータを格納
今回、自分で問題や解答のデータをcsvで作成しAPIを作りました。
csvから**seedでテーブルにデータ**を入れました。
```ruby:seed.rb
require 'csv'

# Subjects
csv_text = File.read(Rails.root.join('db', 'csv_data', 'subjects.csv'))
csv = CSV.parse(csv_text, headers: true, encoding: 'UTF-8')
csv.each do |row|
  Subject.create!({
    year: row['year'].to_i,
    exam_subject: row['exam_subject'].to_i
  })
end
puts "Imported #{csv.size} rows to Subjects"
```

## 主な使用技術と選定理由
### フロントエンド(主なもの)
- Next.js 14.0.3
- React 18
- Mantine 7.3.x
- Nodemailer 6.9.7
#### 技術選定理由
##### React、Next.js
自分でHTML、CSSを書くのは正直怖く（とんでもないデザインのものを作ってしまう気がした）、**コンポーネントを使って開発を行いたかった**ので、Reactを学習し始めました。**Next.js**はReactベースのフレームワークのなかでも**ディフェクトスタンダードになりつつある**と感じたので、Next.jsにしました。また、**App Router**によるフォルダーベースでのルーティングができるのも自分にとっては**新鮮**で、React Routerよりも容易に感じました。

:::note warn
ただ、先輩方もよく仰っていますが、私のように「やってみたい」だけの理由で学習をするのではなく、就活を見据えて、「御社に入りたくて、〇〇の技術をつかったPFを作りました」と言えるくらい**戦略立てて技術選定、開発**したほうがいいと思います。
:::

##### Mantine
数あるコンポーネントのなかで、**Mantine**を選んだのは、RUNTEQの大先輩[いない](https://twitter.com/yusuke_blog1026)さんの[TechFinder](https://qiita.com/yusuke_blog1026/items/c68c45790a977cbd0715)をみて「こんな見た目のサイトつくりたい！」と思ったのが一番の理由です（技術的には足元にもおよんでいませんが）。一応、Reactを学ぶなかで、**MUI**も触ってみましたが、最終的に「やっぱりMantineのほうが**統一感**があっていい」と感じたので、Mantineにしました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">【個人開発】<br>個人開発に役立つ技術記事を簡単に検索・閲覧できるサービス「TechFinder」をリリースしました！<a href="https://t.co/NX2y2Z2XFW">https://t.co/NX2y2Z2XFW</a><br><br>特にポートフォリオ作りに悩んでいる <a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニア</a> の皆さんに重宝するサービスとなってますので、ぜひ利用してみてください〜👏 <a href="https://t.co/zuRD4Ra6Ff">pic.twitter.com/zuRD4Ra6Ff</a></p>&mdash; いない (@yusuke_blog1026) <a href="https://twitter.com/yusuke_blog1026/status/1623935829377970176?ref_src=twsrc%5Etfw">February 10, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##### Nodemailer
**お問い合わせフォーム**はMantineの`useForm`と、Node.jsで動作するメール送信用のライブラリの[Nodemailer](https://nodemailer.com/)を使用して、作成しました。**OAuth2.0**を使って問い合わせ本人と管理者（私）にGメールが飛ぶようになっています。実装は下記記事が参考になりました。
実装に際して、サーバーアクションかAPIルートにするか悩みましたが、ちょっとサーバーアクションの理解が浅かったのとnodemailerに関してAPIルートの情報しかネットで見つけられなかったこともあり、今回は**APIルート**にしました。

- [node.js上のnodemailerでOAuth2.0を使って gmail からメールを送る](https://gist.github.com/neguse11/bc09d86e7acbd6442cd4)

### バックエンド
- Ruby on Rails(APIモード) 7.0.4
- ruby 3.1.2
- PostgreSQL

#### 技術選定理由
##### Ruby on Rails
プログラミングスクールで学習したのがRuby on Railsで、**未経験ながら、もっとも慣れていた**ため今回使いました。

### インフラ
- Vercel
- Heroku

#### 技術選定理由
大前提として、私自身のインフラ知識が乏しかったので、とにかく簡単で自分が**確実にデプロイできるPaaS**を選ぼうと思ってました。
##### Vercel
Next.jsを開発したVercel社が運営するホスティングサービスで、**Next.jsとの親和性**が高く、**GitHubと連携**させるとpushするだけでデプロイしてくれるのがなによりもよかったです。一度別アプリ等を自分なりにデプロイして試した上で、**Vercel**に決定しました。
##### Heroku
こちらも、プログラミングスクール内で**一度触ったことがあり、安心感**があったので、使いました。同じく、**GitHubと連携**させるとpushするだけでデプロイまで行ってくれるように設定できるのが好きでした。

## ER図
[![Image from Gyazo](https://i.gyazo.com/04681425c2d317b91335d867c48e0a78.png)](https://gyazo.com/04681425c2d317b91335d867c48e0a78)

## 勉強期間、開発期間
ここからは、**RUNTEQで学習中の方**や、未経験からプログラミングを学習して、**Webアプリを制作しようとしている方**に向けて、私の経験をお伝えさせていただきます。
現在、**モダンJavaScript**のポートフォリオへの採用熱が高まっており、RUNTEQ界隈や未経験組のXを見ていても、最近は2人に1人くらいの勢いで、Reactや、Vueなどのフロントを使っている方を見かけます。
ただ、スクールのカリキュラムではRuby on Railsで完結するWebアプリが中心なので、**別途、勉強する時間を設けなくてはいけません**。
私の場合ですが、**勉強期間と開発期間合わせ、3ヶ月くらい**かかってます。（私の効率が悪いだけかもしれませんが、）働きながらの方はそのくらいは見積もっておいたほうがいいと思います。

たぶん、自分のWebアプリにReactとかを取り入れるか迷っている未経験の方がいるかなと思うので、どのくらいのスケジュール感でいけそうか、私の開発、勉強時間が**一つの判断材料**になれば幸いです。
ちなみに、私は**平日仕事をしながら**の学習で、カリキュラムの**進捗と卒業は割りとぎりぎり**なほうでした。

:::note warn
あくまで、私自身の学習と開発にかかった時間です。
**もっと、効率的に勉強することはできる**と思います。働きながらの場合の**目安**として見ていただければ幸いです。
また、フルコミの方や、カリキュラムを早く終えた方はもっと時間に余裕があるので、もっと開発に時間を割いてブラッシュアップに力を入れることもできると思います。
:::

### 勉強期間
まず、開発を開始する前に勉強した内容は以下です。

- Rails APIモード
- JavaScript
- React
- TypeScript
- Next.js

スクールのカリキュラムを終えた（卒業認定を受けた）のは**9月末頃**で、**2023年12月末までにMVP**するというマイルストーンを自分で立てて、取り組みました。
また、上記に加えて、私の場合は、問題文や解説文の試験情報を手作りしたので、それも平行して行いました。

:::note warn
自分でコンテンツをつくるのは**絶対に**おすすめしません!!! 絶対に**オープンデータでできるものを考えたほうがいい**です!!!
:::

#### Rails APIモード  (約2週間：2023年10月10日〜2023年10月21日)
こちらは、RUNTEQのアディショナルのカリキュラムにある、**Rails API編**をそのままやりました。

#### JavaScript、React (約1ヶ月：2023年10月1日〜2023年10月30日)
その後、フロント側の勉強をどうやってやっていけばいいのかな〜と思いつつ、Rails APIとどんな感じでやり取りし合うのかイメージがわかなかったので、**Youtubeで無料の動画**を見ながら真似をしてアプリをつくったりしてました（このときはReactをまったく理解しておらず、とにかくコードを書き写しただけでした）。

<iframe width="560" height="315" src="https://www.youtube.com/embed/yD0QotED6f8?si=10OV7gJG1FHJFvu3" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

なんとなくReact + Rails APIのWebアプリ開発の**雰囲気がわかった**ところで、つぎに[Reactのチュートリアル](https://ja.react.dev/learn)をやろうとしたのですが、意味がまったく分からず、やばいってなりました。おそらく**JavaScriptの知識が乏しいせい**だとなんとなくは理解しつつ、どうしようか本屋で本を眺めていると、[基礎から学ぶ React/React Hooks](https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6-React-Hooks-asakohattori/dp/486354359X)の本と出会いました。この本は、**Reactのチュートリアルに挫折した人向け**に書かれています。最初にJavaScriptについて説明してくれているところがあり、大変わかりやすく、**Reactに必要なJavaScriptの知識が網羅**されています。
書籍を一通り読みながら、なんとなく理解してきたので、**同時にReactチュートリアルも**やり始めました。この段階で、やっとチュートリアルの言ってる内容を**おおかた理解**しはじめました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">2023.10.20~22 (day277~279)<br>学習時間: 8h (total 623.5h)<br>学習内容: 基礎から学ぶReact/React Hooks、Rails API編、Reactチュートリアル<br><br>やっと、チュートリアルの言ってること9割がた理解でき始めた〜<br>ちょっと回り道したけど、「基礎から学ぶ〜」の本やってよかった。<br>しかし、このペースやばいな <a href="https://t.co/5VEA9Zgowt">pic.twitter.com/5VEA9Zgowt</a></p>&mdash; けーちゃん@RUNTEQ41期 (@mimi048599) <a href="https://twitter.com/mimi048599/status/1716026791440302264?ref_src=twsrc%5Etfw">October 22, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

#### React、TypeScript、Next.js (約2週間：2023年11月20日〜2023年11月30日)
Reactチュートリアルをおおかた終え、**Reactの概念的な部分は理解**したので、さあ、create-react-appしようかと思ったのですが、実装のイメージがまだわかず、**フレームワークとかを勉強すべきでは**、、、と思い始めました。
そこで、有名な[これからはじめるReact実践入門 コンポーネントの基本からNext.jsによるアプリ開発まで](https://www.amazon.co.jp/%E3%81%93%E3%82%8C%E3%81%8B%E3%82%89%E3%81%AF%E3%81%98%E3%82%81%E3%82%8BReact%E5%AE%9F%E8%B7%B5%E5%85%A5%E9%96%80-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E5%9F%BA%E6%9C%AC%E3%81%8B%E3%82%89Next-js%E3%81%AB%E3%82%88%E3%82%8B%E3%82%A2%E3%83%97%E3%83%AA%E9%96%8B%E7%99%BA%E3%81%BE%E3%81%A7-%E5%B1%B1%E7%94%B0-%E7%A5%A5%E5%AF%9B/dp/4815619484)の書籍をやりはじめ、**TypeScript、Next.js、Vercelへのデプロイ方法まで**網羅しました。
TypeScript、Next.jsに関してはそこまで詳細を説明してくれているわけではないですが、「**TypeScriptはChatGPTでなんとかなる**だろう」、「**Next.jsはドキュメントが英語だけどなんとなく読みやすいし大丈夫**だろう」と思い、やっと、**そろそろ実装はじめるぞ**と思える段階まできました。

### 開発期間
#### バックエンド側 (約1週間：2023年10月31日〜2023年11月7日)
私の制作するWebアプリは単純で、ほぼほぼ、RUNTEQのRails API編で学習したことを**そのまま活かせた**ので、**困ることはありません**でした（ただ、テーブルに格納するデータを自分で作るのには膨大な時間がかかりました、何度も言いますが**絶対におすすめしません**）。

#### フロントエンド側 (約1ヶ月：2023年12月1日〜2024年1月5日)
11月に3週間ほど現職の出張があったり、業務が忙しく、毎日残業や会食づけで全く勉強する時間がとれない時期がありました。
そろそろ、開発を始めなくては本格的にまずいなと直感的に思いながら、create-next-appをして、フロントエンド側の開発を始めました。
**Mantineのコンポーネントはどれも可愛くて**、いろんなコンポーネントを吟味して楽しみながら実装しました。

## 心残り、今後の課題
以下のことを今後の課題だと感じています。
- **サーバーアクション**：やっぱり、Next.js 14を使っているのであれば、APIルートではなく、**サーバーアクションにチャレンジしてみるべき**だと思いました。次はリベンジしたいです。
- **ログイン認証**：自作のAPIで認証をしたのですが、**Firebase Authなどでユーザー体験を向上**させるべきだったかなと思っています。しかし、初めてのRails APIで、自分で**認証の仕組みを1から作ったのはとても勉強**にはなりました。
- **コンポーネントの設計**：再レンダリングの範囲やひとつのファイルに多くのロジックを書いていたり、重複して書いている箇所が多くあり、**コンポーネント設計の考え方を学ぶ必要がある**と思っています。なにかいい教材があれば教えていただければ嬉しいです。
- **テスト**:現在、テストを一切書いていないのでそろそろ書いていきます。

## おわり
今回**始めて0からWebアプリを作成**しましたが、本当に**たくさんの学び**がありました。
自分の**知識不足**、**課題も**多くみつかり、次への宿題も多くみつかりました。

だいぶ、のんびりと勉強、開発をしてしまいましたが、**それも含め、自分のレベル感**かなとも感じています。
本記事がこれから開発しようとしている方や、技術選定に悩んでいる方、お仕事をしながら学習している方、みなさまのなにかの参考になれば幸いです。
最後までご覧いただき、ありがとうございました:blush: