안녕하세요. 스타트업에서 프론트엔드 개발을 하고 있는 inseong입니다. 😁
AWS의 S3와 CloudFront, Github OIDC를 활용해 React 프로젝트의 CI / CD 환경을 구성한 경험을 정리했습니다.
AWS에 GitHub OIDC 공급자 등록
▫️ GitHub Actions가 발급한 OIDC 토큰을 AWS에서 신뢰할 수 있도록 GitHub를 ID 제공업체로 등록합니다.
▫️ IAM → 액세스 관리 → ID 제공업체

▫️ 공급자 유형 : OpenID Connect
▫️ 공급자 URL : https://token.actions.githubusercontent.com
▫️ 대상 : sts.amazonaws.com

GitHub Actions용 IAM 역할 생성
▫️ GitHub Actions가 AWS 리소스에 접근할 때 사용할 IAM 역할을 생성합니다.
▫️ IAM → 액세스 관리 → 역할 → 역할 생성으로 이동합니다.
▫️ 신뢰할 수 있는 엔터티 유형에서 웹 자격 증명을 선택합니다.
▫️ ID 제공업체에서 token.actions.githubusercontent.com을 선택합니다. (1단계에서 생성 완료)

▫️ Github organization : 본인의 깃허브 아이디
▫️ Github Repository : 본인의 깃허브 프로젝트 저장소
▫️ Github branch : 본인의 깃허브 프로젝트 브랜치

✅ S3와 CloudFront 배포 권한 추가
▫️ 역할의 이름까지 작성하고 역할을 만들었다면 아래와 같이 설정을 합니다.
▫️ S3_BUCKET_NAME : AWS 콘솔 → S3 → 버킷
▫️ AWS_ACCOUNT_ID : AWS 콘솔 오른쪽 위의 계정 메뉴를 누르면 표시되는 12자리 계정 ID
▫️ CLOUDFRONT_DISTRIBUTION_ID : AWS 콘솔 → CloudFront → 배포 → ID
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucket",
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::<S3_BUCKET_NAME>"
},
{
"Sid": "ManageObjects",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::<S3_BUCKET_NAME>/*"
},
{
"Sid": "InvalidateCloudFront",
"Effect": "Allow",
"Action": [
"cloudfront:CreateInvalidation"
],
"Resource": "arn:aws:cloudfront::<AWS_ACCOUNT_ID>:distribution/<CLOUDFRONT_DISTRIBUTION_ID>"
}
]
}
역할의 신뢰 정책 확인
▫️ IAM → 역할 → 배포 역할 → 신뢰 관계에서 역할의 신뢰 정책을 확인합니다.
▫️ 신뢰 정책은 역할에 AWS 권한을 부여하는 정책이 아니라, 어떤 외부 주체가 이 역할을 인수할 수 있는지 정의하는 정책입니다.
▫️ AWS_ACCOUNT_ID : AWS 계정 ID
▫️ GITHUB_OWNER : GitHub 사용자 또는 조직
▫️ GITHUB_REPOSITORY : 저장소 이름
▫️ DEPLOY_BRANCH : 배포 브랜치
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<AWS_ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:<GITHUB_OWNER>@*/<GITHUB_REPOSITORY>@*:ref:refs/heads/<DEPLOY_BRANCH>"
}
}
}
]
}
역할 ARN 복사

▫️ GitHub Actions에서 생성한 IAM 역할을 사용하려면 역할의 ARN이 필요합니다.
▫️ IAM → 액세스 관리 → 역할로 이동
▫️ 생성한 GitHub Actions 배포 역할을 선택
▫️ 역할 상세 화면의 ARN 옆 복사 버튼을 선택
GitHub Secret 등록
▫️GitHub 프로젝트 저장소에서 다음 경로로 이동
▫️Settings → Secrets and variables → Actions → Repository secrets → New repository secret
▫️ Name : AWS_DEPLOY_ROLE_ARN 또는 원하는 ARN 닉네임
▫️ Secret : 앞에서 복사한 IAM 역할 ARN

워크플로 파일 생성
- push.branches : 지정한 브랜치에 코드가 반영되면 자동 실행
- workflow_dispatch : GitHub에서 수동 실행 허용
- contents: read : 저장소 코드를 Checkout할 수 있는 권한
- id-token: write : AWS 인증용 GitHub OIDC 토큰 발급 허용
- npm ci: package-lock.json 기준으로 의존성 설치
- npm run build : 운영용 정적 파일 생성
- configure-aws-credentials : OIDC 토큰으로 IAM 역할을 인수하고 임시 AWS 자격 증명 발급
- aws s3 sync : 빌드 결과물을 S3에 동기화
- create-invalidation : 배포 후 CloudFront에 남아 있는 기존 캐시 제거
- concurrency : 이전 배포가 실행 중일 때 새로운 배포가 시작되면 이전 작업 취소
name: Deploy Production
on:
push:
branches:
- <DEPLOY_BRANCH> // ex. Production
workflow_dispatch:
permissions:
contents: read
id-token: write
concurrency:
group: production-deployment // 임의로 정해도 됌
cancel-in-progress: true
jobs:
deploy:
name: Build and deploy
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build production
run: npm run build
env: // Github Action에서 참고해야할 환경 변수들
VITE_FIREBASE_API_KEY: ${{ secrets.VITE_FIREBASE_API_KEY }}
VITE_FIREBASE_APP_ID: ${{ secrets.VITE_FIREBASE_APP_ID }}
VITE_FIREBASE_AUTH_DOMAIN: ${{ secrets.VITE_FIREBASE_AUTH_DOMAIN }}
VITE_FIREBASE_MEASUREMENT_ID: ${{ secrets.VITE_FIREBASE_MEASUREMENT_ID }}
VITE_FIREBASE_MESSAGING_SENDER_ID: ${{ secrets.VITE_FIREBASE_MESSAGING_SENDER_ID }}
VITE_FIREBASE_PROJECT_ID: ${{ secrets.VITE_FIREBASE_PROJECT_ID }}
VITE_FIREBASE_STORAGE_BUCKET: ${{ secrets.VITE_FIREBASE_STORAGE_BUCKET }}
- name: Verify build output
run: | // 프로젝트의 빌드 파일 경로 (아래는 React & Vite)
test -f dist/index.html
test -d dist/assets
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
aws-region: ap-northeast-2
- name: Deploy static assets
run: | // S3 버킷명
aws s3 sync dist/assets "s3://<S3_BUCKET_NAME>/assets" \
--delete \
--cache-control "public,max-age=31536000,immutable"
- name: Deploy HTML and public files
run: | // S3 버킷명
aws s3 sync dist "s3://<S3_BUCKET_NAME>" \
--delete \
--exclude "assets/*" \
--cache-control "no-cache,no-store,must-revalidate"
- name: Invalidate CloudFront cache
run: | // CloudFront의 아이디
aws cloudfront create-invalidation \
--distribution-id "<CLOUDFRONT_DISTRIBUTION_ID>" \
--paths "/*"
- name: Deployment summary
run: |
echo "## 운영 배포 완료 🚀" >> "$GITHUB_STEP_SUMMARY"
echo "- Branch: \`${GITHUB_REF_NAME}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- Commit: \`${GITHUB_SHA}\`" >> "$GITHUB_STEP_SUMMARY"
Github 배포 자동화 테스트
▫️ 설정한 브랜치에 푸쉬 이벤트를 발생시키고 정상적으로 배포가 됐는지 확인
