Astro 블로그 Vercel → AWS 이전기 (GoDaddy 도메인 사용)
개요
- 개인 도메인: angela-blog.com (GoDaddy에서 구매)
- 이전 스택: Vercel + Astro
- 현재 스택: AWS S3 + CloudFront + ACM + GoDaddy DNS + GitHub Actions
- 이유: 비용 절감 & AWS 학습 겸용
1. 왜 이전했나?
- Vercel 무료 플랜도 충분했지만, AWS로 옮겨 직접 인프라를 다뤄보고 싶었다
- 정적 사이트(Astro)는 S3 + CloudFront 조합이 최적
- 이미 GoDaddy에서 구매한 도메인이 있어서 Route53으로 이전하지 않고 그대로 연결하고 싶었다
2. 아키텍처 개요
- S3 (서울, ap-northeast-2): 정적 파일 저장
- CloudFront (글로벌 CDN): HTTPS 배포
- ACM (버지니아 북부, us-east-1): SSL 인증서
- GoDaddy DNS: 도메인 관리 (Route53 안 씀)
- GitHub Actions: main push → 자동 빌드/배포
3. 세부 과정
(1) S3 버킷 생성 & 수동 업로드
yarn build # Astro -> dist/ 생성
aws s3 sync ./dist s3://angela-blog --delete
(2) ACM 인증서 발급
- 리전: us-east-1
- 도메인: angela-blog.com, www.angela-blog.com
- 검증: GoDaddy DNS에 CNAME 추가
- 주의: GoDaddy는 Name 칸에 도메인을 자동으로 붙여줌 → 중복 입력하지 말 것
- 상태: Issued
(3) CloudFront 배포 생성
- Origin: angela-blog.s3.ap-northeast-2.amazonaws.com
- Viewer protocol: Redirect HTTP → HTTPS
- CNAMEs: angela-blog.com, www.angela-blog.com
- SSL: ACM 인증서 선택
(4) GoDaddy DNS 설정
Route53 대신 GoDaddy DNS를 그대로 사용
- www → CNAME → CloudFront 도메인 (예: dxxxx.cloudfront.net)
- 루트 도메인 (angela-blog.com) → GoDaddy Forwarding으로 https://www.angela-blog.com 301 리다이렉트
GoDaddy에서 Forwarding 기능 제공
비용 아끼려면 이 방식 추천 (Route53 쓰면 DNS 이전 비용 발생)
(5) GitHub Actions 자동 배포
IAM 사용자 생성 → S3 + CloudFront 최소 권한 정책 부여
Access key 발급 후 GitHub Secrets 등록:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_REGION
= ap-northeast-2CF_DIST_ID
= CloudFront 배포 ID
.github/workflows/deploy.yml 예시:
name: Deploy to S3 & Invalidate CF
on:
push:
branches: ["main"]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: |
echo "YARN_IGNORE_PATH=1" >> $GITHUB_ENV
corepack enable
corepack prepare yarn@4.6.0 --activate
corepack yarn install --immutable
corepack yarn build
- uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- run: aws s3 sync ./dist s3://angela-blog --delete
- run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DIST_ID }} --paths "/*"
4. 트러블슈팅
- 인증서 Pending → GoDaddy DNS에 CNAME 오타 / 중복
- ERR_CERT_COMMON_NAME_INVALID → CloudFront CNAMEs에 www 누락
- 배포 후 변경 안 보임 → CloudFront Invalidation 실행
5. 경로(Path) 접근 시 AccessDenied 문제 해결
AWS S3 + CloudFront 조합으로 정적 사이트를 배포하면, /work
같은 폴더형 경로로 접속할 때 문제가 생깁니다.
브라우저는 …/work
→ S3에서 객체 work를 찾으려 하는데, 실제로는 work/index.html
만 있어서 AccessDenied XML이 떨어집니다.
해결 방법 1) CloudFront Function으로 index.html 리라이트 (권장)
확장자가 없는 요청은 자동으로 …/index.html
을 붙여주도록 설정합니다.
CloudFront → Functions → Create function → 아래 코드 추가:
function handler(event) {
var req = event.request;
var uri = req.uri;
// 이미 파일(확장자 있는 경우)은 그대로
if (uri.includes(".")) return req;
// 루트 경로 → /index.html
if (uri === "" || uri === "/") {
req.uri = "/index.html";
return req;
}
// 슬래시로 끝나면 index.html 붙이기
if (uri.endsWith("/")) {
req.uri = uri + "index.html";
return req;
}
// 그 외 → /index.html 붙이기
req.uri = uri + "/index.html";
return req;
}
Publish 후,
CloudFront 배포 → Behaviors → Edit → Function associations → Viewer request에 연결
→ 이후 /work
, /about
, /posts/slug
모두 …/index.html
로 매핑되어 정상 동작합니다.
Astro를 쓰는 경우
astro.config.mjs
에서trailingSlash: 'always'
를 지정하면 빌드 산출물이/path/
형태가 되고, CloudFront Function과 조합하면 거의 완벽하게 정적 라우팅을 지원합니다.
해결 방법 2) SPA 폴백 (선택)
만약 프로젝트가 싱글 페이지 앱(SPA)라면, **존재하지 않는 경로도 전부 /index.html
**로 리다이렉트하도록 CloudFront Error Pages를 설정할 수도 있습니다.
403 / 404 → Response code 200, Response page: /index.html
하지만 Astro 블로그처럼 정적 페이지가 많은 경우, Function 방식이 더 안정적이고 권장됩니다.
6. 마무리
- Vercel도 편리하지만, AWS로 옮기니 구조가 보이고 직접 제어 가능
- GoDaddy 도메인을 Route53으로 옮기지 않고도 CloudFront 배포 성공
- ACM 인증서는 자동 갱신이라 관리 부담 없음
- GitHub Actions 덕분에 main 푸시 → 자동 배포 완료
경로 라우팅 문제 해결
Astro 같은 정적 사이트는 모든 경로에 index.html
이 존재하지만, S3+CloudFront는 기본적으로 index.html
을 찾아주지 않습니다. 따라서 CloudFront Function으로 리라이트를 추가하는 게 사실상 필수입니다.
👉 이 작업을 해주고 나니 /work
처럼 path만 입력해도 정상적으로 블로그가 열리게 되었고, Vercel에서처럼 편하게 라우팅을 쓸 수 있었습니다.