
AWS S3와 Github Actions를 사용하여 여태까지 개발한 애플리케이션을 사용자가 사용할 수 있도록 배포해보자.
아마존 웹서비스의 클라이언트 스토리지 서비스로, 웹 서비스 및 애플리케이션에 대한 데이터 저장, 백업 및 복원, 정적 웹 호스팅 등 다양한 서비스를 제공한다.
사이트
S3는 정적 웹 사이트를 호스팅하기 위한 기능을 제공한다.
웹 호스팅(Web hosting)
웹 사이트나 웹 애플리케이션을 인터넷을 통해 액세스할 수 있도록 하는 서비스이다.
사용자가 웹 페이지를 올리고 관리할 수 있는 웹 서버를 제공하고, 이를 통해 사용자는 인터넷 브라우저를 통해 해당 웹 사이트에 접속할 수 있다.
Github에서 호스팅되는 지속적 통합(CI, Continuous Integration) 및 지속적 배포(CD, Continuous Deployment) 서비스이다.
GitHub 리포지토리 내에서 코드 변경 사항을 트리거로 하여 자동화된 소프트웨어 개발 워크플로우를 실행할 수 있게 해준다.
공식 문서
CI/CD 워크플로우를 설정하여 코드가 변경될 때마다 빌드, 테스트 및 배포와 같은 지속적 통합 및 배포 워크플로우를 설정할 수 있고 특정 이벤트(예: pull request 생성, push 등)가 발생할 때 워크플로우를 트리거할 수 있다.
AWS S3 + Github Actions
- GitHub Actions를 사용하여 빌드하고 AWS S3에 배포하면 손쉽게 웹 사이트를 호스팅할 수 있다.
- GitHub Actions를 사용하여 CI/CD 워크플로우를 설정하면 코드가 변경될 때마다 자동으로 빌드 및 테스트를 수행하고, 그 결과를 AWS S3에 업로드하여 배포할 수 있다.
- AWS S3는 대용량의 정적 파일을 비교적 저렴한 비용으로 호스팅할 수 있고 GitHub Actions는 GitHub에 호스팅되어 있으며 무료로 제공되는 한도 내에서 사용할 수 있다. 따라서 이 두 서비스를 함께 사용하면 비용을 절감하면서도 안정적인 웹 호스팅 및 CI/CD 환경을 구축할 수 있다.
해보자.
먼저 AWS에 계정을 만들고, AWS IAM 대시보드에서 사용자를 생성한다.

사용자 그룹도 생성해주고 해당 그룹에 AmazonS3FullAccess 권한을 추가해준다.


그룹에 위에서 생성한 사용자를 추가해준다. (그룹 이름 클릭)
그리고 사용자를 클릭하여 AWS accces key와 secret key를 발급 받는다.(보안에 신경써서 잘 보관해야 함)
이제 이 키를 Github Actions에 등록해준다.
프로젝트 깃허브 리포지토리의 Settings 탭의 Secrets and variables -> Actions의 New repository secret 버튼을 클릭해 키를 등록할 수 있다.
AWS accces key는 AWS_ACCESS_KEY_ID 값으로 등록하고,
secret key는 AWS_SECRET_ACCESS_KEY 값으로 등록한다.

이제 CI/CD 워크플로우를 정의할 YAML 형식 파일인 .yml 파일을 작성해준다.
참고로 YAML은 사람이 쉽게 읽고 쓸 수 있는 데이터 직렬화 언어로, 데이터를 계층적 구조로 표현하는 데 사용된다고 한다.
특히 소프트웨어 설정 파일, CI/CD 워크플로우 정의, 구성 파일 등에 사용된다.
예를 들어, Github Actions에서 CI/CD 워크플로우를 정의할 때 .yml파일을 사용한다.
먼저 해당 깃허브 레포지토리의 Actions 탭에 들어가서 set up a workflow yourself를 클릭해준다.

그러면 바로 이렇게 프로젝트의 .github 폴더 안 workflows 폴더에 .yml 파일을 작성할 수 있게 된다.
코드 에디터를 통해서도 작성 가능하다.

워크플로우 코드는 다음과 같이 작성한다.
name: client
on:
push:
branches:
- deploy # 브랜치명
jobs:
build:
runs-on: ubuntu-20.04
steps:
# 소스 코드를 가져옴
- name: Checkout source code.
uses: actions/checkout@v2
# 의존성 설치
- name: Install dependencies
run: yarn
# 빌드
- name: Build
run: yarn build
# AWS CLI 설치 확인
- name: SHOW AWS CLI VERSION
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_EC2_METADATA_DISABLED: true
run: aws --version
# 로컬에서 빌드된 파일을 S3 버킷으로 동기화 -> 호스팅
- name: Sync Bucket
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_EC2_METADATA_DISABLED: true
run: |
aws s3 sync \
--region ${{ AWS 리전 }} \
${{ 빌드 폴더 이름 ex) build 혹은 dist}} s3://${{S3 버킷이름}} \
--delete
이 워크플로우는 각 단계가 성공적으로 실행될 때마다 다음 단계로 진행된다.
각 단계는 필요한 환경 변수를 설정하고 AWS 자격 증명이 저장된 GitHub Secrets(앞서 저장했던 AWS key 확인)에서 해당 정보를 가져와 사용한다.
참고로 S3 버킷으로 동기화 부분 명령어는 예를 들어 다음과 같이 작성할 수 있다.
run: |
aws s3 sync \
--region ap-northeast-2 \
dist s3://bucketname \
--delete
해당 워크플로우 파일을 작성하고 커밋 후 푸시하면, 자동으로 워크플로우가 실행된다.
이후 새로운 push가 실행될 때마다 자동으로 배포가 진행된다.
워크플로우의 실행이 완료되면 초록색으로 표시되고 배포가 완료된다.
배포된 링크는 S3의 버킷 -> 속성 탭에서 스크롤을 맨 아래로 내리면 보이는 '정적 웹 호스팅' 칸에서 확인할 수 있다.
위에 작성되어 있는 대로 하면 오류가 발생하지 않는다.
하지만 나는 처음으로 .yml 파일을 올리고 워크 플로우가 실행 되었을 때 오류가 당연히 발생하였다.


문제
Access Denied 라는 문구가 표시된 걸 보니 해당 오류는 권한 설정에 관한 문제라는 걸 알 수 있었다.
사실 처음에 AWS IAM에서 사용자 그룹을 설정해줄 때 그룹에 대한 권한을 부여해주지 않아서 발생한 문제였다.
해결
위에 적혀있듯이 사용자 그룹 권한에 AmazonS3FullAccess을 추가해서 해결해주었다.
이후 다시 실행시키니 워크플로우가 정상적으로 실행되었다.
문제:
배포에 성공한 뒤 생성된 링크를 통해 들어갔다. 하지만 화면에 아무 것도 나오지 않아 콘솔 창을 열어보니 Firebase API Key 관련 에러가 찍혔다.
현재 Firebase API 키는 보안을 위해 환경 변수 파일인 .env 파일에서 관리하고 있으며, .gitignore 파일에 .env 파일을 추가하여 깃허브에 파일이 올라가지 않고 있다.
따라서 깃허브에 올라간 코드, 즉 배포된 코드에는 API 키가 포함되지 않은 코드이므로 배포된 사이트에서 이 키가 유효하지 않다(키를 찾을 수 없다)는 에러가 발생한 것이다.
해결
1. 먼저 키 값을 하드코딩으로 넣으면 안될까? 하는 생각을 했다.
보안상의 이유로 따로 환경변수 파일을 만들어 키 값을 관리하는 것은 알았지만 이에 대해 더 자세히 찾아보게 되었다.
제일 먼저 이와 관련된 스택오버플로우 질문을 찾아볼 수 있었다.
질문의 답변에서는 apiKey는 Google 서버에서 Firebase 프로젝트를 식별하는 것으로, 다른 사람이 이를 알아도 보안 위험이 되지 않는다는 답변을 볼 수 있었다.
덧붙여 Firebase 서버 측의 보안 규칙을 사용하여 데이터 수정을 제한하면 된다고 했다.
그래서 결론은.. 정말 하드코딩으로 키 값을 넣어줬다.
하지만 키를 노출하지 않고도 에러를 해결하는 방법이 있을 것 같았다.
일반적으로 API 키를 노출하는 것은 보안상 위협이 될 수 있기 때문에 다른 방법이 있어야만 했고 이를 찾아보았다.
2. 역시 방법이 있었다.
Github Actions Secrets를 통해 해결했다.
이는 GitHub 저장소에서 안전하게 민감한 정보를 저장하고 관리하는 데 사용된다.
API 키, 비밀번호, 인증 토큰 등과 같은 보안 정보를 저장할 수 있다.
이러한 Secrets는 저장소에 직접 노출되지 않으며, GitHub Actions 워크플로우에서만 사용된다.
앞서 AWS access key를 등록한 곳에서 똑같이 등록하면 된다. (VITE_APP_API_KEY)

그리고 워크플로우 파일(.yml)의 steps에 환경 변수 파일을 생성하는 명령어를 추가한다.
steps:
- name: Checkout source code.
uses: actions/checkout@v2
# .env.production 파일 생성
- name: Create env production file
run: |
echo "VITE_APP_API_KEY=$VITE_APP_API_KEY" >> .env.production
env:
VITE_APP_API_KEY: ${{ secrets.VITE_APP_API_KEY }}
- name: Install dependencies
run: yarn
...
이를 통해 배포 시, 이 .env.production 파일을 통해 API 키에 접근이 가능하게 된다.
이제 사이트에서 해당 에러가 사라진 것을 확인한 내가 해야 할 일은?
새 Firebase 프로젝트 생성하기! ㅎ 
이미 API 키는 기존 코드에 노출되어있는 상태였기 때문에 아무래도 새 프로젝트를 만들어 새로운 API를 발급받아야겠다고 생각했다.
그래서 새로운 프로젝트를 만들고 규칙과 색인, 키 등록 등 다 다시 설정해주었다네요.
(3/5 업데이트) CloudFront도 함께 사용하기로 했다.
AWS의 글로벌 콘텐츠 전송 네트워크(CDN) 서비스로, 사용자가 웹 콘텐츠를 안정적이고 빠르게 제공할 수 있도록 도와준다.
CloudFront는 전 세계에 분산된 엣지 로케이션 네트워크를 통해 사용자에게 콘텐츠를 가까운 위치에서 전송하여 로드 타임을 최적화하고 대역폭을 줄인다.
한국 서버에 호스팅된 웹 사이트가 있다고 가정해보자.
이 웹 사이트는 한국 이용자에게는 빠르게 로드되지만, 미국에 있는 사용자에게는 로드 속도가 느릴 수 있다.
이를 해결하기 위해 CloudFront를 설정하여 미국, 유럽, 아시아 등 다양한 지역에 엣지 로케이션을 설정한다. 사용자가 웹 사이트에 접속하면, 사용자의 위치에 가장 가까운 엣지 로케이션에서 콘텐츠를 제공받게 된다.
예를 들어, 미국에 있는 사용자가 이 웹 사이트에 접속하면, CloudFront는 미국에 있는 엣지 로케이션에서 해당 콘텐츠를 제공한다.
이렇게 함으로써 콘텐츠의 전송 거리를 줄이고 네트워크 지연을 최소화하여 빠르고 안정적인 콘텐츠 전송을 제공할 수 있다.
AWS S3 + AWS CloudFront
- 객체 스토리지 서비스로서 매우 안정적이고 확장 가능한 저장소를 제공하는 Amazon S3와 전 세계에 분산된 엣지 위치를 통해 콘텐츠를 제공하는 CloudFront를 결합하면 웹 콘텐츠의 빠른 전송 및 로드 타임 최적화를 달성할 수 있다.
- Amazon S3는 스토리지 및 요청에 대한 요금을 부과하고, CloudFront는 데이터 전송과 엣지 캐싱에 대한 요금을 부과한다. 따라서 S3와 CloudFront를 함께 사용하면 비용을 절약할 수 있다. 특히, CloudFront를 통해 캐싱된 콘텐츠에 대한 요청을 S3로 전달하지 않고 엣지 위치에서 처리함으로써 데이터 전송 비용을 줄일 수 있다.
- Amazon S3는 고급 보안 기능을 제공하여 데이터를 보호하고, CloudFront는 SSL/TLS 암호화 및 서명된 URL을 통해 콘텐츠의 보안을 강화한다. 이 두 서비스를 함께 사용하면 웹 콘텐츠를 안전하게 제공할 수 있다.
해보자.
먼저 S3에서 등록한 버킷의 퍼블릭 엑세스 차단 설정을 '모든 퍼블릭 엑세스 차단'으로 바꿔준다. s3로 바로 접속하는 것이 아니라 cloudfront를 통해 접속할 것이기 때문에 액세스를 차단해줘도 된다.
CloudFront 콘솔에서 '배포 생성' 버튼을 클릭한다.
(언급해 놓은 설정 외 나머지는 디폴트값으로 설정)
원본 도메인에서 이미 생성되어 있는 s3 버킷 도메인을 선택한다.
원본 액세스는 다음과 같이 설정한다. (CloudFront에서만 접근 가능하게 설정)

Enable Origin Shield에서 '예'를 선택한 후 Origin Sheid 리전을 아시아 태평양(서울)으로 설정한다.
뷰어 프로토콜 정책은 Redirect HTTP to HTTPS 옵션으로 설정한다.
웹 애플리케이션 방화벽(WAF)은 보안 보호 활성화로 선택한다.
가격 분류는 북미, 유럽, 아시아, 중동 및 아프리카에서 사용으로 선택해주었다.
기본값 루트 객체에 index.html를 입력한다.
이제 '배포 생성' 버튼을 통해 배포를 생성해주면 새 배포를 생성했다는 알림과 함께 S3 버킷 정책을 업데이트 하라는 알림이 함께 뜬다.
'정책 복사'를 클릭하여 S3 버킷 권한 설정에 복사한 정책을 그대로 붙여넣어주면 된다.
배포된 링크는 CloudFront에서 배포 아이디 클릭 -> '세부 정보의 배포 도메인 이름'에서 확인할 수 있다.