Next.jsでブログ作成

やりたい事
Next.jsでブログを作成したい。
低コストとなると選択肢はサーバーレスになる為、S3 + CloudFront環境で構築。
静的ファイルはS3に保存され、CloudFrontがキャッシュを配信してくれる為費用を抑えられる。
ドメインはお名前ドットコムで初期費用無料。SSL化もRoute 53が対応してくれる。
ブログはMarkDown形式ファイルを記事毎に作成。
このブログサイトは下記の構成で作成されています。
目次
事前準備
Node環境構築
nvm => node => npm => npxという依存関係になっているので、nvmの最新をインストール。
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash
nvm install stable
これでnpxに至るまで全てインストールされる
AWS credentials
簡単にS3にファイルをデプロイ出来るように準備
公式手順に則って、AWS CLIをインストール
aws configureで下記を環境変数に登録
Access Key: xxxxxxxx
Secret Key: xxxxxxxx
Region: ap-northeast-1
Ouput format: json
サイトデプロイ
Linux上でプロジェクト作成
プロジェクト名はドメイン取得してから作成すると、名称一致できるのでおすすめ。
公式手順に記載されているコマンドを全て実行して、tailwind & typescriptのプロジェクト作成。
ブログモードなら下記のように改変する。
npx create-next-app@latest --example blog-starter xxx --typescript --eslint
npm install --save eslint eslint-config-next
最後にnpm install
Github
ブラウザ上でsign inしてレポジトリ作成
- Private
- README.md作らない
git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/xxx/xxx.git
git push origin main
ビルド準備
package.jsonのscriptsのbuild「next build => next build && next export」に書き換える。
SSGはImageタグがあるとnext export時にエラーを吐くので、Image => imgに置き換える。Imageタグ内のpriorityクラスも対応していないので削除。
そのままだとビルド時にエラーを吐くので、.eslintrc.jsonに下記を追加
{
"extends": ["next", "next/core-web-vitals"],
"rules": {
"@next/next/no-img-element": "off"
}
}
next.config.jsに
swcMinify: true,
trailingSlash: true
を追加して、npm run buildでビルド
S3 + CloudFront
ドメインと同一名で東京リージョンにS3バケットを作成して、
ACL有効でオブジェクト所有者: オブジェクトライター
ブロックパブリックアクセス設定を一旦オフにしてバケットを作成。
プロパティタブの静的ウェブサイトホスティングを有効にする
静的ウェブサイトをホストする。
インデックスドキュメント: index.html
エラードキュメント: 404/index.html
これで403 Forbiddenになる
S3 URLをコピーしておく。http://{your-domain}.s3-website-ap-northeast-1.amazonaws.com
CloudFrontでディストリビューションの作成。
オリジンドメイン: S3の静的ホスティングのS3 URLを入力
名前: 自動的に入力されるが、({your-domain}.s3-website-ap-northeast-1.amazonaws.com)
選択ではなく直接入力する。
プロトコルポリシーをHTTPS onlyにする。
ディストリビューションを作成。
ディストリビューション名をコピーしておく。https://{your-distribution}.cloudfront.net
オリジンタブを開いて、カスタムヘッダーを設定する。
Referer: https://{your-distribution}.cloudfront.net/ *
S3のバケットポリシーを下記にすると、CloudFront URLでアクセス可能、S3が403 Forbiddenになる
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::{your_bucket_name}/*",
"Condition": {
"StringLike": {
"aws:Referer": "https://{your-distribution}.cloudfront.net/*"
}
}
}
]
}
ビューワーのキャッシュ時間を変えたい場合はビヘイビアを編集してキャッシュポリシーを弄る。
ドメイン
A8.netにログインして、セルフバックでお名前ドットコムのドメインを購入。
Route53でドメイン設定
Route53で左のメニューからホストゾーンを選択し、ドメイン名を入力してパブリックホストゾーンの作成。
ホストゾーン上に生成された4つの「値/トラフィックのルーティング先」を使う。
お名前.comを開いて、ドメイン設定でネームサーバーの設定からネームサーバーの変更を選択。
他のネームサーバーを利用を選択。末尾のドットを削除して、4つの「値/トラフィックのルーティング先」を貼る。
AWSのCertificate Managerで証明書のリクエスト。必ず米国東部 (バージニア北部) リージョン us-east-1にする。
完全修飾ドメイン名にドメインを入力して、証明書リクエスト。
保留中の検証ステータスだが、開くとRoute53でDNSレコードを作成ボタンがあるので、作成する。
CloudFrontに戻り、一般タブの設定で、代替ドメイン名を設定して、カスタム証明書の選択肢に追加されている証明書を選択。
最後に、Route53のホストゾーンからレコードの作成を選択。 レコード名を空欄、レコードタイプは「A-ipvアドレスと…」を選択して、エイリアスのトグルをONにする。 CloudFrontのディストリビューションへのエイリアスを選択して、ディストリビューション名を入力してレコード作成して完了。
SEO対策
pages/posts/[slug].tsxに下記の内容を追加する。
<Head>
<title>
{post.title}
</title>
<meta name="description" content={post.excerpt} />
<meta property="og:title" content={post.title} />
<meta property="og:image" content={post.ogImage.url} />
<meta property="twitter:image" content={post.ogImage.url} />
<meta property="og:type" content="article" />
</Head>
また、下段にあるgetStaticPropsの引数にもexcerptを追加して、値渡しする。
components/layout.tsxで、components/meta.tsxを呼び出しているので、
meta.tsx上にあるmeta name=descriptionとmeta property=og:imageは削除しておく。
代わりに、canonicalを記載してURL正規化する。
<link rel="canonical" href="https://xxxxxx.com" />
Markdownをカスタマイズ
CSSをカスタマイズ
components/markdown-styles.module.cssを編集する。
見出しにID要素を自動追加
remark-slugというモジュールをインストールする。
npm install remark-slug
lib/markdownToHtml.tsを下記の内容に変更する。
import { remark } from 'remark'
import remarkSlug from 'remark-slug'
import html from 'remark-html'
export default async function markdownToHtml(markdown: string) {
const result = await remark()
.use(html)
.use(remarkSlug)
.process(markdown)
return result.toString()
}