S3와 CloudFront를 이용한 NextJS 배포 및 Github Actions 배포 자동화(2024)
작성 일자 : 2024년 05월 19일
본 포스팅에서 다루는 내용
- Static Exports 방식으로 NextJS 프로젝트 빌드
- S3 버킷 생성 및 정적 웹 사이트 호스팅
- Route 53 Hosted Zone 생성
- ACM SSL/TLS 인증서 발급
- CloudFront 배포 및 도메인 연결
- Github Actions를 통한 배포 자동화
Static Exports 방식으로 NextJS 프로젝트 빌드
Static Exports는 Next.js 애플리케이션을 정적 HTML 파일로 내보낼 수 있는 기능으로, Node.js 서버 없이도 모든 정적 호스팅 서비스에서 제공할 수 있습니다. Static Exports는 빌드 시간 동안 애플리케이션의 모든 페이지를 미리 렌더링하여 각 페이지에 대한 정적 파일(HTML, CSS 및 JavaScript) 집합을 생성하는 작업이 포함됩니다. 이를 이용하면 런타임에 서버 측 렌더링이나 동적 데이터 가져오기가 필요하지 않은 사이트를 배포하는 데 유용하므로 로드 시간을 개선하고 호스팅 비용을 절감할 수 있습니다.
Deploying: Static Exports | Next.js
Next.js enables starting as a static site or Single-Page Application (SPA), then later optionally upgrading to use features that require a server.
nextjs.org
1. 먼저 NextJS 프로젝트를 생성합니다.
npx create-next-app {project-name} --typescript
2. 프로젝트 디렉토리로 이동한 후, Static Exports를 위한 next.config.mjs
파일 설정을 진행합니다.
/** @type {import('next').NextConfig} */
const nextConfig = {
output: "export",
};
export default nextConfig;
3. 저의 경우에는 마지막 과정인 Github Actions를 통해서 환경변수를 주입하는 방법을 보여드리기 위해서 src/app/pages.tsx
를 다음과 같이 수정하고, .env.local
파일을 생성하여 NEXT_PUBLIC_MESSAGE
환경변수를 추가하겠습니다.
src/app/pages/tsx
import Image from "next/image";
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<p>{process.env.NEXT_PUBLIC_MESSAGE}</p>
</main>
);
}
.env.local
NEXT_PUBLIC_MESSAGE="This message is from .env"
4. 이후 다음 명령어를 통해 프로젝트를 빌드합니다.
명령어
npm run build
빌드 과정
> thejudgementofsolomon-nextjs-deploy@0.1.0 build
> next build
▲ Next.js 14.2.3
- Environments: .env.local
Creating an optimized production build ...
✓ Compiled successfully
✓ Linting and checking validity of types
✓ Collecting page data
✓ Generating static pages (5/5)
✓ Collecting build traces
✓ Finalizing page optimization
Route (app) Size First Load JS
┌ ○ / 136 B 87.2 kB
└ ○ /_not-found 875 B 87.9 kB
+ First Load JS shared by all 87.1 kB
├ chunks/23-9cefcdc0615b541e.js 31.6 kB
├ chunks/fd9d1056-2821b0f0cabcd8bd.js 53.7 kB
└ other shared chunks (total) 1.85 kB
○ (Static) prerendered as static content
5. 빌드가 완료되면 out
디렉토리에 정적 파일이 생성됩니다.
S3 버킷 생성 및 정적 웹 사이트 호스팅
정적 웹 사이트 호스팅은 웹 서버 없이 정적 콘텐츠(HTML, CSS, JavaScript, 이미지 및 비디오)로 웹 사이트를 호스팅할 수 있게 해줍니다. AWS에서 제공하는 S3 버킷을 사용하면 버킷을 생성하고 정적 웹 리소스를 업로드한 후 공개 엑세스 권한을 설정하여 정적 웹 사이트를 호스팅할 수 있습니다. 이후 S3 엔드포인트를 통해 웹 사이트에 접근할 수 있으며 CloudFront를 통해 사용자 정의 도메인 및 SSL/TLS 인증서를 추가하고 Route 53을 통해 도메인 관리를 할 수 있습니다.
우선 S3 버킷을 생성하고 정적 웹 사이트 호스팅을 설정해보겠습니다.
S3 버킷 생성
1. AWS Management Console에 로그인한 후 Amazon S3 서비스로 이동합니다.
2. Create bucket 버튼을 클릭합니다.
3. 아래와 같이 버킷 설정을 진행합니다.
- Bucket name : 생성할 버킷 이름
- Object ownership : ACLs disabled
- Block all public access : 체크 해제 및 하단 유의사항 확인 체크
- Bucket versioning : Disable
- Default encryption : 기본 설정
4. 이후 Create bucket 버튼을 클릭하여 버킷을 생성합니다.
정적 웹 사이트 호스팅 설정
1. 생성된 버킷을 클릭한 후 Object 탭의 Upload 버튼을 클릭하여 out
디렉토리에 생성되었던 정적 파일을 업로드합니다.
2. 버킷의 Properties 탭으로 이동한 후, 최하단의 Static website hosting을 클릭합니다.
3. 아래와 같이 Static website hosting을 활성화하고 Index document
와 Error document
를 설정합니다.
- Static website hosting : Enable
- Index document : index.html
- Error document : 404.html
4. Save changes 버튼을 클릭하여 설정을 저장합니다.
5. 생성된 엔드포인트로 접속하기 이전에 버킷 정책을 수정하여 모든 사용자에게 버킷에 대한 읽기 권한을 부여하겠습니다. Permissions 탭으로 이동한 후, Bucket policy의 Edit 버튼을 클릭합니다.
6. 아래와 같이 버킷 정책을 수정합니다. {bucket-name}
은 생성한 버킷 이름으로 변경합니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": ["s3:GetObject"],
"Effect": "Allow",
"Resource": "arn:aws:s3:::{bucket-name}/*",
"Principal": "*"
}
]
}
7. Save changes 버튼을 클릭하여 버킷 정책을 저장합니다. 이후 생성된 엔드포인트로 접속하여 정적 웹 사이트를 확인할 수 있습니다.
Route 53 Hosted Zone 생성
Route 53은 AWS에서 제공하는 클라우드 DNS 서비스로, 도메인을 등록하고 관리할 수 있습니다. Route 53을 사용하면 도메인을 등록하고 관리할 수 있으며, 도메인에 대한 DNS 레코드를 생성하여 다양한 서비스에 연결할 수 있습니다.
우선 Route 53 Hosted Zone을 생성해보겠습니다.
Hosted Zone 생성
1. AWS의 Route 53 서비스로 이동합니다.
2. Create hosted zone 버튼을 클릭합니다.
3. 아래와 같이 Hosted zone을 생성합니다.
- Domain name : 도메인 입력
- Type : Public hosted zone
NS 레코드를 등록한 도메인에 추가
NS 레코드란 네임 서버 레코드로, 도메인을 호스팅하는 네임 서버의 정보를 가지고 있습니다. Route 53에서 생성한 Hosted Zone의 NS 레코드를 등록한 도메인에 추가하여 Route 53을 통해 도메인을 관리할 수 있습니다.
1. 생성한 Hosted Zone의 NS 레코드를 확인합니다.
2. 도메인을 등록한 도메인 등록 사이트로 이동한 후, NS 레코드를 추가합니다.(저는 가비아를 사용하였습니다)
ACM SSL/TLS 인증서 발급
SSL/TLS 인증서는 웹 사이트와 사용자 간의 통신을 암호화하여 보안을 강화하는 역할을 하며, HTTPS 프로토콜을 사용할 수 있게 해줍니다. AWS에서 제공하는 ACM(AWS Certificate Manager)을 사용하면 무료로 SSL/TLS 인증서를 발급받을 수 있습니다.
지역 설정
CloudFront에서 SSL 인증서를 사용하기 위해서는 ACM 인증서가 us-east-1
지역에 있어야 합니다. AWS Management Console에서 오른쪽 상단의 지역을 US East (N. Virginia)로 변경합니다.
SSL/TLS 인증서 발급
1. AWS Management Console에서 ACM 서비스로 이동합니다.
2. Request a certificate 버튼을 클릭합니다.
3. 아래와 같이 인증서를 요청합니다.
- Certificate type : Request a public certificate
- Domain names : 도메인 입력
- Validation method : DNS validation
- Key algorithm : RSA 2048
4. Request 버튼을 클릭하여 인증서를 요청하게 되면, Certificates
페이지의 우측 상단에 View cerfiticate 버튼이 활성화됩니다.
5. View certificate 버튼을 클릭하고, Domains 탭의 Create records in Route 53 버튼을 클릭하여 CNAME 레코드를 생성하겠습니다.
6. 자동으로 설정된 CNAME 레코드를 확인하고, Create records 버튼을 클릭하여 레코드를 생성합니다.
추가된 CNAME 레코드
7. 생성된 SSL/TLS 인증서의 상태가 Issued
로 변경되면 인증서 발급이 완료된 것입니다. 인증서는 바로 발급되는 경우도 있지만, 몇 분이 걸릴 수도 있습니다.
CloudFront 배포 및 도메인 연결
CloudFront는 AWS에서 제공하는 CDN(Content Delivery Network) 서비스로, 전 세계의 사용자에게 빠르고 안정적인 콘텐츠 전송을 제공합니다. CloudFront를 사용하면 정적 파일을 캐싱하여 사용자에게 빠르게 전달할 수 있으며, SSL 인증서를 사용하여 HTTPS 프로토콜을 지원할 수 있습니다.
CloudFront 배포
1. AWS Management Console에서 CloudFront 서비스로 이동합니다.
2. Create Distribution 버튼을 클릭합니다.
3. 아래와 같이 배포를 설정합니다.
- Origin : S3 버킷을 선택한 후, 아래와 같이
User website endpoint
를 선택합니다.
- Protocols : HTTP only
- HTTP port : 80
- Name : 자동으로 생성되는 값
- Enable Origin Shield : No(변경 가능)
- Path Pattern : Default (*)
- Compress Objects Automatically : Yes
- Viewer Protocol Policy : Redirect HTTP to HTTPS
- Allowed HTTP Methods : 업로드한 서비스에 따라 설정
- Restrict Viewer Access : No
- Cache key and origin requests : Cache policy and origin request policy
- Function associations : 기본값
- Web Application Firewall (WAF) : Do not enable security protections(변경 가능)
- Price class : Use all edge ;ocations (best performance)
- Alternate domain names (CNAME) : 도메인 입력
- Custom SSL certificate : 위에서 발급한 SSL/TLS 인증서 선택
- Default root object : index.html
4. Create Distribution 버튼을 클릭하여 CloudFront 배포를 생성합니다.
도메인 연결
Route 53에서 생성한 Hosted Zone에 CloudFront의 도메인을 연결하여 사용자가 도메인을 통해 CloudFront에 접근할 수 있도록 설정하겠습니다.
1. 생성한 Hosted Zone의 Records에서 Create record 버튼을 클릭합니다.
2. 아래와 같이 레코드를 추가합니다.
- Record type : A
- Alias : Yes
- Route traffic to : Alias to CloudFront distribution
- Distribution : 생성한 CloudFront 배포 선택
3. Create records 버튼을 클릭하여 레코드를 생성합니다.
4. 이후 도메인을 통해 웹 사이트에 접속하여 정상적으로 배포되었는지 확인합니다.
Github Actions를 통한 배포 자동화
Github Actions를 사용하면 Github 저장소에 푸시되는 코드 변경 사항을 감지하여 자동으로 배포할 수 있습니다. 이를 통해 코드 변경 사항이 발생할 때마다 수동으로 배포하는 번거로움을 줄일 수 있습니다. 이번 단계에서는 Github Actions를 사용하여 S3 버킷에 정적 파일을 배포하고, CloudFront cache를 무효화하는 작업을 자동화하겠습니다.
AWS IAM 사용자 생성
먼저, Github Actions에서 AWS 서비스에 접근할 수 있는 IAM 사용자를 생성하겠습니다.
1. AWS Management Console에서 IAM 서비스로 이동합니다.
2. 좌측 메뉴에서 Users
를 클릭하고, Create user
버튼을 클릭합니다.
3. 아래와 같이 사용자를 생성합니다.
- User name : 원하는 이름 입력
4. Next를 누른 후, Attach policies directly를 선택합니다. 그리고 AmazonS3FullAccess
와 CloudFrontFullAccess
정책을 선택합니다.
5. 아래와 같은 설정을 확인 후, Create user 버튼을 클릭합니다.
생성한 IAM 사용자의 액세스 키 발급
1. 생성한 IAM 사용자를 선택하고, Security credentials
탭으로 이동합니다.
2. Access keys 섹션에서 Create access key
버튼을 클릭합니다.
3. Third-party service와 Confirmation을 체크한 후, Next 버튼을 클릭합니다.
4. Create access key 버튼을 클릭하여 액세스 키를 생성합니다.
5. 생성된 액세스 키 ID와 비밀 액세스 키를 기록해둡니다.
Github Actions Workflow 생성
1. 프로젝트 루트 디렉토리에 .github/workflows
디렉토리를 생성합니다.
2. .github/workflows
디렉토리에 deploy.yml
파일을 생성합니다.
3. deploy.yml
파일에 아래와 같이 작성합니다.
name: Next.js deployment with AWS S3 and CloudFront
on:
push:
branches: ["main"]
jobs:
cloudfront-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 21
- name: Make .env
run: |
touch ./.env
echo "${{ secrets.ENV_VARIABLES }}" > ./.env
shell: bash
- name: Build with Yarn
run: |
npm install -g yarn
yarn install --frozen-lockfile
yarn build
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Deploy to S3
run: aws s3 sync ./${{ secrets.BUILD_DIRECTORY }} s3://${{ secrets.AWS_S3_BUCKET_NAME }} --delete
- name: CloudFront Invalidate Cache
run: aws cloudfront create-invalidation --distribution-id ${{ secrets.AWS_CLOUDFRONT_DISTRIBUTION_ID }} --paths '/*'
on
: Github Actions가 실행될 이벤트를 설정합니다. 이 경우main
브랜치에 푸시되었을 때 실행됩니다.jobs
: Github Actions의 작업을 설정합니다.cloudfront-deploy
: 작업 이름을 설정합니다.runs-on
: Github Actions가 실행될 환경을 설정합니다.steps
: 작업의 단계를 설정합니다.Checkout
: Github 저장소를 체크아웃합니다.Setup Node.js
: Node.js를 서버에 설치합니다.Make .env
: Github Secrets에 저장된 환경 변수를.env
파일로 생성합니다.(Secrets에ENV_VARIABLES
로 환경 변수를 저장해야 합니다.)Build with Yarn
: 프로젝트를 빌드합니다.Configure AWS credentials
: AWS 인증 정보를 설정합니다.Deploy to S3
: S3 버킷에 정적 파일을 배포합니다.CloudFront Invalidate Cache
: CloudFront 캐시를 무효화합니다.
4. Github 저장소의 Settings > Secrets and variables > Actions 에서 아래와 같이 Repository secrets를 추가합니다.
AWS_ACCESS_KEY_ID
: 위에서 발급한 AWS Access Key IDAWS_SECRET_ACCESS_KEY
: 위에서 발급한 AWS Secret Access KeyAWS_REGION
: ap-northeast-2BUILD_DIRECTORY
: outAWS_S3_BUCKET_NAME
: 생성한 S3 버킷 이름AWS_CLOUDFRONT_DISTRIBUTION_ID
: 생성한 CloudFront의 배포 IDENV_VARIABLES
:.env
파일에 저장할 환경 변수- 예시:
NEXT_PUBLIC_MESSAGE="This message is from github actions secrets"
- 예시:
5. Github 저장소에 코드를 푸시하면 Github Actions가 실행되며, S3 버킷과 CloudFront에 정적 파일이 배포됩니다.
6. CloudFront의 캐시 무효화가 완료되면, 새로 업로드한 파일이 반영됩니다.
- Github Actions의
ENV_VARIABLES
에 저장해놓았던This message is from github actions secrets
를 불러와서 페이지에 출력되는 것을 확인할 수 있습니다.