Next.js+Goでクイズアプリを作ってみる

## はじめに

### 概要

インフラエンジニアだった私が、開発の勉強をするためにクイズアプリを開発してみました。文法や実装の仕方の記事はいろいろとあるので、どのように学習したかを重点的に書いています。私の勉強の備忘録の記事になってしまいましたが、勉強方法が分からず困っている人の参考になれば幸いです。

### 対象読者

* 開発の勉強をし始めている人
* Go言語初心者
* Next.js, TypeScriptちょっとわかる人

### 書いたきっかけ

* Go言語の製品やサンプルAppを多く見かけるようになったのでGo言語の勉強をしたくなった
* 部署内で技術用語を確認し合えるサービスがあったらいいなと思った
* Advent Calendarという目標と期限を決めて勉強したかった

### 完成品
いろいろやりたいことはありましたが、とりあえず一問一答までとなりました。
題材はとりあえず私に身近なクラウドネイティブにしてみました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2249551/bcda4c57-dd6a-81a2-0e69-ea7f72f77bee.png" width=350px> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2249551/92f69f60-12d8-bb7e-289b-1d1c1b1a2166.png" width=350px>

## バックエンドの開発

### Go言語の勉強

新しい言語を学ぶとき、私は簡単なサンプルAPを実装しながら学習します。私のおすすめはToDoアプリです。大体の言語でToDoアプリの記事があり、かつToDoアプリはいろいろな要素を学習できる良い教材だと思います。

Go言語を触るのが初めてだったので、いつもと同じように学習しようとしましたが、パッケージ管理で早速つまずきました。パッケージ管理方法がバージョンごとで異なるとか色々出てきましたが、最終的にはきちんとプロジェクトを作っていなかったことが原因でした。言われてみれば当たり前のことでした。。

具体的には、下記コマンドを実行していませんでした。

```bash
$ go mod init ${PROJECT_NAME}
```
当たり前であるが故に省略されていることがあるので、皆様は同じ失敗をしないよう気をつけてください。ちなみに私がこのことに気づいたのは、GoのホームページのGet Startedをやって気づきました。改めてGet Startedをやることの重要性に気付かされました。

プロジェクト作成やパッケージ管理は、この記事にも書いている通り、他言語から来た人がはまるポイントのようです。

https://qiita.com/mumoshu/items/0d2f2a13c6e9fc8da2a4

最終的に下記２つのサイトを実践して学習しました。

- [Go / Ginで超簡単なWebアプリを作る - Qiita](https://qiita.com/hyo_07/items/59c093dda143325b1859)
- [Tutorial: Get Started with Go](https://go.dev/doc/tutorial/getting-started)

### Docker環境

OSに依存しないようにDocker環境で開発しました。

"docker go"で検索した時に出てきた記事を参考に作りました。今回Dockerの操作について詳細に説明しませんが、作成したファイルのみを参考として記載します。

```yaml:docker-compose.yml
services:
  api:
    container_name: api
    build:
      context: .
      dockerfile: ./docker/go/Dockerfile
    ports:
      - 8080:8080
    links:
      - db
    tty: true
    volumes:
      - ./api:/go/src/api
  db:
    container_name: db
    build: ./docker/mysql
    hostname: db
    ports:
      - '3306:3306'
    environment:
      - MYSQL_ROOT_PASSWORD: password
      - MYSQL_DATABASE: quiz
      - MYSQL_USER: user
      - MYSQL_PASSWORD: password
    command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci --skip-character-set-client-handshake
    volumes:
      - './db/data:/var/lib/mysql'
      - './db/config:/etc/mysql/conf.d'
      - './db/initdb:/docker-entrypoint-initdb.d'
```

```dockerfile:docker/go/Dockerfile
FROM golang:1.17.3-alpine3.14
RUN mkdir /go/src/api
WORKDIR /go/src/api
COPY ./api /go/src/api
VOLUME /go/src/api
RUN go mod download
EXPOSE 8080
CMD "go" "run" "main.go"
```

### APIの作成

この2つの記事を参考にして作りました。

1つ目の記事はステップバイステップでやってくれているので、初学者の私にはちょうど良かったです。データモデル部分を置き換えるだけで簡単に実装できました。
２つ目の記事は1つ目の記事をベースに作っているので、CORS設定やMySQL部分などを参考にさせていただきました。

https://qiita.com/Asuforce/items/0bde8cabb30ac094fcb4

https://mintaku-blog.net/go-gin/

## フロントエンドの開発

### Next.js,TypeScriptの勉強

フロントエンドは以前学習したことのあるNext.js,TypeScriptを使いました。知り合いに教えてもらいながら勉強したのでGo言語ほどちゃんと案内ができませんが、わかりやすく解説しているYoutubeの教材がたくさんあるので、教材には困らないと思います。

### 環境準備

バックエンド同様にDocker環境にしました。

```yaml:docker-compose.yml
sevices:
  frontend:
    container_name: frontend
    build: ./docker/frontend/
    environment:
      - NODE_ENV=development
    command: sh -c "npm run dev"
    # command: sh -c "cd frontend && yarn dev"
    volumes:
      - ./frontend:/usr/src/app
      # - .:/usr/src/app
    ports:
      - '3000:3000'
    tty: true
```

下記コマンドでプロジェクトを作成しました。プロジェクト作成後に毎回cdするのが大変だったので、上のファイルのコメントアウト部分の通り修正しています。

```bash
$ docker-compose run --rm <container_name> sh -c 'npx create-next-app --example with-typescript-eslint-jest <project_name>'
```

### Atomic Design

コンポーネントの分割の仕方はAtomic Designを採用しました。Atomic Designについては、ちょうど[16日目の記事](https://qiita.com/kinakome/items/5ab96b45880ce69a4c5e)で解説しているのでそちらをご参照ください。この記事にも書いてある通り、Atoms/Molecules/Organismsの分け方に正解はなく、試行錯誤しながらベストなものを見つけていくしかないところが難しいなあと実感しました。

ちなみに私は下記教材で学習しました。有料ですが、初学者にはいい教材だと思います。

https://www.techpit.jp/courses/109

### TailwindCSS

デザイン周りはCSSフレームワークの1つであり、利用者が増えている[TailwindCSS](https://tailwindcss.com/)を導入しました。良い面や悪い面を書いている記事があるのは知っていますが、まずは使ってみないと何も言えないので使ってみました。

導入については、検索すればすぐ出てくる下記2つのサイトを参考に導入しました。

* [Next.js (JavaScript/TypeScript) に Tailwind CSS を導入する方法](https://fwywd.com/tech/next-tailwind)
* [Nextjsにtailwindcssをインストールしてみる](https://zenn.dev/mo_ri_regen/articles/install-tailwind-article)

作成までに時間がなかったので、コンポーネントはありものを参考にしました。具体的には、[tailwind UI](https://tailwindui.com/), [TAILBLOCKS](https://tailblocks.cc/), [tailwindcomponents](https://tailwindcomponents.com/) あたりを参照しました。ツール集をまとめているサイトもあります。

https://omkz.net/archives/89

### APIとの繋ぎ込み
今回は、質問と解答という動的に変更しない項目を呼び出すだけなので、getStaticProps内で呼び出すことにしました。最終的に下記のような形で実装しています。

https://nextjs-ja-translation-docs.vercel.app/docs/basic-features/data-fetching

getStaticProps内でfetchではなくuseSWRを使おうと思いましたが、フックの基本ルールに引っかかることを忘れていました。。

https://qiita.com/tatsumin0206/items/4e1076e2deedf20a9485

ちなみに以前開発した時はCORSエラーにハマりましたが、今回はバックエンド部分であらかじめ設定したので問題ありませんでした。

## まとめ
以上で本記事は終了となります。
今回は一問一答という最低限の実装のみになってしまいましたが、短時間でフロントエンドとバックエンドの触りを理解できたのかと思っています。

用語に関するクイズに決まった正解はないと思っているので、他人の解答を見て投票したり、質問文をこう変えたらいいのでは？と提案できるなどもっと回答者とインタラクティブな機能を今後は実装していきたいです。

自分が興味のあった内容を取り入れただけなのでベストプラクティスとは限りません。あくまでも一例としてご参照ください。また、私個人がプライベートで勉強した内容なので、会社とは関係なく個人の意見であることをご了承ください。

## 参考

### ディレクトリ構成
frontendの下にsrcディレクトリを挟めば良かったなと後から思いました。

```bash
├── docker-compose.yml
├── api #バックエンド（Go言語）
│   ├── controller
│   ├── db
│   ├── entity
│   ├── server
│   ├── service
│   ├── go.mod
│   ├── go.sum
│   └── main.go
├── db #DB（MySQL）
│   ├── config
│   ├── data
│   └── initdb
├── docker #Dockerfile
└── frontend #フロントエンド（Next.js）
    ├── api
    ├── components
    │   ├── atoms
    │   │   ├── Button
    │   │   ├── CloseButton
    │   │   └── Modal
    │   ├── molecules
    │   │   ├── Answer
    │   │   ├── Question
    │   │   └── UserNameForm
    │   └── organisms
    │       ├── AboutService
    │       ├── QuestionCard
    │       └── QuestionModal
    ├── constants
    ├── node_modules
    ├── pages
    │   ├── _app.tsx
    │   └── index.tsx
    ├── public/img
    ├── types
    ├── package-lock.json
    ├── package.json
    ├── tailwind.config.js
    └── postcss.config.js
```