
프론트엔드 어플리케이션을 배포하는 과정에서 깃허브에서 CI/CD 파이프라인을 구축하고 Amazon S3에 업로드 하는 일련의 과정에 대해서 다룬 글입니다.
크게 아래의 과정을 거쳐 배포했습니다.
AWS S3, CloudFront를 활용한 배포와 Github Actions를 통한 CI/CD 파이프라인 구축을 진행하기 이전에 간단한 개념들을 정리하고 넘어가겠습니다.
❗ CI/CD
Continuous Integration/Continuous Delivery & Continuous Deployment의 약어로 지속적인 통합/지속적인 서비스 제공, 배포를 의미한다. 간단히 요약하자면 빌드 및 테스트를 자동화하는 과정이다.
왜 CI/CD가 필요한지에 대한 궁금증이 생기실 수 있는데요, 어플리케이션을 배포 프로세스는 간단하게 다음과 같습니다.
소스코드 빌드 → 빌드된 파일을 저장소에 업데이트
이 글에서 소개하는 방법에서는 소스코드를 빌드하고, 빌드한 파일을 Amazon S3에 업로드하여 배포가 이루어지게 됩니다. 만일 하루에 변경사항이 100번, 아니 1,000번 발생했다고 가정해 보겠습니다.
소스코드를 빌드하고, 빌드된 파일을 저장소에 업데이트하는 과정을 반복해 배포하기에는 비효율적이지 않을까요? 이를 해결하기 위해 CI/CD 환경을 구축함으로 비효율적인 단순 작업을 자동화하여 개발 프로세스의 효율을 높일 수 있습니다.
여러 도구들이 있지만 해당 글에서는 Github Actions을 사용합니다.
Identity and Access ManagementSimple Storage Service(S3)는 최고의 확장성, 데이터 가용성, 보안 및 성능을 제공하는 객체 스토리지 서비스❓ 버킷
S3에서 데이터를 저장하는 기본 컨테이너
❓ CDN
Content Delivery Network or Content Distribution Network
CDN(콘텐츠 전송 네트워크)은 지리적으로 분산된 여러 개의 서버
사용자가 컨텐츠를 전달받기까지 물리적으로 긴 거리를 이동하게 되는데, 이런 지리적인 제약 없이 데이터를 빠르고 안전하게 전달받을 수 있는 기술
루트 사용자의 권한으로 작업을 진행하게 되면 민감한 정보가 유출되었을 때 위험이 크기 때문에, IAM 사용자를 생성하여 한정된 권한을 부여하고 그 권한 내에서만 루트 저장소에 접근할 수 있도록 하겠습니다.

원하는 이름으로 설정 및 해당 옵션 체크 후 IAM 사용자를 생성하고 싶음 체크

기존 정책 직접 연결 옵션을 선택하고 S3와 CloudFront를 사용할 것이기 때문에 AmazonS3FullAccess, CloudFrontFullAccess 옵션 두 개를 선택해주세요.

IAM 사용자가 생성되었으면 해당 IAM 사용자의 액세스 키를 발급받도록 하겠습니다.
사용자의 보안 자격 증명 탭에서 액세스 키를 생성해주세요.

AWS 외부에서 실행되는 애플리케이션을 선택하고 생성하겠습니다.

추후 액세스 키와 시크릿 액세스 키가 사용되는데, 생성된 액세스 키와 시크릿 액세스 키는 생성한 직후가 아니라면 다시 확인할 수 없기 때문에 복사하여 잘 보관해주세요.

이제 빌드된 파일을 업로드할 수 있는 저장소인 S3 버킷을 생성하겠습니다. 상단 검색창에 S3를 검색하여 이동해주세요.
버킷 만들기를 클릭해 버킷을 생성합니다. 원하는 버킷 이름으로 설정해주세요. 만일 사용할 도메인 주소가 존재한다면 도메인 주소와 버킷의 이름을 통일해주세요.

저희는 CloudFront로 CDN 서비스를 사용할 것이기 때문에 AWS 리전은 어떠한 위치를 사용해도 괜찮지만, S3를 통해 정적 배포를 하는 경우에는 현재 위치와 가까운 리전을 선택해주세요.

모든 퍼블릭 액세스 차단을 해제해주세요. 다음에 진행하게 될 정적 웹사이트 호스팅을 위한 버킷 정책을 수정하기 위함입니다. 나머지 옵션들은 건드리지 않고 버킷을 생성합니다.
버킷을 생성한 이후 버킷 속성으로 이동해 버킷의 Amazon 리소스 이름(ARN)을 복사해두세요.

속성 탭 최하단, 정적 웹 사이트 호스팅의 편집으로 이동해주세요

정적 웹 사이트 호스팅을 활성화 하고 아래와 같이 작성해주세요. 만일 오류 발생시 반환할 페이지가 존재한다면 해당 파일을 적어주세요. 리디렉션 규칙은 작성하지 않아도 괜찮습니다.

이 과정을 마치면 어플리케이션에 접근할 수 있는 엔드포인트가 생성됩니다. 해당 엔드포인트는 이후 진행할 CloudFront 설정에 필요하니 복사해두시기 바랍니다. 지금 상태에서 엔드포인트로 접근하면 403 Forbidden 에러가 반환되는데, 버킷 정책이 등록되지 않았기 때문에 에러가 발생합니다.
권한 탭에서 버킷 정책 편집 탭으로 이동해주세요.

웹 사이트에 대한 퍼블릭 액세스 권한을 부여하기 위해 다음 버킷 정책을 복사한 후 버킷 정책 편집기에 붙여 넣습니다.
❗️ Resource의
Bucket-Name은 여러분의 버킷 이름으로 수정해주세요.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::Bucket-Name/*"
]
}
]
}
이제 버킷 정책을 적용해주세요.
여기까지 완료하셨다면, 이제 생성된 S3 엔드포인트로 접근을 시도하면 403 에러에서 404 에러로 바뀌어있는 것을 확인할 수 있습니다. 이제 버킷에 업로드되어 있는 index.html 파일에 접근할 수 있게 되었습니다!
여기까지만 진행해도 빌드된 파일들을 올려 엔드포인트로 접근시 정상적으로 index.html 파일을 확인할 수 있지만, CD 환경을 구축하기 위해 Github Actions를 활용할 것이기 때문에 빌드된 파일들을 직접 업로드 하지 않겠습니다.
S3 버킷에 접근하기 위해서 이전에 발급받았던 IAM 키와 시크릿 키 그리고 버킷의 이름이 필요합니다.
IAM 키와 시크릿 키는 외부로 유출되면 안되는 데이터이기 때문에 해당 내용을 별도로 관리해야 합니다.
Github Actions에는 민감한 정보를 별도로 관리할 수 있는 시크릿이라는 기능을 제공합니다. 등록된 시크릿들은 워크플로우 파일(.yml)에서 사용할 수 있습니다.
해당 기능을 사용하기 위해 Github Actions를 사용할 레포지토리로 이동해주세요. 이후 해당 레포지토리에서 [Settings → Secrets and variables → Actions]로 이동합니다.

Repository secrets에 사용할 시크릿들을 등록해주세요.

저희는 AWS S3 버킷에 접근할 수 있는 권한을 부여받은 IAM 사용자의 액세스 키가 필요하기 때문에 액세스 관련된 시크릿들을 생성해주도록 하겠습니다. 시크릿의 이름은 변경하셔도 좋지만, 이후 Github Actions 워크플로우 파일을 수정해주셔야 합니다.
AWS_S3_BUCKET_NAME
버킷 이름
AWS_S3_ACCESS_KEY_ID
액세스 키
AWS_S3_SECRET_ACCESS_KEY_ID
시크릿 액세스 키
만일 어플리케이션에서 사용되는
환경변수가 존재한다면, 마찬가지로 시크릿 키로 생성해주세요. 워크플로우에서 환경변수 파일을 생성하고 환경변수들을 설정해 빌드하도록 설정할 수 있습니다.
이제 CI/CD를 위한 워크플로우 파일을 생성해 보겠습니다. Github Action을 생성하고자 하는 레포지토리에서 Actions 탭으로 이동해주세요. 이후 액션 생성 페이지로 이동합니다.

아래의 yml 파일을 작성해주세요. Create env file 워크플로우는 환경변수가 필요한 경우에만 작성하면 되고, 필요하지 않은 경우는 해당 내용을 지우고 사용하시면 됩니다.
Create env file 작업은 리눅스 명령어로 .env 파일을 생성하고, echo 명령어를 통해 환경변수_키=깃허브_시크릿에_등록한_환경변수의 형태로 .env 파일에 내용이 작성되도록 합니다.
name: Build & Deploy React App
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: "checkout branch"
uses: actions/checkout@v1
with:
ref: main
- name: "Npm Install"
run: npm install
- name: "Create env file"
run: |
touch .env
echo 환경변수_키=${{ secrets.깃허브_시크릿에_등록한_환경변수 }} >> .env
- name: "Npm Build"
run: npm run build
# 아래 Create env file 작업은 환경변수가 필요한 경우에만 사용하시면 됩니다.
- name: "Deploy to S3"
uses: jakejarvis/s3-sync-action@master
with:
args: --delete
env:
AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET_NAME }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_S3_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_S3_SECRET_ACCESS_KEY_ID }}
SOURCE_DIR: "build"
설정을 마치고 커밋 이후 yml 파일에 action이 실행되는 조건에 부합하면 Github Action이 실행됩니다. 위 워크플로우에서는 main branch에 push 이벤트가 발생시 자동으로 배포가 이루어지도록 되어있습니다.

이 제 S3 버킷에 CD 파이프라인 구축이 완료되었습니다. 이제 실제 사용자가 접속할 캐시 서버인 CloudFront 설정을 해주겠습니다. 위에서 설명드렸듯 사용자가 직접 S3 스토리지에 접근하지 않고 전세계에 분산되어 있는 서버에 캐싱되어 있는 데이터를 제공함으로 사용자에게 빠르게 데이터를 전송할 수 있도록 하기 위해 해당 서비스를 이용합니다.
CloudFront 서비스로 이동한 이후, 배포 생성으로 이동합니다.

원본 도메인은 드롭다운 메뉴에서 선택하지 않고 아까 생성해둔 S3 버킷 엔드포인트를 등록해줍니다. CloudFront가 자동으로 Origin path를 채워줍니다.

뷰어 프로토콜 정책에서 Redirect Http to HTTPS 옵션을 선택하면 모든 트래픽이 https 요청으로 서빙됩니다. 나머지 항목은 기본값으로 설정해주세요.

가격 분류 탭은 가장 저렴한 옵션으로 선택해주었습니다.

다시 CloudFront 생성 페이지로 돌아와 해당 인증서로 배포를 생성해줍니다.
이제 CloudFront 세팅은 끝났습니다. CloudFront로 배포된 도메인으로 S3 스토리지에 직접 접근하지 않고, CDN을 통해 캐싱되어 있는 데이터를 사용자에게 전송할 수 있습니다.
CloudFront 배포를 성공적으로 생성했으므로, 도메인과 CloudFront를 연결하기 위해 Route 53의 별칭 레코드를 업데이트 해주도록 하겠습니다.
Route53에서 도메인을 구매하면 알아서 호스팅 영역에 포함되지만, 외부에서 구매한 도메인이 존재한다면 AWS 서버와 연결하기 위해 호스팅 영역에 등록이 필요합니다. Route 53 서비스로 이동해주세요.
외부에서 도메인을 구매해 사용하려는 경우, 호스팅 영역 생성 버튼을 클릭해주세요.

보유중인 도메인 이름을 입력하고 호스팅 영역을 생성해주시면 됩니다.

생성된 호스팅 영역으로 이동해 레코드 중 NS 유형의 값/트래픽 라우팅 대상을 확인해 네임서버를 확인하고 Route53 또는 가비아에 네임서버를 등록해줍니다. 문자열 맨 뒤 .은 제외하고 등록해주세요.

이제 CloudFront와 구매한 도메인이 연결되도록 하는 기본 설정을 마쳤습니다.
이제 본격적으로 도메인을 CloudFront와 연결하기 위해 CloudFront 서비스로 이동해 배포된 CloudFront를 눌러 상세 페이지로 이동한 후 설정 편집으로 이동해주세요.

구매한 도메인을 사용하기 위해 대체 도메인 이름(CNAME)에 구매해둔 도메인을 입력해주세요.

대체 도메인으로 https 프로토콜을 사용하기 위해서는 사용자 정의 SSL 인증서를 연결해야 합니다. 해당하는 인증서를 발급받기 위해 인증서 요청을 눌러 Certificate Manager 서비스로 이동해주세요.

❗️ 주의사항
인증서를 요청하기 이전에 SSL 인증서를 발행하는 곳이 버지니아 북부(us-east-1)인지 확인해주세요.

위 예시처럼 작성하신 이후 인증서를 요청하면 검증 대기중인 인증서가 있을텐데요, 인증서 발급에 시간이 소요됩니다. 시간이 흘러 인증서가 발급되었다면, CloudFront에 사용자 정의 SSL 인증서를 선택해주신 이후 CloudFront 설정 편집을 마무리 해주세요.
도메인과 CloudFront를 연결하기 위한 모든 준비는 끝났습니다! 이제 본격적으로 Route 53과 CloudFront를 연결해보도록 하겠습니다. Route 53의 호스팅 영역으로 이동한 이후 미리 생성한 호스팅 영역으로 이동해주세요.

이동한 호스팅 영역에서 레코드 생성 버튼을 클릭해주세요.

레코드는 A 유형으로 선택해주시고, 저희는 CloudFront로 배포를 진행했기 때문에 트래픽 라우팅 대상은 CloudFront로 선택해주세요. 이후 연결하고자 하는 CloudFront를 선택하고 레코드를 생성해주겠습니다.

이제 브라우저로 https://도메인 엔드포인트로 요청시 잘 동작하는 것을 확인할 수 있습니다.
저처럼 AWS로 배포를 처음 진행하시는 분들에게 조금이나마 도움이 되셨길 바랍니다.
해당 글의 과정들을 진행하시면서 문제가 발생하셨거나, 오류가 있는 경우 언제든지 아래 댓글로 남겨주세요.