GCP 무료 티어 비용 최적화 전략

무료 티어 한도 분석

Cloud Functions

  • 호출 한도: 200만 호출/월
  • 컴퓨팅 시간: 40만 GB-초/월
  • 네트워크 송신: 5GB/월

예상 사용량:

  • 일일 키워드 조회: 1,000건
  • 월간 총 호출: 30,000건 (한도의 1.5%)
  • 충분히 여유 있음

Cloud Run

  • CPU 시간: 180만 vCPU-초/월
  • 메모리: 360만 GiB-초/월
  • 요청 수: 200만 요청/월

예상 사용량:

  • 평균 응답 시간: 500ms
  • 일일 API 요청: 500건
  • 월간 vCPU-초: 7,500초 (한도의 0.4%)
  • 충분히 여유 있음

Firestore

  • 읽기: 50,000 문서/일
  • 쓰기: 20,000 문서/일
  • 삭제: 20,000 문서/일
  • 저장: 1GiB

예상 사용량:

  • 일일 쓰기: 100건 (한도의 0.5%)
  • 일일 읽기: 200건 (한도의 0.4%)
  • 충분히 여유 있음

Firebase Hosting

  • 저장: 10GB
  • 전송: 360MB/일

예상 사용량:

  • React 앱 크기: 2MB
  • 일일 방문자: 50명
  • 일일 전송량: 100MB (한도의 28%)
  • 여유 있음

비용 모니터링 설정

1. 예산 알림 설정

# 예산 생성 (월 $1 한도)
gcloud billing budgets create \
    --billing-account=YOUR_BILLING_ACCOUNT \
    --display-name="무료 티어 모니터링" \
    --budget-amount=1USD \
    --threshold-rule=percent:50 \
    --threshold-rule=percent:90

2. 비용 알림 Cloud Function

# budget-alert-function/main.py
import functions_framework
import json
from google.cloud import logging
 
@functions_framework.cloud_event
def budget_alert(cloud_event):
    """예산 초과 시 알림"""
    
    pubsub_message = json.loads(cloud_event.data["message"]["data"])
    cost_amount = pubsub_message["costAmount"]
    budget_amount = pubsub_message["budgetAmount"]
    
    if cost_amount >= budget_amount * 0.8:
        # 80% 초과 시 서비스 일시 중단
        logging_client = logging.Client()
        logger = logging_client.logger("budget-alert")
        logger.error(f"예산 80% 초과! 비용: ${cost_amount}, 예산: ${budget_amount}")
        
        # TODO: 서비스 중단 로직 추가

3. 사용량 대시보드

# monitoring/usage_tracker.py
from google.cloud import monitoring_v3
 
def get_usage_metrics():
    """GCP 사용량 조회"""
    client = monitoring_v3.MetricServiceClient()
    project_name = f"projects/{PROJECT_ID}"
    
    # Cloud Functions 호출 수
    functions_query = monitoring_v3.ListTimeSeriesRequest(
        name=project_name,
        filter='resource.type="cloud_function"',
        interval=monitoring_v3.TimeInterval({
            "end_time": {"seconds": int(time.time())},
            "start_time": {"seconds": int(time.time()) - 86400}  # 24시간
        })
    )
    
    return client.list_time_series(request=functions_query)

비용 최적화 기법

1. Cloud Functions 최적화

# 콜드 스타트 최소화
import functions_framework
from functools import lru_cache
 
# 전역 변수로 연결 재사용
firestore_client = None
 
@functions_framework.http
def optimized_function(request):
    global firestore_client
    
    # 클라이언트 재사용
    if firestore_client is None:
        firestore_client = firestore.Client()
    
    # 캐싱으로 중복 호출 방지
    @lru_cache(maxsize=100)
    def cached_api_call(keyword):
        return call_naver_api(keyword)
    
    return cached_api_call(request.args.get('keyword'))

2. Firestore 쿼리 최적화

# 복합 인덱스 활용
def get_keyword_data(keyword, start_date, end_date):
    """효율적인 쿼리 구성"""
    
    # 단일 쿼리로 필요한 데이터만 조회
    query = db.collection('keyword_data') \
        .where('keyword', '==', keyword) \
        .where('timestamp', '>=', start_date) \
        .where('timestamp', '<=', end_date) \
        .order_by('timestamp', direction=firestore.Query.DESCENDING) \
        .limit(10)  # 필요한 만큼만 조회
    
    return [doc.to_dict() for doc in query.stream()]

3. 캐싱 전략

# Redis 대신 메모리 캐싱 활용
import time
from functools import wraps
 
cache = {}
CACHE_EXPIRE = 3600  # 1시간
 
def memory_cache(expire_time=CACHE_EXPIRE):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            cache_key = f"{func.__name__}:{str(args)}:{str(kwargs)}"
            current_time = time.time()
            
            # 캐시 확인
            if cache_key in cache:
                cached_data, timestamp = cache[cache_key]
                if current_time - timestamp < expire_time:
                    return cached_data
            
            # 캐시 미스 시 실행 및 저장
            result = func(*args, **kwargs)
            cache[cache_key] = (result, current_time)
            
            return result
        return wrapper
    return decorator
 
@memory_cache(expire_time=1800)  # 30분 캐시
def get_keyword_trends(keyword):
    """트렌드 데이터 캐싱"""
    return call_datalab_api(keyword)

스케일링 전략

단계별 확장 계획

1단계 (무료 티어): ~100 사용자/일
2단계 (유료 전환): ~1,000 사용자/일  
3단계 (최적화): ~10,000 사용자/일

2단계 진입 시점 판단

  • Cloud Functions 호출: 월 100만건 초과
  • Firestore 읽기: 일 25,000건 초과
  • 예상 월 비용: $10-20

3단계 최적화 방안

  • CDN 도입: Cloud CDN으로 응답 캐싱
  • 데이터 파티셔닝: Firestore 컬렉션 분리
  • 배치 처리: Cloud Scheduler로 주기적 데이터 갱신

비용 대비 성능 최적화

1. 지역 선택 최적화

# 서울 리전 사용 (레이턴시 최소화)
gcloud config set run/region asia-northeast3
gcloud config set functions/region asia-northeast3

2. 리소스 할당 최적화

# Cloud Run 서비스 설정
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: naver-api-service
spec:
  template:
    metadata:
      annotations:
        # 최소 리소스로 비용 절약
        run.googleapis.com/cpu: "1"
        run.googleapis.com/memory: "256Mi"
        # 콜드 스타트 최소화
        run.googleapis.com/execution-environment: gen2
    spec:
      containers:
      - image: gcr.io/project/image
        resources:
          limits:
            memory: "256Mi"
            cpu: "1000m"

3. 자동 확장 설정

# 트래픽 기반 자동 확장
metadata:
  annotations:
    run.googleapis.com/ingress: all
    autoscaling.knative.dev/minScale: "0"  # 비용 절약
    autoscaling.knative.dev/maxScale: "5"  # 한도 제한
    autoscaling.knative.dev/target: "10"   # 동시 요청 수

모니터링 대시보드

비용 추적 쿼리

-- BigQuery 비용 분석
SELECT
  service.description as service,
  location.location as region,
  SUM(cost) as total_cost,
  SUM(usage.amount) as usage_amount,
  usage.unit
FROM `project.dataset.gcp_billing_export_v1_XXXXXX`
WHERE _PARTITIONTIME >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 30 DAY)
GROUP BY service, region, usage.unit
ORDER BY total_cost DESC;

Grafana 대시보드 설정

{
  "dashboard": {
    "title": "GCP 무료 티어 사용량",
    "panels": [
      {
        "title": "Cloud Functions 호출 수",
        "type": "graph",
        "targets": [
          {
            "query": "gcp_cloud_function_execution_count"
          }
        ]
      },
      {
        "title": "Firestore 읽기/쓰기",
        "type": "graph", 
        "targets": [
          {
            "query": "gcp_firestore_document_read_count"
          }
        ]
      }
    ]
  }
}

위험 관리

1. 사용량 급증 대응

# 사용량 제한 미들웨어
def rate_limiter(max_requests_per_hour=100):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            client_ip = request.environ.get('HTTP_X_FORWARDED_FOR', request.remote_addr)
            
            # Redis 대신 Firestore로 요청 수 추적
            doc_ref = db.collection('rate_limits').document(client_ip)
            doc = doc_ref.get()
            
            current_hour = int(time.time()) // 3600
            
            if doc.exists:
                data = doc.to_dict()
                if data.get('hour') == current_hour:
                    if data.get('count', 0) >= max_requests_per_hour:
                        return {'error': 'Rate limit exceeded'}, 429
                    
                    doc_ref.update({'count': firestore.Increment(1)})
                else:
                    doc_ref.set({'hour': current_hour, 'count': 1})
            else:
                doc_ref.set({'hour': current_hour, 'count': 1})
            
            return func(*args, **kwargs)
        return wrapper
    return decorator

2. 비상 차단 스위치

# 환경변수로 서비스 제어
import os
 
def circuit_breaker(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        if os.environ.get('SERVICE_DISABLED', 'false').lower() == 'true':
            return {'error': 'Service temporarily disabled'}, 503
        return func(*args, **kwargs)
    return wrapper

업데이트: 2025-11-08
목표: 월 비용 $0 유지


작성일: 2025-11-08