DEVELOP/AWS
S3 버전 관리와 권한
콘순이
2025. 5. 29. 23:13
S3 버전 관리
- 객체(=파일)의 버전을 관리하는 기능
- default가 비활성화라서, 사용하려면 직접 활성화해야함
- 업로드된 객체의 키를 기준으로 버전 할당
- 키 단위로 처음 업로드된 객체가 버전1, 같은 키로 업로드된 객체가 버전2
- S3의 버전 관리는 시간 순으로 버전이 쌓이는 구조이다.
S3 버전 관리 장점
- 실수로 삭제하거나 덮어쓸 때 이전 버전을 보존하거나 복원 가능
- 버전별로 고유한 버전 ID를 가지고, 삭제나 조회 등 관리 가능
- Delete Marker - 객체를 삭제해도 해당 객체는 삭제되지 않고, 삭제되었다는 마커만 추가
→ 비용은 비효율적일 수 있지만, 버전이 보존되는 장점 → 비용 같은 경우, LifeCycle 기능을 통해 단점을 보완 가능
AWS S3 삭제 마커(Delete Marker)란?
- 삭제 마커는 버전 관리가 활성화된 S3 버킷의 기능
- 버전 관리가 활성화된 버킷에서 객체를 삭제해도 해당 객체는 영구적으로 삭제되지 않음
- 대신, AWS는 객체에 대한 자리 표시자(마커)를 생성하게 되고, 이를 삭제 마커라고 함
- 이 마커는 객체의 현재 버전이 된다.
- 삭제 마커는 AWS S3가 마치 객체가 삭제된 것처럼 동작하도록 만든다.
- 버전 관리가 활성화된 경우, 단순 삭제 시 Delete Marker 생성, 객체의 기존 버전들은 유지

- goopang.jpg 처음 업로드 → ID = aaa 생성
- 파일 수정 후 재업로드 → ID = bbb 생성
- 또 다시 수정 후 업로드 → ID = ccc 생성
- 사용자가 Key = goopang.jpg에 대해 삭제 요청 → 실제 삭제가 아닌, 삭제 마커 (ID = ddd) 생성
- 이때 기본 GET goopang.jpg 요청은 404 Not Found → 삭제 마커가 가장 위에 있어서 마치 삭제된 것처럼 보임
- 사용자가 같은 key로 재업로드 → ID = eee 생성 → 이 시점부터 다시 GET goopang.jpg 시 eee 반환됨
삭제 마커가 있어도 권한+버전 ID만 알면 이전 버전 접근 가능함
그러면 객체를 진짜 삭제하려면?
- 단순 Delete 요청이 아닌 Delete 요청에 삭제하려는 버전을 명확히 지정해야함
- 아래 그림처럼 Delete Marker를 제외한 모든 버전이 삭제 되면, 해당 객체는 만료된 객체 삭제 마커가 됨

- 여기서 문제점
- 삭제 마커는 객체의 실체가 아님에도 S3에서 스토리지 비용을 청구할 수 있음.
- 수천, 수만 개의 객체가 이런 상태가 되면 스토리지 요금이 낭비될 수 있음.
- 해결책
- Lifecycle 정책을 설정해서 이런 Expired Delete Marker들을 자동으로 삭제하도록 설정
Life Cycle과 스토리지 클래스
스토리지 클래스란
- S3 객체의 저장 방식(옵션) -> 가격이 다 다름
- 오래된 객체(파일)들은 좀 더 저렴한 저장 옵션을 사용할 수 있도록 하면 좋겠죠?
- 하나의 버킷 안의 객체마다 서로 다른 스토리지 클래스를 가질 수 있다.
- 스토리지 클래스는 버킷에 대한 설정이 아니라, 객체 단위라서
- 예시
- my-bucket/report-2023.pdf → S3 Standard
- my-bucket/logs/2020-access.log → Glacier
- my-bucket/image-thumbnail.jpg → Standard-IA
- 객체 업로드 시 또는 이후 Copy/Transition 작업을 통해 스토리지 클래스를 지정하거나 변경할 수 있음
- LifeCycle 정책으로 스토리지 클래스로 자동 변경 가능함
- 뭐가 좋은건데?
- 비용 절감
- S3 Standard: 자주 접근하는 데이터에 적합하지만, GB당 저장 비용이 높음
- Standard-IA: 덜 접근하는 데이터에 적합하며, 저장 비용이 약 50% 저렴 → 단, 조회(읽기) 시 추가 요금 발생
- 오래됐고 잘 안 보는 데이터를 Standard-IA나 Glacier로 자동 이전시키면 비용 절감 가능
- 운영 자동화
- 운영자가 매번 스토리지 최적화를 수동으로 하지 않아도 된다.
- 보존 및 감사 대응
- 규정상 1년 보관 후 파기해야 한다면→ 규정 준수 자동화
- LifeCycle Rule로 정해진 시점에 삭제(Expiration) 설정 가능
- 비용 절감
- 사용 예시
→ 업로드된 지 30일이 지난 객체를 Standard-IA로 자동 전환하는 Lifecycle 정책"Transition": { "Days": 30, "StorageClass": "STANDARD_IA" }
- 뭐가 좋은건데?
예시
시점 | 접근 빈도 | 스토리지 클래스 | 설명 |
업로드 직후 ~ 30일 | 자주 접근 | S3 Standard | 최근 로그 분석 |
30일 초과 ~ 180일 | 거의 안 봄 | Standard-IA | 분석용 백업 유지 |
180일 초과 | 아카이브만 필요 | Glacier | 장기 백업 (복원 시 몇 시간 소요 가능) |
S3 권한 관리
S3는 전세계에서 접속할 수 있는 스토리지 서비스, 따라서 어느 누가 접속해 내용물을 변조할 수 있기 때문에 S3의 버킷의 기본 정책은 public이 아닌 private으로 설정되어 있다.
하지만, 만일 외부에서 접속해 S3의 버킷을 제어해야 한다면, 버킷을 통째로 public으로 개방하는 것이 아닌 S3 권한 제어를 통해 접근 제어를 설정할 수 있다.
- 사용자를 생성하고, 사용자의 버킷 권한 액세스를 관리하는 IAM
- 권한 있는 사용자에 대해 간단한 개별 객체(오브젝트)를 액세스 가능하게 만드는 액세스 제어 목록(ACL)
- 단일 S3 버킷 내 모든 객체에 대한 권한을 세부적으로 구성하는 버킷 정책
- 임시 URL을 사용하여 다른 사용자에게 기간 제한(임시 권한) 액세스를 부여하는 쿼리 문자열 인증(pre-signed URL)
- AWS SDK 사용이나 IAM 인증 없이 클라에서 바로 S3로 다운, 업로드 같은 동작을 할 수 있게 해주는 URL (서버에서 서명된 URL을 보내줌)
즉..
- 버킷 정책: "이 버킷에 누가 어떻게 접근할 수 있어?" → 주로 버킷 기준 설정
- ACL: "이 객체에 어떤 계정이 접근할 수 있어?" → 간단, 레거시
- IAM 정책: "이 사용자(또는 역할)가 어떤 AWS 리소스를 쓸 수 있어?" → 사용자 중심
간단하게 버킷 정책 속성 알아보기
{
"Version": "2012-10-17", // Bucket Policy의 문법이 언제 날짜 기준으로 확정된 문법을 사용하는지 → 2008-10-17 버전 후 2012-10-17 버전이 있는데, 그 뒤로는 업데이트가 안됐음
"Id": "S3PolicyId1", // Bucket Policy의 고유 아이디, 자동으로 부여되는 경우가 많음
"Statement": [
{
"Sid": "IPAllow", // 각 Statement의 고유 아이디. 무슨 역할을 하는 policy인가
"Effect": "Allow", // 버킷에 대한 명령을 허락(allow)하거나 거부(deny). 특정 사용자에 대해 명령을 제한하거나, 허용하는 식으로 사용
"Principal": {"AWS": "arn:aws:iam::spark323:user/spark"}, // Bucket Policy의 적용대상 (spark323 아이디의 유저에 대해서)
"Action": [ // Bucket Policy에서 허용한 Action
"s3:GetObject", // 객체 가져오는 행동
"s3:GetBucketLocation", // 버켓 위치를 확인하는 행동
"s3:ListBucket", // 버켓 리스트를 확인하는 행동
],
"Resource": "arn:aws:s3:::bucketname/*", // 대상이 대는 Bucket에 대한 명세
"Condition": { // 어떤 조건 하에
"NotIpAddress": { // 이 IP비허용
"aws:SourceIp": "1.1.1.1/32"
},
"IpAddress": { // 이 IP허용
"aws:SourceIp": [
"192.168.1.1",
"192.168.1.2/32",
]
}
}
}
]
}
- Effect: 명시적 정책에 대한 허용 혹은 차단
- Principal: 접근을 허용 혹은 차단하고자 하는 대상
- Action: 허용 혹은 차단하고자 하는 접근 타입
- Resource: 요청의 목적지가 되는 서비스
- Condition: 명시적 조건이 유효하다고 판단될 수 있는 조건
S3 보안 설정에 유의해야 하는 이유
- 버킷 이름은 전 세계적으로 유일하지만 유추 가능
- 공격자가 자동 스캐너를 이용해 company-name-logs 같은 버킷 이름을 쉽게 추측할 수 있음
- S3 URL 형식도 고정되어 있어 URL만으로 접근 시도 가능
예: https://bucket-name.s3.amazonaws.com/key
- 요금 폭탄 발생 가능
- PUT 요청을 제한하지 않으면 외부에서 무차별적으로 파일 업로드 가능 → 저장 요금 증가
- 공개한 파일이 불특정 다수에 의해 과도하게 다운로드될 경우 → 트래픽 요금 폭증
- 실제 유출 사례가 다수 존재
- 잘못된 공개 설정으로 인한 기업 데이터 유출 사례가 반복적으로 발생 중
- 특히 로그, 백업 파일, 내부 문서 등이 의도치 않게 공개된 경우가 많음
그럼 어떻게 하는게 좋을까
1. 버킷 정책에서는 최소 권한만 부여 (필수)
- 예시: 익명 사용자에게 GetObject만 허용
- 주의할 점
- 실수로 비공개 문서를 공개 경로에 넣었다면, 버킷 정책만으로는 방지 불가
- 객체 수준 암호화, 접근 로깅, 민감한 파일용 별도 버킷 구성 등 추가적인 보안 장치 필요
2. 세부 권한은 IAM 정책으로 통제 (필수)
- 예시: PutObject, DeleteObject와 같은 민감한 권한은 서버 측 IAM Role로만 부여
- IAM 정책을 통해 접근 주체(IP, Role 등)에 따라 세밀하게 제어 가능
3. 클라이언트에서 직접 S3 업로드해야 한다면, Pre-signed URL 활용 (선택-권장)
- 특정 객체에 대해 임시로 PUT 권한 부여
- 유효시간 제한 가능 (예: 5분)
- 객체 키는 백엔드가 지정 → 클라이언트가 마음대로 경로 조작 불가
- 클라이언트에게 S3 권한 자체를 주지 않기 때문에 상대적으로 안전한 방식
4. VPC Endpoint로 접근 경로를 내부로 제한 (선택-보안 강화)
- S3 Gateway VPC Endpoint를 구성하면 특정 VPC/Subnet에서만 접근 가능
- 그 외 모든 접근은 버킷 정책으로 차단
- 클라이언트가 S3에 직접 접근할 수 없기 때문에, 백엔드가 중개자 역할을 해야 함