Claude Code × AWS S3 입문: 최소 권한으로 sync, 이미지 업로드, 백업, 서명 URL 만들기
Claude Code와 AWS S3를 안전하게 쓰는 입문 가이드. IAM 최소 권한, aws s3 sync, 정적 사이트, 업로드, 백업, 서명 URL을 다룹니다.
S3는 단순한 파일 저장소처럼 보이지만 실제 서비스에 붙이면 권한, 공개 범위, 동기화, 캐시, 삭제, 비용이 모두 얽힙니다. Claude Code는 스크립트와 TypeScript helper를 빠르게 만들 수 있지만, 프롬프트가 애매하면 너무 넓은 IAM policy도 쉽게 만들어냅니다.
이 글은 S3를 처음으로 운영 환경에 가깝게 쓰려는 사람을 위한 입문 가이드입니다. 정적 사이트, 이미지 업로드, 백업, 서명 URL을 모두 다루되 핵심은 least privilege입니다. 각 스크립트에는 필요한 action과 prefix만 줍니다.
명령과 설계는 다음 공식 문서를 기준으로 합니다: AWS CLI S3 reference, aws s3 sync reference, Boto3 presigned URL guide, Claude Code common workflows.
배경지식은 다음 글도 함께 보면 좋습니다: IAM, security, context.
코드보다 먼저 S3 용도를 나누기
use case 1은 정적 사이트입니다. dist를 site/에 sync하고 CloudFront가 캐시합니다. use case 2는 이미지 관리입니다. 관리자 화면이 uploads/ 또는 assets/images/에 올리고 사이트는 읽기만 합니다. use case 3은 백업입니다. DB dump나 PDF를 날짜 prefix로 보관하며 —delete를 쓰지 않습니다. use case 4는 비공개 다운로드입니다. 버킷은 private이고 앱이 짧은 서명 URL만 발급합니다.
이 네 가지를 섞으면 s3:*가 필요해 보입니다. 먼저 site/, assets/images/, uploads/, backups/, assets/private-reports/처럼 prefix를 나누고 Claude Code에 전달하세요. 그러면 생성되는 policy와 코드도 훨씬 검토하기 쉬워집니다.
계정 확인과 IAM 최소 권한
버킷과 리전을 변수로 두면 사람과 Claude Code가 같은 대상을 쉽게 확인할 수 있습니다.
export AWS_REGION=ap-northeast-1
export S3_BUCKET=claudecode-lab-assets-prod
aws sts get-caller-identity
aws s3 ls "s3://${S3_BUCKET}/" --region "${AWS_REGION}"
이 정책은 assets 읽기, uploads 쓰기만 허용하고 넓은 삭제 권한은 주지 않습니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadPublicAssetsOnly",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::claudecode-lab-assets-prod/assets/*"
},
{
"Sid": "WriteUploadsOnly",
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:AbortMultipartUpload"],
"Resource": "arn:aws:s3:::claudecode-lab-assets-prod/uploads/*"
},
{
"Sid": "ListLimitedPrefixes",
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::claudecode-lab-assets-prod",
"Condition": {
"StringLike": {
"s3:prefix": ["assets/*", "uploads/*", "backups/*"]
}
}
}
]
}
정적 사이트나 이미지 폴더를 동기화하는 기본 형태입니다. 처음에는 반드시 dryrun으로 확인합니다.
# 1. Preview changes first. This should become a habit.
aws s3 sync ./dist "s3://${S3_BUCKET}/site/" \
--region "${AWS_REGION}" \
--delete \
--cache-control "public,max-age=300" \
--dryrun
# 2. Deploy only after the preview looks right.
aws s3 sync ./dist "s3://${S3_BUCKET}/site/" \
--region "${AWS_REGION}" \
--delete \
--cache-control "public,max-age=300"
# 3. Upload long-lived images with a different cache policy.
aws s3 sync ./public/images "s3://${S3_BUCKET}/assets/images/" \
--region "${AWS_REGION}" \
--exclude "*.psd" \
--cache-control "public,max-age=31536000,immutable"
서명 URL은 버킷을 비공개로 유지하면서 짧은 시간만 다운로드를 허용합니다.
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const s3 = new S3Client({ region: process.env.AWS_REGION ?? "ap-northeast-1" });
export async function createDownloadUrl(key: string, filename: string) {
if (!key.startsWith("assets/private-reports/")) {
throw new Error(`Unexpected S3 key prefix: ${key}`);
}
const command = new GetObjectCommand({
Bucket: process.env.S3_BUCKET_NAME,
Key: key,
ResponseContentDisposition: `attachment; filename="${filename}"`,
});
return getSignedUrl(s3, command, { expiresIn: 900 });
}
Claude Code에는 목적, 금지사항, 확인 명령까지 함께 전달합니다.
このリポジトリに AWS S3 連携を追加してください。
目的: public/images を S3 の assets/images/ に同期し、private-reports/ のPDFだけ署名付きURLで配布する。
制約: バケット全体公開は禁止。s3:DeleteObject は付けない。aws s3 sync は必ず --dryrun を先に出す。
成果物: scripts/s3-sync-assets.mjs、lib/s3-presigned-url.ts、READMEの手順、確認コマンド。
確認: npm test、aws s3 ls、aws s3 sync --dryrun の出力で説明してください。
参照: AWS CLI s3/sync docs と Anthropic Claude Code common workflows。
이 policy는 일부러 좁게 만들었습니다. assets만 읽고 uploads만 쓰며 ListBucket도 prefix로 제한합니다. 초보자가 자주 하는 실수는 ListBucket을 object ARN에 붙이거나, 편하다는 이유로 전체 버킷에 s3:*를 주는 것입니다.
삭제 권한도 pitfall입니다. 대부분의 업로드 흐름에는 DeleteObject가 필요 없습니다. 필요하다면 별도 role이나 별도 스크립트로 분리하고 dryrun, 삭제 건수 확인, 사람의 승인을 넣는 편이 안전합니다.
aws s3 sync를 안전하게 쓰기
aws s3 sync는 로컬 경로와 S3 prefix를 비교해 변경분을 복사합니다. —delete를 붙이면 로컬에 없는 원격 파일을 지웁니다. 정적 사이트 배포에는 유용하지만 백업이나 공유 이미지 폴더에는 위험합니다.
# Backup use case: append-only, no --delete.
BACKUP_DATE=$(date +%Y-%m-%d)
aws s3 sync ./backups "s3://${S3_BUCKET}/backups/${BACKUP_DATE}/" \
--region "${AWS_REGION}" \
--storage-class STANDARD_IA \
--exclude "*.tmp"
aws s3 ls "s3://${S3_BUCKET}/backups/${BACKUP_DATE}/" --recursive --summarize
저는 항상 dryrun을 먼저 실행합니다. Claude Code에게 스크립트를 만들게 할 때 bucket, region, prefix, dryrun diff를 출력하게 하고 삭제 건수가 많으면 멈추게 합니다. 단순하지만 잘못된 경로와 —delete 조합을 막아줍니다.
서명 URL은 공개가 아니라 임시 허가
presigned URL은 버킷을 private으로 유지하면서 특정 시간 동안만 다운로드나 업로드를 허용합니다. 청구서 PDF, 사용자 보고서, 강의 자료처럼 인증 후 잠깐 배포하는 파일에 잘 맞습니다.
주의할 점은 expiresIn의 단위가 초라는 것, key prefix 검증이 필요하다는 것, 브라우저 파일명을 그대로 S3 key로 쓰지 않는다는 것입니다. 테스트 편의 때문에 하루짜리 URL을 만들고 그대로 운영에 남기는 것은 흔한 failure입니다.
비용, 캐시, 공개 범위의 함정
S3 비용은 저장 용량만이 아닙니다. 요청 수, 데이터 전송, CloudFront, 버전 관리, lifecycle도 영향을 줍니다. 공개 이미지에는 CloudFront와 cache-control을 두고, private 파일은 public bucket으로 만들지 않는 것이 기본입니다.
DRYRUN_OUTPUT=$(aws s3 sync ./dist "s3://${S3_BUCKET}/site/" --delete --dryrun)
echo "$DRYRUN_OUTPUT"
DELETE_COUNT=$(echo "$DRYRUN_OUTPUT" | grep -c "delete:" || true)
if [ "$DELETE_COUNT" -gt 20 ]; then
echo "Too many deletes: ${DELETE_COUNT}. Stop and review."
exit 1
fi
오래된 튜토리얼은 S3 static website hosting과 public bucket을 바로 권합니다. 하지만 실제 서비스에서는 private S3 + CloudFront 구성이 더 안전하고 설명하기 쉽습니다. 보안 review나 기업 고객 대응에서도 유리합니다.
Claude Code가 할 일과 사람이 볼 일
Claude Code는 sync 스크립트, presigned URL helper, README, 테스트, 에러 메시지 생성에 좋습니다. 사람은 AWS account, bucket, prefix, DeleteObject, public access, CloudFront 접근 방식을 반드시 봐야 합니다.
Masa 검증 메모: 가장 효과가 컸던 것은 prefix 분리였습니다. site, assets, uploads, backups, private-reports를 나누자 IAM review도 프롬프트도 쉬워졌습니다. 코드와 함께 verification steps를 쓰게 하는 것도 운영 신뢰도를 올렸습니다.
정리
Claude Code × AWS S3는 강력하지만, 핵심은 업로드 성공이 아니라 사고 방지입니다. use case를 나누고, prefix를 정하고, sync는 dryrun부터, presigned URL은 짧게, 공개는 CloudFront 앞단에서 관리하세요.
실제 리포지토리에 이 방식을 적용하고 싶다면 training / consultation.
무료 PDF: Claude Code 치트시트
이메일을 입력하면 명령, 리뷰 습관, 안전한 워크플로를 정리한 PDF를 받을 수 있습니다.
개인정보를 안전하게 관리하며 스팸을 보내지 않습니다.
작성자 소개
Masa
Claude Code 실무 워크플로와 팀 도입을 검증하는 엔지니어입니다.
관련 글
Obsidian 메모를 CLAUDE.md로 바꾸는 Claude Code 워크플로
Obsidian 작업 메모를 CLAUDE.md 운영 노트로 정리해 Claude Code 세션의 문맥 반복을 줄입니다.
Claude Code Revenue CTA Routing: 글에서 PDF, Gumroad, 상담으로 보내기
독자 의도에 따라 무료 PDF, Gumroad 상품, 상담으로 나누는 Claude Code CTA 설계입니다.
Claude Code 팀 인계 규칙: 리뷰 증거, 권한, 롤백, 수익 경로까지 넘기는 법
Claude Code 작업을 팀에 넘길 때 필요한 증거, 권한 규칙, 롤백, 무료 PDF, Gumroad, 상담 경로 체크리스트.