カレーのライスをtech忘れ

odmishienのtechメモ

Webフロントエンド何も分からんがGatsbyを使ってポートフォリオを作り直してみる

はじめに

こんにちは。 odmishienです。この記事はDeNA 21 新卒 Advent Calendar 2020の12日目の記事です。実は4日目も担当していました。

tech.odmishien.fun

4日目に書いた記事はどちらかというと技術的な話というより勉強法や精神論的なポエムだったので、今回は技術ネタを持ってきました。

私はアルバイトの業務では主にバックエンドを担当していて、特にPerl, Python, Go などを書くことが多いです。Webフロントエンド何それ美味しいのという感じで、Vue.jsを趣味のプロダクトで使ってみたことがあり、かろうじてコンポーネントを理解している…というようなレベルです。

でも....カッコよくて便利なポートフォリオが欲しい.....!!!!

そんな気持ちが、ポートフォリオになりました❤︎

今回はWebフロントエンドをバリバリに書いたことのない私がGatsby.jsを使ってポートフォリオサイトを作るにあたってやってみたことをまとめてみようかと思います。

これまでのポートフォリオの問題点

なんとも言えないパフォーマンス

以前作っていたポートフォリオはVue.jsで作成してGitHubPagesでホストしていました。Lighthouse Report Viewerのスコアがこんな感じ。

スクリーンショット 2020-12-03 23.53.15.png

そこまで重たいコンテンツを置いているわけではないのですが、スコアが微妙。

コンテンツをHTMLにベタ書きして更新しなければならない

ポートフォリオなので作ったものを掲載したりしていたのですが、これを手動でやっていました。具体的には以下のようなコンポーネントを作成してそこに props を渡したものを毎回書いていました。

<template>
  <div class="card">
    <img class="card-img-top" width="100%" height="50%":src="src" />
    <div class="card-body">
      <h4 class="card-title">{{ title }}</h4>
      <p class="card-text">{{ text }}</p>
      <div class="row my-2">
        <a
          v-if="appLink"
          class="btn btn-primary col-4 offset-2"
          target="_blank"
          :href="appLink"
        >Check App</a>
        <a v-if="githubLink" class="col-4" target="_blank" :href="githubLink">
          <LogoGithubIcon w="40px" h="40px"></LogoGithubIcon>
        </a>
      </div>
      <div class="tags">
        <p v-for="tag in tags" class="card-text text-muted">#{{ tag }}</p>
      </div>
    </div>
  </div>
</template>
<script>
import LogoGithubIcon from "vue-ionicons/dist/logo-github.vue";
export default {
  name: "card",
  components: {
    LogoGithubIcon
  },
  props: {
    imgPath: {
      type: String,
      required: true
    },
    title: {
      type: String,
      required: true
    },
    text: {
      type: String,
      required: true
    },
    appLink: {
      type: String,
      default: null
    },
    githubLink: {
      type: String,
      default: null
    },
    tags: {
      type: Array,
      required: true
    }
  },
  data() {
    return {
      src: require(`../assets/${this.imgPath}`)
    };
  }
};
</script>

新しいものを作成するたびにポートフォリオの方も修正して、buildして、pushして....みたいなことをしていて、結構面倒でした。

手動でビルドしないといけない

GitHubPagesを使っていたので、決まったブランチの決まったディレクトリにpushさえすればデプロイはできたのですが、手元でwebpackを使ってbuildしてあげる必要がありました。よくコマンドを忘れてhistoryから探したり、「あれ?ビルドしたっけ?」となることがあったりして、少し不便でした。

そもそもGatsby.jsとは何か

公式サイト

Gatsby is a React-based open source framework for creating websites and apps. Build anything you can imagine with over 2000 plugins and performance, scalability, and security built-in by default.

  • なんか早い
  • なんかプラグインで色々簡単に拡張できる
  • なんかコマンドポチポチしたらサイトができてる

とにかく導入が簡単!コマンドを打つだけで複雑な設定は隠蔽してくれます!!というインターネットの声をよく目にしました。こういうなんでもやってくれる系のフレームワークはお節介が過ぎると敬遠してしまう場面もありますが、Webフロントエンドに精通していない私にとっては手取り足取りして欲しい!ということでGatsbyを導入してみることにしました。

プロジェクトの作成方法など、Gatsbyのコマンドに関する話はここでは割愛させていただきます。(インターネットにたくさん良記事が転がっていた記憶があります)

構成

ある程度長くメンテナンスするだろうなということとお勉強のためにTypeScriptを選びました。Reactを使うのはGatsby.jsの場合避けられません。

ホスティングはこれまでも使っていて使い慣れているGitHub Pagesを採用しました。また、デプロイも親和性の高いGitHub Actionsで行うことにしました。

GitHubリポジトリ情報にGraphQL経由でアクセスする

ldd/gatsby-source-github-api を使います。

READMEの通りに設定していきます。まずは gatsby-config.jsプラグインを追加します。

{
  resolve: "gatsby-source-github-api",
  options: {
    token: process.env.GITHUB_API_TOKEN,
    graphQLQuery: `
    query ($nFirst: Int, $q: String!) {
      search(query: $q, type: REPOSITORY, first: $nFirst) {
        edges {
          node {
            ... on Repository {
              id
              name
              description
              url
            }
          }
        }
      }
    }`,
    variables: {
      q: "topic:portfolio user:odmishien",
      nFirst: 10,
    },
  },
}

今回は portfolio というtopicが指定された 私のリポジトリを10件取ってくる設定になっています。これで人様に見せてもいいと思うリポジトリには portfolio というtopicを付けておくだけでOKです。

そしてindexページのtsxファイルの中でGraphQLでデータにアクセスします。

import { IndexQuery } from "../../types/graphql-types"

interface IndexProps {
  data: IndexQuery
}

const IndexPage: React.FC<IndexProps> = ({ data }) => {
  const repos = data.githubData?.data?.search?.edges
   ... // 省略
}
export const query = graphql`
  query Index {
    githubData {
      data {
        search {
          edges {
            node {
              id
              name
              description
              url
            }
          }
        }
      }
    }
  }
`

ひとまずこんな感じでリポジトリとそのdescriptionを表示させることに成功。

スクリーンショット 2020-12-03 23.59.23.png

GraphQLのクエリの構築に結構苦戦しましたが、GitHub GraphQL API Explorer で型の補完を効かせながら試行錯誤しました。

また同じようなことを自分の技術ブログ(はてなブログ)のRSSフィードから生成するみたいなことをmottox2/gatsby-source-rss-feedを使って実現しています。

GraphQLのスキーマをTypeScriptの型に落とし込む

GraphQLを使ってAPIなどのレスポンスを使えるのは便利なのですが、その構造を型に落とし込むのを手動でやっていくのはちょっと骨が折れます。先ほど触れなかったのですが

import { IndexQuery } from "../../types/graphql-types"`

としている部分があったことにお気付きでしょうか。これは私が定義したのではなく、 gatsby-plugin-graphql-codegen を使って、型定義ファイルを自動で生成しています。

またも、gatsby-config.jsの中にプラグインを書いていきます。

{
      resolve: "gatsby-plugin-graphql-codegen",
      options: {
        fileName: `types/graphql-types.d.ts`,
      },
}

これで types/graphql-types.d.ts というファイルに gatsby build されるたびに発行しているqueryに応じて、型定義が追加されていきます。

例えば私のポートフォリオの場合、index.tsの末尾に以下のようなIndexという名前のクエリを発行しています。

export const query = graphql`
  query Index {
    ...省略
    }
`

ビルドしてみると、この名前に Query というsuffixが付いた IndexQuery という型が定義されているのが確認できます。便利!

GitHub Actionsを使って決まった時間に自動デプロイ

GitHubRSSから欲しい情報を取ってくることはできますが、コンテンツの更新はビルドし直さないとできません。そこでmasterにpushがあった時と毎日夜0:00にビルドしてGitHub PagesにデプロイするというActionを仕込みます。これはkentaromさんの記事がとても参考になりました

name: Deploy

on:
  push:
    branches:
      - master
  schedule:
    - cron: "0 0 * * *"

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1

      - name: setup node 14.x
        uses: actions/setup-node@v1
        with:
          node-version: "14.x"

      - name: install
        run: npm install

      - name: build
        run: npm run build
        env:
          GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          cname: your.domain.com # 独自ドメインを利用したい場合 

secrets.GITHUB_TOKEN の方はActionsのworkflow内で使える自動で生成されたトークンなのでsecretsに登録する必要がありません1。便利!

peaceiris/actions-gh-pagesgithub_tokenを指定しておくだけで、デフォルトで gh-pagesブランチpublicディレクトリの内容をデプロイしてくれます。カスタマイズしたかったらこれらの値をyamlに書いてあげてください。

また、独自ドメインを利用したい場合は cname フィールドに利用したいドメインを指定しておきます。これをしないとデプロイの度にCNAMEファイルが作られないので、毎回デフォルトのURL(https://username.github.io)が指定されてしまいます。

まとめ

スクリーンショット 2020-12-02 16.52.18.png

無事読み込みも速くなって、オールグリーン!ビルドやデプロイのことはGitHub Actionsが全て担ってくれるので、私がやることはなくなりました。サイコー!Webフロントエンドのことをあまりよく分かっていない私でもある程度のセッティングができたので便利ですね、Gatsby!!

改善点としては

  • npm install を毎回やってしまっているので、キャッシュする
  • GraphQLについてかなりフィーリングで触っているので勉強して無駄のないクエリを書く
  • 掲載するコンテンツを増やす

などでしょうか。随時手を入れていこうと思います。

最後までお読みいただきありがとうございました。

参考になった記事