ポートフォリオサイトをGatsby.jsからNext.jsに移行した。

PR

github.com

動機

Next.jsが今後伸びていくと踏んで今のうちに技術投資をしておきたいと思い移行をしました。
Next.jsにはstatic HTML exportという機能があり、Gatsby.jsと同じ様に静的サイトを作れるのでキャッチアップにちょうど良かったという事情もありました。

nextjs.org

Nuxt.jsに投資するかも悩みましたが、

  1. Vue.jsよりReact.jsに慣れている
  2. TypeScript Full Support

という点もあり、Next.jsにしました。

移行で困った点

PRを見てもらえればわかると思うのですが、1枚だけのページという事もあって困るような事に遭遇することすらありませんでした。
styled-componentを使っていると大変という話を聞きましたが、使っていなかったこともあり、特につまずくようなところは無かったです。逆につまずいて知見を得たかったので残念

所管

Reactで静的なサイトを作る場合はGatsby.js以外にもNext.jsがあるよというお話でした。Next.jsの知見を利用出来る&貯めることが出来るので、今後静的なサイトを作る場合はNext.jsでやろうと思います。

Next.jsでfaviconを設定する

PR

github.com

Projectの構成はこのような形

$ tree src/pages public
src/pages
├── _app.tsx
├── _document.tsx
└── index.tsx
public
└── static
    └── favicon
        ├── android-chrome-192x192.png
        ├── android-chrome-512x512.png
        ├── apple-touch-icon.png
        ├── browserconfig.xml
        ├── favicon-16x16.png
        ├── favicon-32x32.png
        ├── favicon.ico
        ├── mstile-150x150.png
        └── site.webmanifest

1. publicディレクトリし、画像を置く

project直下にpublicというディレクトリを作成してfaviconの画像ファイルを置いてください。 以前はstaticというディレクトリ名でしたが、非推奨になりました。

github.com

2. pages/_document.tsxを作成し、link tagを追加する

nextjs.org

import Document, { Head, Main, NextScript } from "next/document";

export default class MyDocument extends Document {
  render() {
    return (
      <html>
        <Head>
          <link
            rel="apple-touch-icon"
            sizes="180x180"
            href="static/favicon/apple-touch-icon.png"
          />
          <link
            rel="icon"
            type="image/png"
            sizes="32x32"
            href="static/favicon/favicon-32x32.png"
          />
          <link
            rel="icon"
            type="image/png"
            sizes="16x16"
            href="static/favicon/favicon-16x16.png"
          />
          <link rel="manifest" href="static/favion/site.webmanifest" />
          <meta name="msapplication-TileColor" content="#da532c" />
          <meta name="theme-color" content="#ffffff" />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

3. build and exportして確認する

$ npm run build && npm run export

Next.jsでSVG画像をReact Componentとして描画する

@svgr/webpackを利用する。

react-svgr.com

npm install @svgr/webpack --save-dev

# or use yarn

yarn add @svgr/webpack --dev

next.config.js

module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      issuer: {
        test: /\.(js|ts)x?$/,
      },
      use: ["@svgr/webpack"],
    });

    return config;
  },
};

github.com

react-svg-loaderを利用する

github.com

npm install react-svg-loader --save-dev

# or 

yarn add react-svg-loader --dev

next.config.js

const path = require("path");

module.exports = {
  webpack(config) {
    config.module.rules.push({
      test: /\.svg$/,
      include: [path.resolve(path.resolve(), "src/images")],
      use: ["react-svg-loader"],
    });

    return config;
  },
};

個人的には@svgr/webpackのほうがスマートに見えるのでそっちを利用する。お好みで。

TerraformでAWS LambdaLayer with Python を利用する

ほぼclassmethodさんの記事通りですが

ソースコード

github.com

$ tree
.
├── README.md
├── build-layer.sh
├── iam.tf
├── lambda
│   ├── function.zip
│   └── lib.zip
├── lambda.tf
├── provider.tf
├── requirements.txt
├── src
│   └── index.py
├── terraform.tfstate
├── terraform.tfstate.backup
└── variables.tf

DeployするLambda functionのソースコード

import os
import sys
import requests
from bs4 import BeautifulSoup


def lambda_handler(event: dict, context):
    response = requests.get("http://example.com/")
    bs = BeautifulSoup(response.text)
    return bs.title.name

lambda functionでrequestsとBeautifulSoupを使いたい。そのためにlambda layerを利用する。

Lambda Layer

見るのはlambda.tfとbuild-layer.sh

data "archive_file" "function" {
  type        = "zip"
  source_file = "./src/index.py"
  output_path = "lambda/function.zip"
}

data "archive_file" "layer" {
  type        = "zip"
  source_dir  = "./lib"
  output_path = "lambda/lib.zip"
}

resource "aws_lambda_layer_version" "layer" {
  filename         = "${data.archive_file.layer.output_path}"
  layer_name       = "lib"
  source_code_hash = "${data.archive_file.layer.output_base64sha256}"

  compatible_runtimes = ["python3.6"]
}

resource "aws_lambda_function" "example_lambda" {
  function_name = "example_lambda"

  runtime          = "python3.6"
  handler          = "index.lambda_handler"
  filename         = "${data.archive_file.function.output_path}"
  role             = "${aws_iam_role.iam_role.arn}"
  source_code_hash = "${data.archive_file.function.output_base64sha256}"
  layers           = ["${aws_lambda_layer_version.layer.arn}"]
}

肝になるのはlambda functionとlayerのsourceの参照先の違い。

lambda functionのソースコードproject/src/index.pyを利用するが、 lambda layerは lib/ を利用する。pipライブラリのinstallについてはnull_resourceを利用してterraformで完結しようとしたが、うまく出来なかったので、shellファイルで実行する。何か良い方法あれば教えてほしい

#!/usr/bin/env bash

if [ -d lib/ ]; then
  rm -rf lib/
fi

echo "bundle pypi packages"
pip install -r requirements.txt -t lib/python

find lib -type f | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm

pip installで lib/python を指定しているのはlambda functionからlambda layerを参照するpathを通すため。lib/packagesなどではpathが通らないので注意。 __pycache__と.pyc、pyoを削除しているが、これはpip installをしても__pycache__配下のファイルの影響で差分が発生してしまうから。

$ sh build-layer.sh
$ terraform plan
$ terraform apply

参考

Markdown文書で画像の表示にURLを使わず埋め込む

今回から技術記事はHatenaBlogに書いていこうと思います。

忘備録

Markdownに画像を表示する場合、どうしていますか。おそらくimgurやGitHubなどに画像をアップロードしてURLを参照していると思います。

imgur.com

![airplane](https://homepages.cae.wisc.edu/~ece533/images/airplane.png)

が、機密性のある画像のURLをpublicに公開したくはないが、Markdownで書きたい場合はどうすればよいか。

A. Data URI schemeを使う

Data URI Schemeとはと言う方はWikipediaをどうぞ。

ja.wikipedia.org

何でも良いですが、画像をbase64 encodingします。

$ base64 airplane.png > airplane_base64.txt
![airplane](data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAIAAAB7GkOtAAAAB3RJTUUH0AESFgINTbbB1gAAAAlwSFlzAAC4jAAAuIwBzPa7LwAG32VJ.....)

この様に画像のURLを使わずに画像を埋め込むことが出来ました。

f:id:teitei_tk:20200425113315p:plain
markdown preview

問題点

GitHub flavored markdownではセキュリティ上に理由でbase64を利用した画像は使えません。

github.com

この点はGitHub flavoerd markdownを利用する時点である程度publicな文書だと思うのでおとなしくGitHubに画像をアップロードして使ったほうが良さそう。