アドベントカレンダーを作れるアプリを作ってみた【Vue.js×Firebase】

この記事は[株式会社クロノス](https://www.kronos.jp/)の「~2020年春~勝手にやりますアドベントカレンダー」の１日目の企画記事です。
（株式会社クロノスのFacebookは[こちら](https://www.facebook.com/kronosjp/)から）

「~2020年春~勝手にやりますアドベントカレンダー」インデックスは[こちら](https://qiita.com/beeeyan/items/342d51fa301bcf81e9c0)（随時更新）

# ということで本題
トップバッターを担当します、maroKanataniです。
上述の通り、会社でアドベントカレンダーやろうって話になったので
流れに乗ってアドベントカレンダーを作れるアプリを作ってみました。
技術の話も含めて公開までの手順等をまとめておきます。

アプリは以下のリンクから使えます。
[アドベントカレンダーツクール](https://ad-calendar-992c5.firebaseapp.com/)

ソースコードは[こちら](https://github.com/Kanatani28/ad-calendar)  
（不具合等ありましたら、コメント等でこっそり教えてください:bow_tone1:）

ちなみに今回やるアドベントカレンダーをこのアプリで作ると以下のような形になります。
[~2020年春~勝手にやりますアドベントカレンダー](https://ad-calendar-992c5.firebaseapp.com/view-calendar/RJjcAaxRFnD5IPDw2bDq)

# できること

## アドベントカレンダーの作成
カレンダーのタイトル、対象月、何日まで投稿するか、休日も投稿するかどうかを設定できます。
設定を変えるとカレンダーのプレビューが同時に更新されます。
![スクリーンショット 2020-02-28 22.05.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/3bac56f5-fb36-bfc2-8c78-110b0ba4fbee.png)

登録を進めると最終的に以下のようなダイアログが出てきます。
テキストボックス内のリンクを共有することで他者とカレンダーの情報を共有することができます。
閲覧専用のページと参加登録等の編集が可能なページの2種類があります。
（今回はなるべく外部から編集されたくなかったのでこのような形にしました。）

![スクリーンショット 2020-02-28 22.23.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/1a588601-bc09-8857-d3f4-e6bc620ea11d.png)

## カレンダーの閲覧、参加登録
共有できるURLには閲覧専用と編集可能なものの2つがあり、
編集可能URLからのみ、参加登録や編集が可能となっています。

閲覧専用ページ
![スクリーンショット 2020-03-01 13.48.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/22813b1b-1a94-cfa4-c74a-342d6b34de48.png)
編集可能ページ
![スクリーンショット 2020-03-01 13.48.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/0a984669-68b0-3279-be71-44a0ab2f3812.png)


# モチベーション
**社内のアウトプットがもっと活発になってほしい。** という切なる願いが一番のモチベーションです。
弊社もQiitaやSlackの社内チャンネルでのアウトプットは今でもあるにはあるのですが
大体いつも同じ人が投稿していたりして、活発なように見えるけど実はそんなにやん・・・と感じることが多く**（個人の感想です）**、もっと色んな人を巻き込んだ取り組みができないかなーと思っていました。
そんなところに今回アドベントカレンダーをやろうという話が来たので、全力で乗っかりにかかった次第です。
自分一人だけでアウトプット続けるのは大変ですし、 **アドベントカレンダーはいい意味でのプレッシャーを抱えつつ続けられる良いツール**だと思っているので、12月以外でもできるようになればいいなーという気持ちもあり、今回このアプリの作成に取り組んでみました。


# 使用技術
![スクリーンショット 2020-02-29 17.04.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/0aba0ee4-9d1b-55aa-2dd2-58f3800e3812.png)

- Vue.js
- Vuex
- Buefy
- Firebase(Hosting, Firestore)

（ちなみに私自身、どの技術も初学でしたので至らない部分は多々あるかと思います。何かあればご指摘いただけると幸いです。）



# 環境構築～デプロイまで
手空きの時間にちょっとづつ作ったのでだいたい2週間くらいかかってしまいました。
一日平均2~3時間といったところでしょうか。

以下手順になります。（長いのでいらない方は最後までスキップ推奨）
※npmコマンドを多用するので、Node.jsをインストールしておく必要があります。

## Vue.js

### vue-cliのインストール
今回はプロジェクトを作成するのにvue-cliを使用しました。
以下のコマンドでインストールできます。（yarnでも可能です）

```
npm install -g @vue/cli
```

### プロジェクトの作成

プロジェクトの作成は以下のコマンドで行います。

```
vue create [プロジェクト名]
```

コマンドを叩くと色々聞かれますが、今回は基本的に全部デフォルトで作成しました。

### ローカルで起動

出来上がったプロジェクトのディレクトリ内で以下のコマンドを実行すると開発用サーバーが立ち上がります。

```
npm run serve
```
![スクリーンショット 2020-02-29 17.37.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/22037d97-bf1e-55ee-98b0-f4306d6b1ff2.png)

デフォルトだとlocalhost:8080で起動します。
ブラウザからアクセスしてみましょう。
以下のような画面が出ればおっけーです。

![スクリーンショット 2020-02-29 17.38.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/69ed4233-882d-fae2-bcf0-c4be3d651e40.png)


### ひたすら実装
プロジェクトができたので、あとはひたすら実装です。
ますはFirebaseと連携する部分以外を画面設計しながら実装しました。
コンポーネントの設計やディレクトリ構成は以下の記事を参考にさせていただきました。

[2020年にWebフロントエンドを勉強する人が作るべきたったひとつのアプリ](https://qiita.com/Dragon-taro/items/03f322dee15b19c33613)
[Vue.js ディレクトリ構成 色々試してみた](https://qiita.com/tockn/items/2ce68b99e0839df52200)

あと、今回はUIライブラリにBuefy、ルーティングにVue Router、状態管理にVuexを使用しているので、main.js、index.htmlに以下のように修正しています。

各ライブラリインストール

```
npm install buefy
npm install vue-router
npm install vuex --save
```

```js:main.js
import Vue from 'vue'
import App from './App.vue'
import Buefy from 'buefy'      // Buefy用
import 'buefy/dist/buefy.css'  // Buefy用
import store from '@/store'    // Vuex用
import router from '@/router'  // Vue Router用

// Buefy用
Vue.use(Buefy, {
  defaultIconPack: 'fa',
})

Vue.config.productionTip = false

new Vue({
  router,    // Vue Router用
  store,     // Vuex用
  render: h => h(App),
}).$mount('#app')
```

```html:index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- Buefyで使用するアイコンをインポートする -->
    <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.2.0/css/all.css">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
```


## Firebase
Firebaseを使用するにはGoogleアカウントが必要になるので、ない場合は作成しましょう。
今回は全て無料で使えるSparkプランで作ってます。
（無料でこんなに使えるなんてGoogleさん太っ腹ですよね）

### プロジェクトの作成
Firebase Consoleの以下の画面からプロジェクトを追加しましょう。
今回はプロジェクト名以外は全てデフォルトで作成しています。
![スクリーンショット 2020-02-29 17.56.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/633a33ba-3027-567a-f897-4500d0853d20.png)

### DB作成
プロジェクトのページのサイドバーからDatabaseをクリックするとDatabaseを管理する画面へ遷移します。
初回はロケーションの設定と、テストモードか本番モードを設定するよう言われるので、今回はロケーションはasia-northeast1、テストモードに設定しました。（あとで本番モードに変更しました。）

設定後はデータの作成が行えます。今回はカレンダーの情報を格納するcalendarsというコレクションと予定を格納するschedulesというコレクションを作成しました。
（ちなみにRDBしか実務で触ったことがなかった自分からすると、 **コレクション=テーブル、ドキュメント=レコード**のような位置づけのように感じました）

![スクリーンショット 2020-02-29 18.13.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/91987d4e-1d4a-a42a-f432-0a01a4098bf1.png)


### Vueのプロジェクトに組み込む

#### 認証情報の取得
DBは用意できたのでVueからアクセスできるようにしましょう。
Firebaseのプロジェクトのページから「＋アプリを追加」を選択します。
今回はWebアプリなのでWebを選択します。
![スクリーンショット 2020-02-29 18.28.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/7717e741-4758-eeec-1c5c-875af11e7fb1.png)

アプリのニックネームを入力し、今回はHostingも利用するのでチェックボックスにチェックを入れて登録。
（②以降はあとでやるので一旦スキップ）

![スクリーンショット 2020-02-29 18.33.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/1e1853e5-c3e5-211f-d2e5-52ea51651dcd.png)


プロジェクトのページに戻ると「＋アプリを追加」の左に先程設定した内容があるのでクリック
画面の下部のFirebase SDK snippet > 構成でAPI Key等の情報が見られるのでコピーする。

#### Vueアプリケーションに設定する
Firebaseのライブラリをインストールする

```
npm install firebase --save
```

今回はsrc直下にfirebaseというディレクトリを作成し、そこに先程作成した認証情報を配置します。
以下2つのファイルを配置します。

```js:firebaseConfig.js
// さっきコピーした内容を貼る
export default { 
    apiKey: '', 
    authDomain: '', 
    databaseURL: '', 
    projectId: '', 
    storageBucket: '', 
    messagingSenderId: '' 
}
```

```js:firebaseInit.js
import firebase from 'firebase'
import 'firebase/firestore'
import firebaseConfig from './firebaseConfig'
const firebaseApp = firebase.initializeApp(firebaseConfig)
export default firebaseApp.firestore()
```

こうすることで以下のように使用できます。

```js:sample
import db from '@/firebase/firebaseInit'
import firebase from 'firebase'

const calendarRef = db.collection(collectionId) // コレクションID
                      .doc(documentId)          // ドキュメントID

calendarRef.get().then(doc => {
    console.log(doc.data())    // JSONが出力される
})
```


### firebase cli導入
デプロイ用にfirebase cliをインストールします

```
npm install -g firebase-tools
```

### デプロイ
まずはFirebaseのコマンド実行するために認証を行います。

```
firebase login
```

次にVueのプロジェクトのディレクトリで以下のコマンドを実行します。

```
firebase init
```

今回はFirestoreとHostingを使用するのでその2つをチェックします。
![スクリーンショット 2020-02-29 19.13.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/4da7d623-1888-8896-c3fd-0cd490ec583f.png)

以降は基本的にはデフォルトで、以下のpublic/index.htmlを書き換えるような設定はNoを選択しましょう。
![スクリーンショット 2020-02-29 19.17.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239351/a5dd2300-87f0-7764-0294-000ed9c900c7.png)

次にfirebase.jsonを修正します。
デプロイの対象をdist配下のファイルに変更します。

```json:firebase.json
{
  "hosting": {
    "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  }
}

```

次にデプロイ用にVueのプロジェクトをビルドします。
dist配下に新しくファイルが作成されるはずです。

```
npm run build
```


最後に以下のコマンドでアプリをデプロイします。

```
firebase deploy
```

コンソールに表示されたURLに無事アクセスできたら成功です。


# 苦労した点
画面コンポーネントの設計に関する部分が一番苦労しました。
特にVuexの使い所です。コンポーネントを分割するのはいいんですが、**何でもかんでもstoreに頼ってしまうと、データの流れがわかりにくくカオスな状態になる（特に更新系）**ので、どのコンポーネントまではstoreを参照して、他は基本的にはpropsで渡す、みたいなルール決めを上手くやらないといけないなーという感想を持ちました。（規模が大きくなったときとか特に難しそう）
今回はとりあえずpages配下のコンポーネントのみstoreとやり取りする形を目指しました。

# 最後に
簡単なものではあるものの、アプリの作成から公開まで一人でできたのは良い経験になったと思います。
Firebaseを使うと結果的にアプリ側の作成に集中できる時間が増えるので、こういった個人開発には特に向いているツールだと感じました。
今回作成したアプリも自分が認識しているだけでも課題は山程あるので、可能な範囲で引き続き改修していけたらなと思ってます。**開発楽しい。技術楽しい。**


# 参考
[2020年にWebフロントエンドを勉強する人が作るべきたったひとつのアプリ](https://qiita.com/Dragon-taro/items/03f322dee15b19c33613)
[Vue.js ディレクトリ構成 色々試してみた](https://qiita.com/tockn/items/2ce68b99e0839df52200)
[光の速さでデプロイ ~ Vue + Firebase hosting ことはじめ ~](https://qiita.com/isihigameKoudai/items/4d41dd083d04adb441c3)
[Vue.js + FirebaseでTodoアプリを作る](https://qiita.com/magaya0403/items/e292cd250184ea3fe7b0)