新宿中央公園検定にNuxt.jsを採用した際の周辺技術選定と設計について

#はじめに
中野（@knakano-gxp）です。
本記事はグロースエクスパートナーズアドベントカレンダー7日目の記事です。

7日目は、新宿中央公園検定を開発した時の話をご紹介します。
[主要プロジェクトメンバーと座談会の弊社ブログ](https://www.gxp-group.co.jp/gplus/?p=5758)もありますので良ければご覧ください。

![スクリーンショット 2021-12-06 101815.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2311299/0d8c87e4-81f4-a16e-beff-0acd173ab813.png)
![スクリーンショット 2021-12-06 102140.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2311299/b4181d87-3eb2-8285-4307-3b02a7ffbd3a.png)

新宿中央公園検定のフロントにはNuxt.jsを使っています。
メインのプロジェクトを抱えた状態でかつ３ヶ月という短い期間で１から開発をおこなった話になります。自分から志願して参加したプロジェクトになります。どう開発していったかを技術を中心にまとめてみました。

#技術選定
###Nuxt.js
Nuxt.jsを採用した主な理由は最初から状態管理やルーティングの自動生成が可能になっていて構築が簡単な事です（開発してくれた方に感謝）
Vueを使ったプロジェクトが以前にあり、その関係で始まったプロジェクトでしたので、使用する技術も合わせた方が他のメンバーも触りやすいと思ったのでVueで進めていく事にしました。
しかし、筆者はVueの経験はそんなになかったので、構築で詰まって時間が掛かりそうというのが目に見えていました。Vueはルーティングする場合にはVueRouter、状態管理をするのにVuexをインストールして設定ファイルを書く必要があったりと一つ一つはそんなに難しいことではないですが、後々課題にぶつかった時にそこに時間を使いたくないと考えていました（それよりもプロダクトの中身の方に力を入れたい）。そこで調べていた所Vue.jsをベースにしたNuxt.jsというOSSを知り採用するという事にしたという経緯です。

###JavaScript
今回のプロジェクトでは開発メンバーがTypeScriptよりもJavaScriptに慣れているということもあり、JavaScriptで書いていくという事にしました。以前にTypeScriptで構築して型が通らなかったり、設定が大変という思いをしたので、期間が短く**初速を出したい**今回のプロジェクトではTypeScript採用はリスクと考えました。使用したい他のJSライブラリが対応してない場合もありますし、型があることのメリットよりもデメリットの方が大きくなりそうという判断もありました。

###Vuetify
デザインを適用する際もなるべく工数を使わないようにしたかったので、VueUIライブラリのVuetifyを使うことにしました。こういったライブラリは用意されているタグとクラスを活用するだけで簡単に見栄えを整えられるというのが魅力だと思います。デザイナーの方にも事前にVuetifyを使うことを伝えておき、VuetifyのUIキットを使ってもらうようにお願いしたのも良かったと思います。
あとはテーマカラーを使用することでデザイン適用時の色変更もテーマカラーを変更するだけで済んだので助かりました。

テーマカラー設定例

```javascript:nuxt.config.js
vuetify: {
    theme: {
      themes: {
        light: {
          primary: '#000000',
        }
      }
    }
```
上記のように予めカラーを設定しておきます。

```vuejs:v-btn-color-sample
<template>
  <v-btn color="primary">
    <slot />
  </v-btn>
</template>
```
例えば上記の例のようにv-btnにcolorに設定するだけで色のついたボタンが作れます。
ボタン一つ一つにカラーコードで設定してしまうと後で変更が大変だと思います。

CSSを書いたのはどうしてもVuetifyで解決するのが難しい所くらいです。
実際にあった例だとプルダウンで選択肢を作る時に思ったように表示されないという問題があり、APIにも用意されていなかったので、カスタマイズすることにしました。
Vuetifyのv-selectをディープセレクタを使いカスタマイズした例：

```vuejs
<style scoped>
::v-deep .v-select__selections{
  text-align:center;
  justify-content:center;
}
::v-deep .v-select__selection--comma{
  white-space: pre-wrap;
}
::v-deep .v-select__selections input{
  display:none;
}
::v-deep .v-list-item__content{
  text-align:center;
}
::v-deep .v-list-item__title{
  white-space: pre-wrap;
}
</style>
```
ディープセレクタを使うと子コンポーネントのスタイルを変更できます。
v-selectそのままだと選択肢がセンタリングされないのと選択肢の文字列が長くなった際に改行されないので上記の様に設定することで変えました。最初はどのクラスが使われているのか分からなかったので、動かしてみてデバッグモードでクラスを確認しながらスタイルを適用して調整する感じでした。

↓カスタマイズしたプルダウン
![スクリーンショット 2021-12-06 112825.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2311299/4efd4fed-6214-05e1-af62-07ea36fb5948.png)

#設計・実装
###アトミックデザイン（Atomic Design）
新しいプロダクトは往々にして最初に決めた仕様から変更する可能性が高いので、最初から変更に強い設計にしておく必要がありました。
そういった点を考慮して調べていく中でアトミックデザインというものを知り、それをベースに作ることにしました。Nuxtのディレクトリ構造が適用させやすかったのも良かったです。
アトミックデザインは単に大きさなどでコンポーネントを分けるということよりも役割を分けることが重要なのだと思いました。それによって変更に強くなり再利用性が高まるのだと思います。
実際に作っていく中で言うと、ボタンをAtomsとして作成しておいたことで再利用しやすくなり、ロジックを入れないようにしていたので変更があった時も対応しやすかったです。

####アトミックデザインに合わせて組んでみた表

|Atomic Design|ディレクトリ（Nuxt.js）|役割|
|---|---|---|
|Atoms（原子）|components/atoms|・最小単位のUIパーツ<br>・Slotでデータ受け取る(単一引数にするため)<br>・ロジックを持たない|
|Molecules(分子)|components/molecules|・Atomsを組み合わせたUIパーツ<br>・Propsでデータ受け取る<br>・ロジックを持たない||
|Organisms(有機体)|components/organisms|・複数のAtoms、Moleculesを持つUIパーツ<br>・Vuexとのやり取り(Dispatch／Getters)<br>・表示制御のロジック|
|Templates(テンプレート)|layouts|・ページレイアウト|
|Pages(ページ)|pages|・アプリケーションのルート<br>・Vuexとのやり取り(Dispatchのみ、そのページに依存したもの)|

###Vuex部分にロジックを集中
今回はバックエンドとのAPIのやり取りやデータ加工などのロジックをVuex部分に集中させました。
これによってページ部分は受け取って表示するのと入力を受け付けて送るだけのシンプルな物になり、ページも組み立て易く、変更も容易になりました。その代わりにVuexの方が肥大化していくことに・・・上手く分割とか出来たら良かったのかも知れないです。

Vuexを使用したサンプルコード

```javascript:store/sample.js
export const actions = {
  async fetchData ({ commit }) {
    const res = await this.$axios.get('/v1/data') // axiosでデータ取得
    await commit('setId', res.data.id) // mutationsを使い設定
    ...他の処理
  }
}
```
設定しておいたアクションをページで呼び出します。

```javascript
export default {
  created() {
    this.$store.dispatch('sample/fetchData')
  }
}
```
ページ側は呼び出すだけになるので、ロジックに変更があった際はVuex側を修正するだけで済むようになります。他のページで同じ動作をさせたい時にも使えるのでこの様な感じに組むと良いのではないかと思いました。

#デプロイ～リリース
ローカルで実行は問題なかったものの、デプロイしてみると問題っておきますよね。検証／本番サーバではコンテキストパスが入っていたので、ルートからのパスになっていた部分で不具合が起きたりしました。
あと起こった問題としては機種やブラウザ依存の問題です。PCのブラウザでは問題なくてもスマホでの操作の場合に想定しない挙動になるというのはよくありました。ローカル開発時でも動作確認はスマホでするようにした方が良いですね。今から考えれば最初から考慮すべきことでしたが、余裕がない中だとそこまで考えられなかったです。今後、気をつけたい点です。

#おわりに
はじめましてなFW使いながら「これで良いんだろうか」と思い悩む日々でした。休みの日も調べ物したりとか、こういう設計にしたら良いんじゃないかと考えたりとか。
でも、限られたリソースの中で取捨選択をしてリリースまで辿り着くことが出来たので、結果的には良かったんだと思います。リリースされたプロダクトを自分の目で現場に見に行った時はほんと嬉しかったです。

今回は技術的なベストプラクティスを追うよりも、いかに限られた中でプロダクトを作るかという点に重きを置きました。今後は見送った改善事項や見直しをしていきたいです。

読んでいただきありがとうございました。