개발 프로젝트/GCP 실습 일지

GCP 시작 가이드 #3 — Cloud Storage 깊이 있게

DataHunter7 2026. 5. 30. 12:26

핵심 요약

  • GCS는 객체 스토리지로, 버킷 단위로 리전 / 멀티리전 / 듀얼리전 / 스토리지 클래스 / 라이프사이클을 결정하면 비용과 가용성이 함께 결정된다.
  • 스토리지 클래스는 Standard / Nearline / Coldline / Archive 4종이며, 접근 빈도가 아닌 검색(retrieval) 비용까지 고려해 선택해야 한다.
  • 한국 사용자 대상 서비스라면 asia-northeast3 메인이 합리적. Always Free 영구 무료(US 3개 리전)는 학습 sandbox용으로만 별도 분리한다.
  • Signed URL과 Object Versioning은 보안/복구의 핵심 기능. Uniform Bucket-level Access를 기본으로 ACL은 사용하지 않는다.
  • 본 글은 콘솔 클릭 가이드가 아닌 gsutil / gcloud storage / Terraform 기반 운영 패턴을 다룬다.

사전 지식: 시리즈 #1, #2, REST API 기본, HTTP 헤더 개념

작성 시점: 2026년 5월 기준 (gcloud storage 명령 GA, gsutil deprecation 진행 중)


1. 객체 스토리지 모델 복습

Cloud Storage(GCS)는 AWS S3, Azure Blob Storage와 같은 객체 스토리지(Object Storage) 다. 파일 시스템과의 차이를 짧게 짚고 가자.

항목파일 시스템객체 스토리지
계층 구조 디렉토리 트리 Flat namespace (prefix로 모방)
부분 수정 가능 (랜덤 액세스) 불가 (객체 단위 교체)
메타데이터 inode 기반, 제한적 Custom metadata 자유롭게 부여 가능
접근 방식 POSIX HTTP REST API
일관성 동기식 Strong consistency (S3와 동일하게 2021년부터)

GCS의 gs://my-bucket/photos/2026/img.jpg 같은 경로는 실제로는 단일 객체 이름이다. 콘솔이나 gsutil이 슬래시를 디렉토리처럼 시각화할 뿐, 내부적으로 photos/ 디렉토리는 존재하지 않는다. 이 점은 prefix listing 성능 특성에 직접적인 영향을 준다.


2. 버킷 구성의 4가지 결정 축

버킷 생성 시 결정하는 4가지가 비용과 가용성을 좌우한다:

  1. Location Type: Region / Dual-region / Multi-region
  2. Storage Class: Standard / Nearline / Coldline / Archive
  3. Access Control: Uniform / Fine-grained
  4. Public Access Prevention: enforced / inherited

이 중 (1), (2), (3)은 생성 후 변경이 까다롭거나 비용이 발생하므로 처음부터 신중하게 결정한다.

2.1 Location Type 비교

Location Type예시가용성 SLA쓰기 비용 (Standard)사용 사례
Region asia-northeast3 99.9% $0.023/GB/월 단일 리전 서비스
Dual-region asia1 99.95% $0.046/GB/월 재해 복구 필요한 운영
Multi-region asia, us 99.95% $0.026/GB/월 글로벌 CDN 원본

요금은 2026년 5월 기준, Standard 클래스 기준 대략치다. 정확한 수치는 GCS Pricing에서 확인한다.

한국 사용자 대상 서비스의 기본 선택: asia-northeast3 단일 리전. 비용 효율과 레이턴시가 모두 우수하다.

재해 복구가 필요한 경우: asia1 Dual-region (도쿄 + 오사카) 또는 asia-northeast3 + asia-northeast1 양쪽에 별도 버킷 + Storage Transfer Service로 복제.

2.2 Storage Class 선택 기준

스토리지 클래스 비교 (asia-northeast3 기준, 2026년 5월):

Class저장 비용 ($/GB/월)검색 비용 ($/GB)최소 보관 기간대표 사용 사례
Standard $0.023 $0 없음 자주 접근, 웹 콘텐츠
Nearline $0.016 $0.01 30일 월 1회 미만 접근
Coldline $0.006 $0.02 90일 분기 1회 미만 접근
Archive $0.0025 $0.05 365일 연 1회 미만, 규제 보관

핵심 함정: 저장 비용만 보고 Archive를 선택하면 검색 시 비용 폭탄이 난다. 1TB를 한 번 전체 다운로드하면 검색 비용만 $50, 여기에 외부 egress 추가($0.12/GB × 1024 = $122)다.

선택 결정 트리:

객체 접근 빈도 (월 단위)
  ├─ 매일 또는 매주 → Standard
  ├─ 월 1회 미만 → Nearline
  ├─ 분기 1회 미만 → Coldline
  └─ 연 1회 미만 또는 규제 보관 → Archive

단, 객체 수명이 최소 보관 기간보다 짧으면
한 단계 위 클래스 선택 (조기 삭제 페널티 회피)
 
 

조기 삭제 페널티 예시: Nearline에 저장한 객체를 10일 후 삭제하면, 남은 20일치 저장 비용이 청구된다. 객체 수명이 가변적이라면 라이프사이클 정책으로 자동 전환하는 게 안전하다.

2.3 Uniform Bucket-level Access (UBLA)

신규 버킷은 무조건 Uniform으로 생성한다. Fine-grained(객체별 ACL)는 다음 문제로 사실상 deprecated 상태다:

  • 객체마다 다른 권한 → 권한 감사 불가능
  • IAM 정책과 ACL이 동시 적용되면서 권한 충돌
  • 대량 객체에 일괄 권한 적용 시 성능 저하

Uniform 모드에서는 버킷 단위 IAM만 사용하며, 객체별 권한 분리가 필요하면 버킷을 분리하거나 Signed URL(7절)로 임시 권한을 부여한다.


3. 버킷 생성 — gcloud storage 기준

3.1 gcloud storage vs gsutil

기존 gsutil은 deprecation 진행 중이며, 신규 명령은 gcloud storage 사용을 권장한다. 두 명령의 동일 작업 매핑:

작업gsutil (legacy)gcloud storage (권장)
버킷 생성 gsutil mb gcloud storage buckets create
객체 업로드 gsutil cp gcloud storage cp
객체 동기화 gsutil rsync gcloud storage rsync
메타데이터 조회 gsutil stat gcloud storage objects describe

본 글은 모두 gcloud storage 기준으로 작성한다.

3.2 운영 권장 설정으로 버킷 생성

서울 리전, Standard 클래스, UBLA, Public Access Prevention 적용 버킷 생성:

gcloud storage buckets create gs://my-app-uploads-prod \
  --location=asia-northeast3 \
  --default-storage-class=STANDARD \
  --uniform-bucket-level-access \
  --public-access-prevention \
  --enable-autoclass=false
 
 

각 옵션의 의미:

  • --location: 리전 또는 멀티리전 지정
  • --default-storage-class: 신규 객체의 기본 클래스
  • --uniform-bucket-level-access: UBLA 강제 (객체별 ACL 차단)
  • --public-access-prevention: 실수로 allUsers 부여해도 차단
  • --enable-autoclass: 자동 클래스 전환 활성화 옵션 (워크로드에 따라 선택)

3.3 Terraform으로 동일 구성

운영 환경은 IaC로 관리한다. Terraform 코드 예시:

resource "google_storage_bucket" "uploads" {
  name          = "my-app-uploads-prod"
  location      = "ASIA-NORTHEAST3"
  storage_class = "STANDARD"

  uniform_bucket_level_access = true
  public_access_prevention    = "enforced"

  versioning {
    enabled = true
  }

  lifecycle_rule {
    condition {
      age = 30
    }
    action {
      type          = "SetStorageClass"
      storage_class = "NEARLINE"
    }
  }

  lifecycle_rule {
    condition {
      age = 365
    }
    action {
      type = "Delete"
    }
  }

  labels = {
    env  = "prod"
    team = "platform"
  }
}
 
 

4. 라이프사이클 정책

라이프사이클 정책은 객체의 나이, 버전, 클래스 등 조건에 따라 자동으로 클래스 전환 또는 삭제를 실행한다. 비용 최적화의 핵심 도구다.

4.1 권장 패턴 - 핫/콜드 분리

웹 서비스의 업로드 파일 같은 워크로드는 초기 7일 핫 + 이후 콜드 패턴이 일반적이다. 라이프사이클 JSON 예시:

{
  "lifecycle": {
    "rule": [
      {
        "action": {
          "type": "SetStorageClass",
          "storageClass": "NEARLINE"
        },
        "condition": {
          "age": 30,
          "matchesStorageClass": ["STANDARD"]
        }
      },
      {
        "action": {
          "type": "SetStorageClass",
          "storageClass": "COLDLINE"
        },
        "condition": {
          "age": 90,
          "matchesStorageClass": ["NEARLINE"]
        }
      },
      {
        "action": {
          "type": "Delete"
        },
        "condition": {
          "age": 730
        }
      }
    ]
  }
}
 
 

이 정책을 적용하면:

  • 0~30일: Standard
  • 30~90일: Nearline (저장비 30% 절감)
  • 90~730일: Coldline (저장비 73% 절감)
  • 730일 이후: 자동 삭제

4.2 적용 명령

라이프사이클 JSON 파일을 작성한 뒤 적용:

gcloud storage buckets update gs://my-app-uploads-prod \
  --lifecycle-file=lifecycle.json
 
 

4.3 Autoclass 옵션

수동 라이프사이클 대신 GCS가 객체 접근 패턴을 학습해서 자동 전환하는 Autoclass도 있다.

 
gcloud storage buckets update gs://my-bucket --enable-autoclass
 

Autoclass의 트레이드오프:

  • 장점: 라이프사이클 정책 작성 불필요, 접근 패턴 변화에 자동 적응
  • 단점: 객체당 월 $0.0025 관리 비용 추가, 1MiB 미만 객체에 비효율적
  • 권장: 객체 수명/접근 패턴이 예측 불가능한 경우 사용. 정형 워크로드는 명시적 라이프사이클이 더 경제적

5. Object Versioning

5.1 동작 원리

Versioning이 활성화된 버킷에서는 객체 덮어쓰기/삭제 시 이전 버전이 noncurrent로 보존된다. 사실상 휴지통 + 시점 복구(point-in-time recovery)다.

활성화 명령:

gcloud storage buckets update gs://my-app-uploads-prod --versioning
 
 

객체 버전 조회:

gcloud storage objects list gs://my-app-uploads-prod --all-versions
 
 

각 버전은 generation 번호로 구분되며, 특정 버전 복구는 다음과 같이 한다:

 
gcloud storage cp gs://my-app-uploads-prod/file.txt#1717000000000000 gs://my-app-uploads-prod/file.txt
 

5.2 비용 관리

Versioning 없이 자주 덮어쓰는 워크로드는 의도치 않게 저장 비용이 폭증할 수 있다. noncurrent 버전 자동 정리 라이프사이클이 필수다:

 
{
  "lifecycle": {
    "rule": [
      {
        "action": {
          "type": "Delete"
        },
        "condition": {
          "numNewerVersions": 3,
          "isLive": false
        }
      },
      {
        "action": {
          "type": "Delete"
        },
        "condition": {
          "daysSinceNoncurrentTime": 30,
          "isLive": false
        }
      }
    ]
  }
}
 

이 정책의 의미: 최신 3개 버전 외에는 30일 후 자동 삭제. isLive: false는 현재 버전이 아닌 noncurrent 버전만 대상으로 한다는 표시다.


6. Signed URL — 임시 권한 부여

6.1 활용 시나리오

다음과 같은 케이스에서 Signed URL이 필수다:

  • 모바일 앱에서 직접 GCS에 업로드 (백엔드 경유 시 트래픽 2배)
  • 일회성 다운로드 링크 (만료 시간 설정)
  • 비공개 객체를 임시로 외부 공유

Signed URL의 원리: 백엔드가 SA 키 또는 IAM 자격증명으로 URL에 서명을 부여하고, 클라이언트는 그 URL로 직접 GCS에 접근한다. 권한은 서명된 URL 자체에 내장되어 있다.

6.2 백엔드 구현 예시 (Python)

업로드용 Signed URL 발급:

from google.cloud import storage
from datetime import timedelta

def generate_upload_signed_url(bucket_name: str, blob_name: str) -> str:
    client = storage.Client()
    bucket = client.bucket(bucket_name)
    blob = bucket.blob(blob_name)
    
    url = blob.generate_signed_url(
        version="v4",
        expiration=timedelta(minutes=15),
        method="PUT",
        content_type="application/octet-stream",
    )
    return url
 
 

다운로드용 Signed URL은 method="GET"으로 동일하게 발급한다.

6.3 인증 자격증명

위 코드가 동작하려면 백엔드가 서명 권한을 가져야 한다. 운영 환경 권장 패턴:

  • Cloud Run / GCE에 SA 첨부 (ADC 자동 사용)
  • 해당 SA에 roles/iam.serviceAccountTokenCreator 부여
  • IAM Service Account Credentials API를 통해 서명 (SA Key 파일 불필요)

SA Key를 디스크에 두지 않고 서명하려면 다음 추가 설정:

client = storage.Client()
url = blob.generate_signed_url(
    version="v4",
    expiration=timedelta(minutes=15),
    method="PUT",
    service_account_email="signer-sa@my-project.iahttp://m.gserviceaccount.com",
    access_token=auth_request_token,
)
 
 

#2편의 Workload Identity Federation과 결합하면, GitHub Actions에서도 키 없이 Signed URL을 발급할 수 있다.

6.4 보안 체크리스트

  • 만료 시간은 짧게 (업로드 15분, 다운로드 1시간 권장)
  • content_type / content_md5 명시로 URL 변조 방지
  • 발급 로그를 Cloud Logging에 기록 (감사 트레일)
  • 클라이언트 IP 제한 필요 시 VPC Service Controls 또는 별도 게이트웨이 검토

7. 한국 사용자 대상 서비스의 리전 결정

7.1 결정 흐름

운영 환경 리전 선택 결정 트리:

 
한국 사용자 대상 서비스인가?
├─ Yes
│   ├─ 재해 복구 RTO < 1h 필요?
│   │   ├─ Yes → Dual-region (asia1)
│   │   └─ No  → asia-northeast3 단일 리전
│   └─ 비용 민감 + 레이턴시 일부 양보 가능?
│       └─ asia-northeast1 (도쿄) 검토
├─ No (글로벌 / 미국 중심)
│   └─ us-central1
└─ 학습 / sandbox / 영구 무료 필요
    └─ us-west1 (Always Free 가능)

7.2 리전별 가격 차이

asia-northeast3 vs us-central1 비교 (Standard, 2026년 5월):

항목asia-northeast3 (서울)us-central1 (아이오와)차이
저장 ($/GB/월) $0.023 $0.020 +15%
Class A 작업 (write) $0.05/10K $0.05/10K 동일
Class B 작업 (read) $0.004/10K $0.004/10K 동일
외부 egress (아시아) $0.12/GB $0.12/GB 동일

대량 저장(수십 TB) 서비스가 아니면 리전 가격 차이의 절대 금액은 작다. 레이턴시 차이(서울 발 한국 사용자 ~10ms vs 미국 발 ~150ms)가 훨씬 큰 의사결정 요인이다.

7.3 Always Free 한도 (영구 무료)

paid 계정 전환 후에도 매월 자동 적용:

  • Cloud Storage 5GB Regional (us-west1, us-central1, us-east1 중 한 곳)
  • Class A 5,000회/월
  • Class B 50,000회/월
  • 네트워크 egress 100GB/월 (북미 발신)

운영 환경과 분리된 학습/sandbox 프로젝트를 us-west1에 별도로 두면 영구 무료 한도 안에서 실험 가능하다. 운영 버킷을 굳이 미국 리전으로 옮기는 비용 최적화는 권장하지 않는다.


8. 운영 모니터링 패턴

8.1 비용 분리 - 라벨

대규모 운영에서 가장 흔한 문제: "어느 서비스가 GCS 비용을 얼마나 쓰는지 모름". 버킷에 라벨을 부여하고 Billing Export로 분석한다.

라벨 부여:

gcloud storage buckets update gs://my-app-uploads-prod \
  --update-labels=team=platform,env=prod,service=upload
 
 

이후 BigQuery로 Billing Export한 데이터에서 라벨 기준 집계가 가능하다.

8.2 객체 수 / 용량 추적

버킷 객체 통계 조회:

 
gcloud storage du gs://my-app-uploads-prod --summarize --readable-sizes
 

대용량 버킷은 위 명령이 느리므로, Cloud Monitoring의 storage.googleapis.com/storage/total_bytes 메트릭을 활용하는 것이 표준이다.

8.3 Access Log 활성화

Bucket Access Logs는 모든 객체 접근을 별도 버킷에 기록한다. 보안 사고 분석과 비정상 접근 패턴 탐지에 필수다.

활성화:

 
gcloud storage buckets update gs://my-app-uploads-prod \
  --log-bucket=gs://my-audit-logs \
  --log-object-prefix=my-app-uploads-prod
 

로그는 다음 날 새벽에 1시간 단위 batch로 생성된다. 실시간 분석이 필요하면 Cloud Audit Logs (Data Access) 를 활성화해야 한다(별도 과금).


9. 흔한 함정과 디버깅

함정 1: Multi-region 버킷의 listing 일관성

Multi-region 버킷도 strong consistency를 보장하지만, listing 작업은 eventual consistency 구간이 있을 수 있다. 업로드 직후 listing에 안 나타나면 단순 재시도로 해결되는 경우가 많다.

함정 2: gsutil cp -r vs gcloud storage cp --recursive

대량 파일 복사 시 gcloud storage cp --recursive가 gsutil cp -r 대비 평균 2~3배 빠르다. 내부 병렬 처리 구현이 다르다. 대량 작업은 무조건 gcloud storage 사용.

함정 3: Signed URL 시계 불일치

서명한 백엔드의 시계가 GCS 서버와 5분 이상 차이 나면 401 에러가 발생한다. 컨테이너 환경에서 NTP 동기화 누락이 원인인 경우가 많다. 디버깅 시 date -u 출력과 GCS API 응답의 Date 헤더를 비교한다.

함정 4: Composite object의 CRC32C 불일치

gcloud storage compose로 합친 객체는 CRC32C가 부모 객체와 다르게 계산된다. 무결성 검증이 필요한 워크로드는 별도로 MD5/SHA-256을 metadata에 저장한다.

함정 5: Public Access Prevention의 enforced vs inherited

enforced는 버킷 레벨에서 강제이며, 누구도 allUsers 권한을 부여할 수 없다. inherited는 Organization Policy를 따른다. 운영 버킷은 무조건 enforced 로 설정한다.


10. 마무리 및 다음 글

핵심 정리

  1. 버킷 생성 시 결정하는 4가지(Location / Class / Access Control / PAP)가 비용과 보안의 90%를 좌우한다.
  2. 한국 사용자 대상 서비스는 asia-northeast3 메인. 영구 무료 한도는 별도 sandbox 프로젝트에서 us-west1로 분리.
  3. Storage Class 선택은 저장 비용이 아닌 검색 비용까지 고려한다. 라이프사이클 또는 Autoclass로 자동화.
  4. Versioning + noncurrent 정리 라이프사이클은 세트로 구성. 버전 무제한 보관은 비용 폭탄의 흔한 원인.
  5. Signed URL은 SA Key 없이 발급하는 것이 표준 (IAM Credentials API + 워크로드 첨부 SA).
  6. 신규 작업은 gcloud storage(legacy gsutil 대신), 운영 환경은 Terraform으로 IaC 관리.

다음 글(#4 예정): Cloud Run 배포 실전 — 컨테이너 빌드 전략, 콜드 스타트 최적화, min/max instances 튜닝, Cloud Run + GCS + Cloud SQL 통합 패턴.


참고 자료


카테고리: Cloud / GCP

태그: gcp google-cloud cloud-storage gcs object-storage terraform signed-url lifecycle cloud-architecture devops