[카테고리:] 미분류

  • FastAPI + Next.js 1

    0. 전체 시리즈 목차 (예고)

    1. 현재 진단 & 기준선 만들기 → 오늘
    2. FastAPI 프로덕션 튜닝(성능/연결/동시성)
    3. 데이터 레이어(Postgres/Redis/Search)와 캐싱 전략
    4. 비동기·이벤트 처리(큐/스트림/워크플로: SQS·Kafka·Celery·Outbox)
    5. 인증/인가(OAuth2/OIDC, JWT/세션, RBAC/ABAC, 토큰 로테이션)
    6. API 게이트웨이·레이트리밋·엣지(CloudFront/Vercel/WAF/BFF 패턴)
    7. Next.js at Scale(SSR/ISR/RSC/Route Handlers, 이미지 최적화, CDN)
    8. 가시성(Observability): OpenTelemetry/로그/메트릭/트레이싱 & SLO
    9. CI/CD & IaC: GitHub Actions, Docker, Terraform, Helm, GitOps
    10. 쿠버네티스 운영: 오토스케일링, 롤아웃, KEDA, PDB, Mesh
    11. 보안·컴플라이언스: 비밀/키 관리, 암호화, PII, ISMS-P/개인정보보호법
    12. 신뢰성·DR: 백업/복구, RTO/RPO, 멀티AZ/리전, 카오스 테스트, 비용관리

    1편. 현재 진단 & 기준선 만들기 (Baseline First)

    1) 목표 정의: “성능·안정성·비용” 3축

    • 성능: API p95 < 200ms(핵심 읽기), p95 < 350ms(쓰기), 웹 p95 TTFB < 300ms(SSR 기준)
    • 안정성: 가용성 99.9% 이상, 에러율 < 0.1%
    • 비용: 월 이용자·요청당 비용(¥/req, ₩/MAU) 목표선 수립

    위 수치는 서비스 특성에 맞게 조정하지만, “측정 가능한 수치”로 박아두는 게 핵심.

    2) 현황 스냅샷: 지금 상태를 수치로 “고정”

    아래 체크리스트로 현재 수치를 기록해 두세요. 앞으로 모든 개선은 이 기준선과 비교합니다.

    • 트래픽: 초당 요청(RPS), 피크 시간대, 엔드포인트 TOP 10
    • 지연: p50/p95/p99(엔드포인트별), TTFB/CLS/LCP(웹)
    • 에러: 4xx/5xx 비율, 상위 에러 유형(타임아웃/DB Deadlock 등)
    • DB: QPS, 느린 쿼리 Top 20, 연결 수/사용률, 캐시 히트율
    • 인프라: CPU/메모리/네트워크 사용률, 오토스케일 동작 여부
    • 배포: 리드타임, 실패율, 롤백 평균 시간
    • 비용: 월 총비용, 리소스별 분해(컴퓨트/DB/스토리지/네트워크)

    k6로 10분만에 벤치 기준선 잡기(샘플)

    // save as baseline.js
    import http from 'k6/http';
    import { sleep, check } from 'k6';
    export const options = { vus: 50, duration: '2m' };
    
    export default function () {
      const res = http.get(`${__ENV.API_URL}/healthz`);
      check(res, { 'status 200': (r) => r.status === 200 });
      sleep(1);
    }
    
    API_URL=https://api.example.com k6 run baseline.js
    

    → p95, 에러율 기록 후 저장(“2025-10-09 기준선” 같은 이름)

    3) 최소 운영 아키텍처(MVP→프로덕션) 제안

    베이스 아키텍처(단계 1)

    [Client] 
       ↓
    [CDN/Edge (CloudFront/Vercel)] — [WAF]
       ↓
    [Next.js SSR/ISR (Vercel or Node pool)]
       ↓           ↘
    [BFF/API Gateway]  [Static/S3, Image Opt]
       ↓
    [FastAPI (Gunicorn+Uvicorn, ASGI)]
       ↓       ↘
    [PgBouncer] [Redis(Cache/Rate limit)]
       ↓
    [Postgres (RDS/Aurora)]
       ↘
    [Queue( SQS/Rabbit/Kafka )] ← [Workers(Celery/KEDA)]
    
    • Next.js: ISR로 캐시 가능한 페이지는 엣지/CDN에 최대한 밀어넣기
    • FastAPI: 동시성 친화(비동기 I/O), 커넥션 풀, 타임아웃·리트라이·Circuit Breaker
    • Redis: 캐시·세션·분산락·레이트리밋 토대
    • PgBouncer: DB 연결 수 관리(트래픽 급등 방지)
    • Queue + Worker: 쓰기/대용량/외부연동은 비동기로 털어주기
    • 관측성: OTEL 수집→Collector→Grafana/Tempo/Loki 등

    성장 단계(단계 2~3)에서 추가

    • 읽기 리플리카, CQRS(읽기 전용 API 라우팅)
    • 서치(OpenSearch/ES) 분리, 보고/분석 DB 분리
    • 서킷브레이커/리밸런싱(service mesh 또는 게이트웨이)
    • 블루/그린·카나리 배포, KEDA로 워커 자동 확장
    • 멀티AZ→멀티리전(액티브/패시브 → 액티브/액티브)

    4) 리포지토리 & 설정 표준화

    모노레포 추천(공유 타입 안전성↑)

    repo/
      apps/
        api/        # FastAPI
        web/        # Next.js
      packages/
        shared/     # 공통 DTO/스키마/유틸(예: zod schema + pydantic 매핑)
      infra/
        terraform/  # VPC/RDS/Redis/Queues/… IaC
        helm/       # k8s 차트
    

    설정 관리(12-Factor)

    • FastAPI: pydantic-settings로 환경변수 중심, .env는 로컬 전용
    • Next.js: NEXT_PUBLIC_*만 클라이언트로 노출, 나머지는 서버 전용
    • 비밀값은 SSM/Vault/KMS 등에서 주입, Git 저장 금지

    5) 헬스체크 & Readiness(실전용 스니펫)

    FastAPI

    # app/main.py
    from fastapi import FastAPI
    import asyncpg, aioredis, os
    
    app = FastAPI()
    @app.get("/healthz")   # liveness
    async def healthz():
        return {"ok": True}
    
    @app.get("/readyz")    # readiness
    async def readyz():
        # DB & Redis 간단 점검(타임아웃 필수)
        # 실제론 커넥션 풀 ping, 쿼리/명령 시간 제한 권장
        return {"db": "ok", "cache": "ok"}
    

    Next.js (Route Handler)

    // app/api/healthz/route.ts
    import { NextResponse } from 'next/server';
    export async function GET() { return NextResponse.json({ ok: true }); }
    

    쿠버네티스일 경우:

    • livenessProbe: /healthz
    • readinessProbe: /readyz (DB/Redis 체크 포함)
    • startupProbe: 마이그/워밍업 시간 고려

    6) 로깅·트레이싱·메트릭 최소 구성

    FastAPI OpenTelemetry 셋업 예

    # app/telemetry.py
    import os
    from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
    from opentelemetry import trace
    from opentelemetry.sdk.resources import Resource
    from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
    from opentelemetry.sdk.trace import TracerProvider
    from opentelemetry.sdk.trace.export import BatchSpanProcessor
    
    def setup_tracing(app):
        provider = TracerProvider(resource=Resource.create({"service.name":"api"}))
        exporter = OTLPSpanExporter(endpoint=os.getenv("OTLP_ENDPOINT","http://otel-collector:4317"), insecure=True)
        provider.add_span_processor(BatchSpanProcessor(exporter))
        trace.set_tracer_provider(provider)
        FastAPIInstrumentor.instrument_app(app)
    
    • 로그는 반드시 JSON 구조화(요청ID/사용자ID/트레이스ID 포함)
    • 메트릭: 요청수, 지연(p50/p95/p99), 에러율, DB풀 사용률, 캐시 히트율

    7) Docker & CI/CD 스켈레톤

    FastAPI Dockerfile(간단형)

    FROM python:3.12-slim
    WORKDIR /app
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt && pip install gunicorn uvicorn
    COPY . .
    ENV PORT=8000
    CMD ["gunicorn","-k","uvicorn.workers.UvicornWorker","-w","4","-b","0.0.0.0:8000","app.main:app"]
    

    Next.js Dockerfile(standalone 빌드)

    FROM node:20-alpine AS deps
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci
    
    FROM node:20-alpine AS builder
    WORKDIR /app
    COPY --from=deps /app/node_modules ./node_modules
    COPY . .
    RUN npm run build
    
    FROM node:20-alpine AS runner
    WORKDIR /app
    ENV NODE_ENV=production
    # standalone 출력 가정
    COPY --from=builder /app/.next/standalone ./
    COPY --from=builder /app/.next/static ./.next/static
    COPY --from=builder /app/public ./public
    EXPOSE 3000
    CMD ["node","server.js"]
    

    GitHub Actions(요지)

    • lint/test → build → sbom 생성 → 이미지 푸시 → 배포(ArgoCD 트리거)
    • PR단 품질 게이트: 유닛/통합 테스트, 타입체크, 슬랙 알림

    8) 빠른 체감 성과(48시간 내) 체크리스트

    • /healthz//readyz 구현 & 모니터링 대시보드 연결
    • API 타임아웃(클라이언트/서버/DB) 상향평준화, 재시도 정책 정의
    • PgBouncer 도입, DB 커넥션 수 절반으로 절감 테스트
    • Redis 캐시 레이어(핵심 읽기 1~2개 엔드포인트) 적용
    • 정적 자산/이미지 최적화 & CDN 헤더 점검(Next.js Image 최적화도)
    • k6로 p95/에러율 재측정 → 기준선 대비 개선폭 기록

    9) 안티 패턴(초기 확장 단계에서 많이 겪는 것)

    • API에서 모든 것 동기 처리(이메일/서드파티/영상처리 등)
    • DB 커넥션 폭주(풀 미사용·ORM 세션 누수)
    • SSR 페이지를 매 요청 렌더(ISR/캐시 미활용)
    • 구조화 로그 부재로 원인 추적 불가
    • 엔드포인트별 SLO 없음(무엇이 중요한지 모름)
    • 스키마/인덱스 관리 부재로 느린 쿼리 누적

    10) 오늘의 산출물 템플릿(복붙용)

    A. 기준선 리포트 (YYYY-MM-DD)

    • 트래픽:
    • 지연 p50/p95/p99:
    • 에러율:
    • DB/캐시 상태:
    • 인프라 사용률:
    • 비용(추정):

    B. 2주 액션 플랜

    1. PgBouncer 도입, 커넥션 튜닝
    2. Redis 캐시(핵심 조회 2개)
    3. ISR 도입(트래픽 상위 페이지 3개)
    4. OTEL + 대시보드(지연/에러율 가시화)
    5. 릴리즈 파이프라인에 롤백 버튼 & 카나리 10% 적용

    다음 편(2편)은 “FastAPI 프로덕션 튜닝”:
    ASGI 서버 선택, 워커 수 결정, 커넥션 풀/타임아웃, N+1 방지, Pydantic v2/Python 3.12 최적화, 레이트리밋/백프레셔, 아이템포턴시 키, 트랜잭션·아웃박스까지 실전 스니펫 위주로 갑니다.