AWS S3 CloudFront 정리 (403 에러 해결 포함) - lets-go-trip/treaXure-backend GitHub Wiki

1. S3 버킷 생성 및 기본 설정

  • 버킷 이름 예시: my-app-static-assets
  • 리전: 예: ap-northeast-2
  • 퍼블릭 액세스 차단 설정: 활성화 (권장 설정)
  • 객체 소유권 설정: 버킷 소유자 적용
  • 버킷 생성 후, CloudFront에서 접근할 예정이므로 퍼블릭 액세스 허용 없이 설정 진행

2. 테스트 이미지 업로드 (CLI)

aws s3 cp sample2.webp s3://my-app-static-assets/images/sample2.webp --acl private
  • -acl private 옵션을 통해 CloudFront 접근만 허용하도록 설정

3. S3 버킷 정책 설정 (CloudFront용)

echo '{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowCloudFrontServicePrincipal",
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-app-static-assets/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::[ACCOUNT_ID]:distribution/[DISTRIBUTION_ID]"
        }
      }
    }
  ]
}' > bucket-policy.json

aws s3api put-bucket-policy \ --bucket my-app-static-assets \ --policy file://bucket-policy.json


4. 첫 CloudFront 배포에서 403 에러 발생

  • 기존 배포는 /images를 Origin Path로 사용해 URL이 이중 중복됨
  • OAI 방식이거나 설정 꼬임으로 OAC 비활성화 → 접근 권한 없음
  • Invalidation 및 버킷 정책 수정에도 효과 없음

5. CORS 설정 확인 및 수정

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT"],
    "AllowedOrigins": [
      "https://[CLOUDFRONT_DOMAIN]",
      "<http://localhost:8080>"
    ],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3000
  }
]
aws s3api put-bucket-cors \\
  --bucket my-app-static-assets \\
  --cors-configuration file://cors.json

실수: 쉼표가 포함된 도메인 입력으로 CORS 무시됨 → 수정 후 정상 작동


6. 문제 해결: CloudFront 새로 생성

aws cloudfront list-distributions
  • 기존 배포는 OAC 연결 실패 상태였고, /images Origin Path 설정 문제도 존재
  • 새 CloudFront 배포 생성 시 OAC 정상 연결 및 버킷 정책 자동 업데이트 설정
  • 생성된 배포:
    • ID 예시: [NEW_DISTRIBUTION_ID]
    • 도메인: [NEW_CLOUDFRONT_DOMAIN]
aws cloudfront get-distribution --id [NEW_DISTRIBUTION_ID] | grep Status
  • 생성 직후 상태: InProgress
  • 일반적으로 15~30분 이내에 Deployed 상태로 전환됨

7. CloudFront Invalidation 수행

aws cloudfront create-invalidation \\
  --distribution-id [NEW_DISTRIBUTION_ID] \\
  --paths "/*"
  • 설정 반영을 위한 강제 캐시 무효화

8. 최종 curl 테스트

curl -I https://[NEW_CLOUDFRONT_DOMAIN]/images/sample2.webp

정상 응답:

HTTP/2 200
content-type: image/webp

🔁 기존 배포 삭제 시도 및 비활성화 문제

aws cloudfront get-distribution-config --id [OLD_DISTRIBUTION_ID] > config.json
  • "Enabled": false로 설정 변경 후 If-Match로 업데이트
  • 상태 전파까지 시간이 걸리므로 수 분 대기 필요
aws cloudfront delete-distribution --id [OLD_DISTRIBUTION_ID] --if-match [ETAG]
  • DistributionNotDisabled 오류 시 → 비활성화 상태가 아직 전파되지 않음

⚠️ 삽질 요약 & 배운 점

문제 원인 해결
CloudFront 403 Origin Path 중복, OAC 미연결, 정책 미반영 새 배포 생성, OAC 새로 생성, 정책 재설정
CORS 실패 도메인 뒤에 쉼표 JSON 수정 후 재적용
캐시 무효화 효과 없음 설정 적용 전 캐시 있음 Invalidation "/*" 수행
삭제 실패 CloudFront 비활성화가 아직 완료되지 않음 비활성화 후 대기, 이후 삭제 시도

✅ 총평 & 교훈

  • CloudFront는 OAC 방식으로 처음부터 세팅하는 것이 권장된다.
  • 문제가 발생하면 아래 순서로 진단하자:
    1. S3 객체 -acl private 확인
    2. 버킷 정책의 PrincipalSourceArn 정확성
    3. CloudFront Origin Path가 비었는지 확인
    4. OAC 설정이 적용되었는지 확인 (기존 배포일 경우 삭제 후 재생성도 고려)
    5. CloudFront Invalidation 수행
    6. curl로 200 응답 확인
    7. CloudFront 비활성화 후 삭제까지는 시간 필요하니 인내심 갖고 대기

✅ 위 작업들은 모두 AWS CLI 기반으로 수행되었으며, 동일한 구조의 CDN 설정이 필요할 경우 이 템플릿을 참고할 것.

⚠️ **GitHub.com Fallback** ⚠️