0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AWS CDKで構築!Next.js SSGで作った静的サイトをホスティングする

Last updated at Posted at 2025-05-09

はじめに

私は最近AWS CDKコントリビューションに入門しいくつかプルリクエストを提出しています。しかし、肝心のCDK自体をがっつり触ったことがなく、エアプの状態でコントリビュートしているような状態でした。せっかくなので何かCDKで作ってみるか〜〜と思い、簡単な静的サイトをホスティングしてみました。

できたもの

私のプロフィールサイトを作りました
https://about-tttol.link/

about-me.jpg

フロントエンドはNext.js SSGを使っています。Vercelでホスティングしても良かったのですが、CDKの勉強をしたかったのであえてAWSリソースで構築しました。

インフラはS3, CloudFront, Route 53, ACMを使っています。

まずはプロフィールサイトをつくる

公開したいサイトを構築しましょう。ここは好きな技術スタックで作ればOKです。

Route 53でドメイン取得

次にRoute 53でドメインを取得しましょう。

Route 53のドメインはTLD(TopLevelDomain)によって値段が大きく異なります。.com, .jpなどの有名ドメインはお高いですが、ドメイン名に特にこだわりがなければ安価なドメインを探すことも可能です。自分が探した限りでは.linkが5ドルと一番安価だったので、こちらのTLDを使ってabout-tttol.linkというドメインを取得しました。

※記載しているドメイン価格は2025/05/09時点のものです

ACMで証明書作成

続いて、Route 53で取得したドメインを用いてSSL証明書を発行していきます。CloudFrontが用意したデフォルトのURLのままでもホスティングはできますが、やはりカスタムドメインで配信したいですよね。

以下の記事を参考にして登録しました。

インフラリソースをCDKで構築・デプロイ

Route 53とACMの証明書ができたら、他のリソースはCDKで構築していきます。
※Route 53とACMもCDKで構築しようと思いましたが、実装方法がいまいち分からず諦めました。

import * as cdk from 'aws-cdk-lib';
import * as acm from 'aws-cdk-lib/aws-certificatemanager';
import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins';
import * as route53 from 'aws-cdk-lib/aws-route53';
import * as targets from 'aws-cdk-lib/aws-route53-targets';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';

export interface AboutMeInfraStackProps extends cdk.StackProps {
  domainName: string | undefined;
  hostedZoneId: string | undefined;
}

export class AboutMeInfraStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: AboutMeInfraStackProps) {
    super(scope, id, props);

    // Validate required parameters
    if (!props.domainName) {
      throw new Error('domainName is required');
    }
    if (!props.hostedZoneId) {
      throw new Error('hostedZoneId is required');
    }

    const hostedZone = route53.HostedZone.fromHostedZoneAttributes(this, 'HostedZone', {
      hostedZoneId: props.hostedZoneId,
      zoneName: props.domainName,
    });

    // S3 bucket for static assets
    const s3Bucket = new s3.Bucket(this, 'AboutMeInfraBucket', {
      bucketName: `about-me-infra-${this.account}`,
      publicReadAccess: false,
      blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
      removalPolicy: cdk.RemovalPolicy.DESTROY,
      autoDeleteObjects: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
    });

    const certificateArn = process.env.ACM_CERTIFICATE_ARN || '';
    const certificate = acm.Certificate.fromCertificateArn(this, 'Certificate', certificateArn);

    // CloudFront distribution with custom domain
    const distribution = new cloudfront.Distribution(this, 'AboutMeDistribution', {
      defaultBehavior: {
        origin: origins.S3BucketOrigin.withOriginAccessControl(s3Bucket),
        viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
        cachePolicy: cloudfront.CachePolicy.CACHING_OPTIMIZED,
        compress: true,
      },
      domainNames: [props.domainName],
      certificate: certificate,
      defaultRootObject: 'index.html',
      errorResponses: [
        {
          httpStatus: 404,
          responseHttpStatus: 200,
          responsePagePath: '/index.html',
        },
        {
          httpStatus: 403,
          responseHttpStatus: 200,
          responsePagePath: '/index.html',
        },
      ],
    });

    // Create A record for the domain
    new route53.ARecord(this, 'AliasRecord', {
      zone: hostedZone,
      target: route53.RecordTarget.fromAlias(
        new targets.CloudFrontTarget(distribution)
      ),
    });

    // Output the CloudFront URL
    new cdk.CfnOutput(this, 'CloudFrontURL', {
      value: `https://${props.domainName}`,
      description: 'Custom Domain URL',
    });
  }
}

ソースコードの全文はこちら。

S3バケットにアプリをアップロード

CDKリソースのデプロイが成功すると静的アセット用のS3バケットが作成されます。このバケットにHTMLなどの成果物をアップロードしましょう。
私はNext.jsでサイトを構築したため、npm run buildコマンドでoutディレクトリに成果物を生成し、配下のディレクトリ・ファイルを全てS3バケットにアップロードしました。この部分をCodeBuildやCodePipelineを使って自動化することも考えましたが、デプロイ頻度と構築コストを考えるとコスパが悪かったため自動化はしませんでした。

さいごに

CDKを使ったAWSリソース構築は初めてでしたが、思ったより簡単でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?