오토스케일링을 붙였더니(오토스케일링 + 로드밸런서) 새 문제가 터졌습니다. 사용자가 올린 프로필 사진이 "어떤 인스턴스에 접속했느냐"에 따라 보이기도, 안 보이기도 합니다. 각 인스턴스의 로컬 디스크에 파일을 저장한 탓입니다. 게다가 로그·백업이 인스턴스 디스크에 쌓여 용량이 꽉 찼습니다. 데이터를 '어떤 종류의 저장소'에 두느냐가 확장성과 비용을 가릅니다.
- 1오브젝트·블록·파일 스토리지의 근본 차이를 설명할 수 있다
- 2각 스토리지가 어떤 접근 패턴에 적합한지 판단할 수 있다
- 3오토스케일링 환경에서 파일을 어디에 둬야 하는지 안다
- 4스토리지 클래스·수명주기로 비용을 최적화하는 법을 안다
- 5내구성과 가용성의 의미 차이를 구분할 수 있다
세 가지 저장 방식
오브젝트 · 블록 · 파일 — 접근 방식이 다르다
- 오브젝트 스토리지(S3): 파일을 '객체(데이터+메타데이터+키)'로 저장, HTTP API로 접근. 사실상 무한 용량, 매우 높은 내구성. 정적 자산·이미지·백업·로그·데이터레이크에 적합. 부분 수정·랜덤 액세스엔 부적합(통째 교체).
- 블록 스토리지(EBS): 인스턴스에 디스크처럼 붙여 OS가 파일시스템을 올림. 낮은 지연·랜덤 R/W. DB 데이터파일·OS 디스크에 적합. 보통 한 인스턴스 전용.
- 파일 스토리지(EFS): NFS 등으로 여러 인스턴스가 동시 마운트하는 공유 파일시스템. 레거시 공유 디렉터리, 다중 워커 공유에 적합.
확대
위 그림처럼 세 스토리지는 접근 방식과 적합 용도가 다릅니다. 오브젝트는 HTTP API·무한 확장, 블록은 인스턴스 직접 연결·낮은 지연, 파일은 다중 인스턴스 동시 마운트입니다.
객체 하나를 올리고 받을 때 실제로 일어나는 일 — PUT부터 GET까지 6단계
aws s3 cp cat.jpg s3://my-bucket/photos/ 한 줄이면 파일이 올라갑니다. 하지만 그 뒤에서는 요청 서명 → 권한 평가 → 여러 노드 복제 저장 → ETag 반환이 순서대로 일어납니다. 이 흐름을 알면 "왜 403이 나는지", "ETag는 왜 주는지", "대용량은 왜 멀티파트인지"가 한 줄기로 이해됩니다. 오브젝트 스토리지는 디스크가 아니라 키로 객체를 통째 올리고 내리는 HTTP API 저장소라는 점이 이 단계마다 드러납니다.
[클라이언트] PUT /my-bucket/photos/cat.jpg (바디: 이미지 바이트)
│
① 요청 구성 → 엔드포인트·버킷·키(경로 문자열)·바디·헤더(Content-Type 등)
│
② 요청 서명 → 액세스 키로 요청 내용+시각을 서명(SigV4)
│ 비밀 키 자체는 네트워크로 전송되지 않음
│
③ 인증·권한 확인 → 서버가 서명을 재계산해 일치 확인 + 버킷 정책·IAM 평가
│ (여기서 막히면 403 AccessDenied)
│
④ 복제 저장 → 데이터를 여러 노드·AZ에 복제(예: 3벌 이상) → 내구성 확보
│
⑤ 메타·무결성 → 체크섬으로 ETag 계산, 키→저장 위치 매핑을 메타데이터에 기록
│
⑥ 응답 → 200 OK + ETag 반환
▼
[클라이언트] 이후 GET /my-bucket/photos/cat.jpg
→ ③ 권한 확인 → 키로 메타 조회 → 노드에서 바이트 스트리밍 → 200 OK + 바디
각 단계에서 무슨 일이 일어나고, 어긋나면 어떤 증상인가:
| 단계 | 하는 일 | 여기서 막히면 |
|---|---|---|
| ① 요청 구성 | 버킷·키·바디·헤더로 HTTP 요청을 만든다. 키는 파일 경로가 아니라 문자열 식별자다(photos/cat.jpg의 /도 폴더가 아니라 이름의 일부) | 키 오타·잘못된 버킷 → GET 시 404 NoSuchKey |
| ② 서명 | 액세스 키로 요청을 서명한다. 비밀 키는 서명 계산에만 쓰이고 전송되지 않음 | 시각 오차(clock skew)·리전 불일치 → SignatureDoesNotMatch |
| ③ 인증·권한 | 서버가 서명을 재계산해 일치 확인, 버킷 정책·IAM·Block Public Access를 평가 | 권한 부족·퍼블릭 차단 → 403 AccessDenied |
| ④ 복제 저장 | 데이터를 여러 노드·AZ에 복제 저장 → 노드 하나가 죽어도 안 잃음(11 9's 내구성의 근거) | 단일 PUT 크기 한도 초과(대용량) → 멀티파트 업로드로 나눠 올려야 함 |
| ⑤ 메타·ETag | 체크섬으로 ETag를 만들고 키→위치 매핑을 메타에 기록. 저장 중 비트 손상도 체크섬으로 감지 | 멀티파트 ETag는 단순 MD5가 아님(-+파트수 형태) — 무결성 비교 시 주의 |
| ⑥ 응답·조회 | PUT은 200+ETag, GET은 키로 메타를 찾아 노드에서 바이트를 반환 | 앞단 CDN·캐시가 있으면 덮어쓴 뒤에도 잠깐 옛 버전이 보일 수 있음(캐시 무효화 필요) |
즉 "업로드 성공"은 ②서명·③권한·④복제가 모두 통과했다는 뜻입니다. 403이 나면 ③(정책·퍼블릭 차단), 대용량이 실패하면 ④(멀티파트), 특정 키만 느리거나 제한되면 그 키·프리픽스에 요청이 몰린 핫키 문제를 의심합니다. 이 흐름을 블록 스토리지와 대비하면 차이가 뚜렷합니다 — 블록(EBS)은 인스턴스에 attach돼 OS가 파일시스템을 올리고, 매 읽기·쓰기는 서명·HTTP 없이 블록 단위로 오가며(POSIX 랜덤 액세스), 보통 한 인스턴스에만 붙습니다. 오브젝트는 요청마다 인증·복제를 거치는 대신 무한 확장·고내구성을, 블록은 낮은 지연·부분 수정을 얻는 구조입니다.
확대
위 그림처럼 접근 빈도가 낮아질수록 저렴한 스토리지 클래스로 자동 이동해 비용을 줄입니다. 수명주기 정책으로 수동 작업 없이 티어 이동과 삭제를 자동화할 수 있습니다.
확대
위 그림처럼 S3는 오브젝트를 최소 3개 AZ에 자동 복제해 11-nine(99.999999999%) 내구성을 보장합니다. 체크섬 검증으로 저장 중 비트 손상도 자동 감지·복구합니다.
오토스케일링이라면 — 업로드 파일은 오브젝트 스토리지로
인스턴스가 수시로 생기고 사라지는 환경(오토스케일링 + 로드밸런서)에서 사용자 업로드를 인스턴스 로컬 디스크(블록)에 두면, 그 인스턴스가 죽을 때 파일이 사라지고 다른 인스턴스는 그 파일을 모릅니다.
정답은 업로드 파일을 오브젝트 스토리지에 두는 것입니다. 모든 인스턴스가 같은 객체를 API로 접근하고, CDN(DNS와 CDN)을 앞에 붙여 빠르게 제공합니다. 무상태 설계의 핵심 조각입니다.
내구성 vs 가용성 — 다른 약속
오브젝트 스토리지가 말하는 내구성(durability) 은 '데이터가 유실되지 않을 확률'(예: 99.999999999%, 11 9's)입니다. 여러 시설에 복제해 사실상 안 잃습니다. 가용성(availability) 은 '필요할 때 접근 가능한 시간 비율'로 별개입니다. 내구성이 높다고 항상 즉시 접근된다는 뜻은 아닙니다(아카이브 클래스는 꺼내는 데 시간).
확대
오브젝트 스토리지 사고의 압도적 원인은 '실수로 공개된 버킷'입니다. 퍼블릭 차단이 켜져 있는지 확인합니다.
aws s3api get-public-access-block --bucket my-bucket \
--query "PublicAccessBlockConfiguration"
{
"BlockPublicAcls": true,
"IgnorePublicAcls": true,
"BlockPublicPolicy": true,
"RestrictPublicBuckets": true
}
aws s3api get-public-access-block오래된 로그·백업이 비싼 Standard에 쌓이면 비용 누수입니다. 수명주기 규칙을 확인합니다.
aws s3api get-bucket-lifecycle-configuration --bucket my-logs \
--query "Rules[].{Id:ID,Days:Transitions[0].Days,Class:Transitions[0].StorageClass}"
[ { "Id": "to-ia-then-archive", "Days": 90, "Class": "STANDARD_IA" } ]
aws s3api get-bucket-lifecycle-configuration- get-public-access-block의 네 값이 모두 true가 아니면 — 공개 노출 위험. 의도된 정적 호스팅이 아니면 즉시 차단
- 수명주기 규칙 없는 로그/백업 버킷 — 오래된 객체가 비싼 클래스에 무한 누적. Days 기준 이전/만료 규칙 추가
- 업로드 파일이 인스턴스 로컬에 저장되는 코드 — 오토스케일/재배포 시 유실. 오브젝트 스토리지로 이전
- 버전 관리(versioning)·삭제 방지 설정 — 실수/랜섬 대비. 중요 버킷은 버전 관리+MFA 삭제 검토
상황: 오브젝트 접근 권한이 의도와 다름(너무 막히거나 너무 열림).
원인: 오브젝트 공개 여부는 ① 계정/버킷의 퍼블릭 액세스 차단, ② 버킷 정책, ③ 객체 ACL이 함께 작용. 비공개가 기본이며, 일부만 공유하려면 정책 대신 사전 서명 URL(presigned URL) 로 한시적 접근을 주는 게 안전.
진단: get-public-access-block → 버킷 정책 → 해당 객체 ACL 순으로 확인.
해결: 공개 호스팅이 목적이면 CDN 경유로만 노출(원본 버킷은 비공개, OAC로 CDN만 접근). 임시 공유는 presigned URL(만료 시간 지정). "전체 공개로 열어 해결"은 사고로 가는 지름길.
심화 — 비용과 한계를 아는 사람이 설계를 잘한다
심화: 오브젝트 스토리지 비용의 세 축 — 저장·요청·전송
청구서를 읽을 줄 알아야 설계가 완성됩니다. 오브젝트 스토리지 비용은 저장량만이 아닙니다.
- 저장(Storage): GB·월 단위. 클래스가 쌀수록 저장비는 줄지만, 대신 아래 두 축이 비싸집니다.
- 요청(Request): PUT/GET 건당 과금. 수백만 개의 작은 파일을 매일 갱신하면 저장비보다 요청비가 커질 수 있습니다. 작은 파일 대량 보관은 묶어서(tar) 올리는 것이 정석입니다.
- 전송(Transfer): 나가는 트래픽(egress) 과금이 복병입니다. 같은 리전 내부는 무료지만 인터넷으로 나가거나 리전을 건너면 GB당 과금 — 이미지 서비스가 CDN 없이 S3에서 직접 서빙하면 전송비가 저장비의 수십 배가 되기도 합니다.
그래서 실무 공식은 "자주 나가는 콘텐츠 앞엔 CDN(엣지 캐시가 egress를 흡수), 거의 안 꺼내는 것은 Archive(단, 꺼낼 때 복원 비용·지연)"입니다. 클래스 선택은 저장비가 아니라 접근 패턴의 총비용으로 결정합니다.
상황: 서비스는 잘 돌고 장애도 없는데 비용만 폭증. 저장량 기준으로 견적을 냈던 팀은 원인을 못 찾습니다.
원인: 앱이 이미지·동영상을 S3 URL로 직접 서빙 — 사용자가 볼 때마다 S3에서 인터넷으로 egress가 발생합니다. 저장 200GB는 몇 달러지만, 월 수 TB 전송은 그 수십 배입니다. 트래픽이 늘수록 전송비는 선형으로 커집니다.
진단: 비용 탐색기에서 사용 유형별로 분해해 DataTransfer-Out-Bytes가 지배적인지 확인. S3 액세스 로그로 어떤 객체가 얼마나 불려 나가는지 상위 목록을 뽑습니다.
해결: ① 정적 콘텐츠 앞에 CDN을 붙여 엣지에서 캐시(원본 egress는 캐시 미스 때만) ② 원본 버킷은 비공개+OAC로 CDN만 접근 허용 ③ 큰 파일은 압축·해상도별 변형으로 바이트 자체를 줄입니다. 전송비는 "쓰는 만큼"이 아니라 설계한 만큼 나옵니다.
스토리지 선택은 비용과 직결됩니다. "왜 클라우드 비용이 높냐"를 파면 ① 안 끄는 인스턴스, ② 수명주기 없는 스토리지가 단골입니다. 오래된 객체를 IA/Archive로 자동 이전하면 저장비가 수십 % 줄기도 합니다(클라우드 비용 최적화).
또한 데이터 유출 사고의 상당수가 '공개된 버킷'에서 납니다. 보안 감사에서 가장 먼저 스캔하는 항목이며, 퍼블릭 차단을 계정 차원에서 강제하고 예외만 검토하는 가드레일(관측과 거버넌스)을 둡니다.
관련 모듈로 더 깊이:
- 관리형 데이터베이스(RDS) — 블록 스토리지 위에서 도는 관리형 DB의 멀티AZ·복제·백업
- 백업과 재해복구 — 오브젝트 스토리지를 백업 대상·보관소로 쓰는 DR 설계
- DNS와 CDN — 오브젝트 스토리지의 정적 콘텐츠를 CDN으로 빠르게 전달하는 법
다음 모듈에서는 블록 스토리지 위에서 도는 데이터의 핵심 — 관리형 데이터베이스(RDS) 의 멀티AZ·읽기 복제·백업을 다룹니다.