
안녕하세요! 사이드 프로젝트에서 사용하고 있는 최신 웹 기술 스택과 배포 파이프라인을 소개하려고 합니다.
프론트엔드로 Next.js (App Router), 백엔드 로직 일부에 Hono를 사용하고 있으며, 모든 런타임과 빌드 도구로 Bun을 적극적으로 활용하고 있습니다. 이 글에서는 이 기술을 Google Cloud Run과 Cloud Build, 그리고 Cloudflare와 엮어 어떻게 효율적이고 자동화된 배포 환경을 구축했는지 공유합니다.
전체적인 아키텍처는 다음과 같습니다.
GitHub에 코드가 푸시되면 Cloud Build가 트리거되어 Docker 이미지를 빌드하고, Cloud Run에 배포된 후 서비스됩니다. 사용자는 Cloudflare CDN을 거쳐 최적화된 콘텐츠를 제공받습니다.
빌드 속도와 런타임 성능을 모두 잡기 위해 Bun을 전면적으로 도입했습니다. 프론트엔드와 백엔드 각각에 맞는 최적화된 Dockerfile 전략을 사용합니다.
Next.js 앱은 standalone 모드를 사용하여 꼭 필요한 파일만 포함한 경량 이미지를 만듭니다.
Dockerfile.nextjs 파일의 핵심 포인트:
1. Bun 기반 빌드: oven/bun:1.3 이미지를 사용해 의존성을 설치하고 빌드합니다. npm이나 yarn보다 훨씬 빠른 속도를 자랑합니다.
2. 보안 강화: 소스맵(sourceMappingURL)을 제거하여 코드를 보호하고 브라우저의 불필요한 요청을 방지합니다.
3. 경량화된 런타임: oven/bun:1.3-alpine을 베이스로 사용하여 이미지 크기를 줄이고, standalone 폴더와 static 에셋만 복사하여 실행합니다.
# Dockerfile.nextjs (요약)
FROM oven/bun:1.3 AS builder
# ... 의존성 설치 및 환경변수 설정 ...
RUN bun run build
# 소스맵 제거로 보안 강화
RUN find .next/static -type f -name '*.js' -exec sed -i -E 's@//# sourceMappingURL=.*$@@g' {} +
FROM oven/bun:1.3-alpine AS runner
# ...
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
CMD ["bun", "./server.js"]
Hono 기반의 백엔드 서비스는 Bun의 강력한 기능인 bun build --compile을 사용하여 단일 실행 파일로 컴파일합니다.
Dockerfile.hono 파일의 핵심 포인트:
1. Single Binary: 소스 코드를 실행 가능한 하나의 바이너리 파일(backend)로 만듭니다.
2. Distroless 이미지: 런타임에는 OS 쉘조차 없는 gcr.io/distroless/base-nossl-debian12:nonroot 이미지를 사용하여 보안성을 극대화하고 이미지 크기를 최소화했습니다.
# Dockerfile.hono (요약)
FROM oven/bun:1.3 AS builder
# ...
RUN bun build --compile \
--minify \
./src/backend/index.ts \
--outfile backend
FROM gcr.io/distroless/base-nossl-debian12:nonroot
COPY --from=builder /app/backend ./
ENTRYPOINT [ "./backend" ]
배포 자동화는 Google Cloud Build를 사용합니다. cloudbuild.prod.nextjs.yaml 설정 파일을 통해 빌드부터 배포까지의 과정을 정의했습니다.
주요 단계는 다음과 같습니다:
1. Build: Docker 이미지를 빌드합니다. 이때 Next.js 빌드에 필요한 환경변수(Sentry 키, API URL 등)를 --build-arg로 주입합니다.
2. Push: 빌드된 이미지를 Google Artifact Registry(GAR)에 푸시합니다.
3. Deploy: gcloud run services update 명령어로 Cloud Run에 새 버전을 배포합니다.
# cloudbuild.prod.nextjs.yaml (일부)
steps:
- name: gcr.io/cloud-builders/docker
args:
- build
- '--no-cache'
- '-t'
- '$_AR_HOSTNAME/...:$COMMIT_SHA'
- .
- '-f'
- Dockerfile.nextjs
# ... build-args ...
- name: gcr.io/cloud-builders/docker
args: ['push', '...']
- name: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
args:
- run
- services
- update
- $_SERVICE_NAME
- '--platform=managed'
- '--region=$_DEPLOY_REGION'
이 구성 덕분에 main 브랜치에 코드가 병합되면 자동으로 프로덕션 환경에 배포가 이루어집니다.
CDN, DNS, 캐싱 규칙 등 엣지(Edge)단의 설정은 Terraform을 사용하여 코드로 관리(IaC)하고 있습니다.
cloudflare/terraform/cache-rules.tf에서 서비스의 특성에 맞는 정교한 캐싱 규칙을 정의했습니다.
/api/ 경로는 오리진 서버의 Cache-Control 헤더를 따릅니다./library, /doc 등의 경로는 엣지에서 30일간 캐싱하여 속도를 높입니다./ranking/(1일), /new/(3시간), /random(10초) 등은 짧은 TTL을 적용합니다./.well-known/ 등은 캐시를 우회합니다.# cache-rules.tf (예시)
resource "cloudflare_ruleset" "cache_rules" {
# ...
rules = [
{
description = "Cache with 30 days TTL"
expression = local.ttl_30d_expression
action = "set_cache_settings"
action_parameters = {
edge_ttl = { mode = "override_origin", default = 2592000 } # 30일
}
},
# ...
]
}
이렇게 Terraform으로 규칙을 관리하면, 복잡한 캐싱 로직을 실수 없이 버전 관리하며 배포할 수 있습니다. 그리고 아래와 같이 원본 서버인 Cloud Run으로 가는 트래픽의 90%를 캐싱해 Cloud Run 서버 비용을 최대 10배까지 절약할 수 있습니다.

이번 글에서는 Next.js와 Hono, Bun, 그리고 Google Cloud와
Cloudflare를 엮어 구축한 배포 시스템을 살펴보았습니다.
이 아키텍처는 초기 설정에 공수가 들지만, 한 번 구축하면 개발자는 비즈니스 로직에만 집중할 수 있는 안정적이고 빠른 배포 환경을 제공해 줍니다.