18. 배포
SvelteKit 애플리케이션을 실제 서비스로 만들기 위해서는 적절한 배포 전략이 필요합니다. 다양한 플랫폼과 환경에 최적화된 어댑터 시스템을 통해 서버리스, 정적 호스팅, 컨테이너 등 원하는 방식으로 배포할 수 있습니다. 이 장에서는 SvelteKit의 어댑터 시스템을 이해하고, 주요 플랫폼별 배포 방법과 Docker를 활용한 컨테이너화까지 완전히 마스터해보겠습니다.
18.1 어댑터 이해하기
다양한 플랫폼 어댑터
SvelteKit의 어댑터는 빌드된 앱을 특정 배포 환경에 맞게 변환하는 플러그인입니다. 각 플랫폼의 고유한 요구사항과 최적화 방법을 처리하여 동일한 코드베이스로 다양한 환경에 배포할 수 있게 합니다. 프레임워크 정의 인프라(FDI) 개념을 통해 플랫폼이 애플리케이션의 요구사항을 자동으로 파악하고 최적화합니다.
공식 어댑터 목록
// 주요 공식 어댑터와 특징
const adapters = {
// 자동 어댑터 - 플랫폼 자동 감지
'@sveltejs/adapter-auto': {
pros: [
'자동 플랫폼 감지',
'설정 불필요',
'초보자 친화적',
],
cons: ['세부 설정 제한', '빌드 시간 증가'],
platforms: ['Vercel', 'Netlify', 'Cloudflare', 'Azure'],
},
// Vercel 어댑터
'@sveltejs/adapter-vercel': {
pros: ['엣지 함수 지원', 'ISR 지원', '자동 최적화'],
cons: ['Vercel 플랫폼 종속'],
features: ['스트리밍', '이미지 최적화', '분석'],
},
// Netlify 어댑터
'@sveltejs/adapter-netlify': {
pros: ['엣지 함수 지원', '자동 HTTPS', 'Forms 통합'],
cons: ['일부 Node.js API 제한'],
features: ['Split testing', 'Identity', 'Functions'],
},
// Node.js 어댑터
'@sveltejs/adapter-node': {
pros: [
'완전한 Node.js 지원',
'유연한 배포',
'PM2 호환',
],
cons: ['수동 서버 관리 필요'],
features: ['커스텀 서버', 'WebSocket', '파일 시스템'],
},
// 정적 어댑터
'@sveltejs/adapter-static': {
pros: ['무료 호스팅 가능', '빠른 로딩', 'CDN 친화적'],
cons: ['SSR 불가', '동적 기능 제한'],
features: ['GitHub Pages', 'Surge.sh', 'S3'],
},
// Cloudflare 어댑터
'@sveltejs/adapter-cloudflare': {
pros: ['전역 엣지 네트워크', 'Workers KV', '무료 티어'],
cons: ['Workers 런타임 제한'],
features: ['Durable Objects', 'R2 스토리지'],
},
};
어댑터 설정 방법
// svelte.config.js - 어댑터 설정
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
// 어댑터별 옵션
// adapter-auto는 옵션을 받지 않음
}),
// 빌드 출력 설정
paths: {
base: process.env.BASE_PATH || '',
},
// 프리렌더링 설정
prerender: {
handleHttpError: 'warn',
handleMissingId: 'warn',
},
// CSP 설정
csp: {
mode: 'auto',
directives: {
'script-src': ['self'],
},
},
},
};
export default config;
정적 어댑터
정적 어댑터는 전체 애플리케이션을 HTML, CSS, JavaScript 파일로 프리렌더링합니다. 서버 사이드 렌더링이 필요 없는 블로그, 문서 사이트, 포트폴리오 등에 적합합니다. GitHub Pages, Netlify, Vercel 등 거의 모든 정적 호스팅 서비스에서 무료로 호스팅할 수 있습니다.
// svelte.config.js - 정적 어댑터 설정
import adapter from '@sveltejs/adapter-static';
export default {
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: '404.html',
precompress: false,
strict: true,
}),
prerender: {
entries: ['*'],
handleHttpError: ({ path, referrer, message }) => {
// 404 에러 처리
if (message.includes('404')) {
console.warn(`404: ${path}`);
return;
}
throw new Error(message);
},
},
},
};
<!-- src/routes/+layout.js -->
<script>
// 모든 페이지를 프리렌더링
export const prerender = true;
// 정적 어댑터 사용 시 SSR 비활성화
export const ssr = false;
// 클라이언트 사이드 라우터 사용
export const csr = true;
// 트레일링 슬래시 설정
export const trailingSlash = 'always';
</script>
<!-- static/404.html - 폴백 페이지 -->
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<title>페이지를 찾을 수 없습니다</title>
<style>
body {
font-family: system-ui;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
}
.error {
text-align: center;
}
</style>
</head>
<body>
<div class="error">
<h1>404</h1>
<p>페이지를 찾을 수 없습니다</p>
<a href="/">홈으로 돌아가기</a>
</div>
</body>
</html>
Node 어댑터
Node 어댑터는 표준 Node.js 서버를 생성하여 VPS, Docker, AWS EC2 등 다양한 환경에서 실행할 수 있습니다. Express, Fastify 같은 프레임워크와 통합하거나 PM2로 프로세스를 관리할 수 있습니다. WebSocket, 파일 업로드, 데이터베이스 직접 연결 등 서버 사이드 기능을 완전히 활용할 수 있습니다.
// svelte.config.js - Node 어댑터 설정
import adapter from '@sveltejs/adapter-node';
export default {
kit: {
adapter: adapter({
out: 'build',
precompress: true,
envPrefix: 'APP_',
polyfill: true,
}),
},
};
// server.js - Express와 통합
import { handler } from './build/handler.js';
import express from 'express';
import compression from 'compression';
import helmet from 'helmet';
const app = express();
const PORT = process.env.PORT || 3000;
// 보안 미들웨어
app.use(
helmet({
contentSecurityPolicy: false,
})
);
// 압축
app.use(compression());
// 정적 파일 서빙 (캐싱 설정)
app.use(
express.static('build/client', {
maxAge: '1y',
immutable: true,
})
);
// 헬스체크 엔드포인트
app.get('/health', (req, res) => {
res.json({ status: 'healthy', timestamp: Date.now() });
});
// SvelteKit 핸들러
app.use(handler);
// Graceful shutdown
const server = app.listen(PORT, () => {
console.log(`서버가 포트 ${PORT}에서 실행 중입니다`);
});
process.on('SIGTERM', () => {
console.log('SIGTERM 신호 수신, 서버 종료 중...');
server.close(() => {
console.log('서버가 정상적으로 종료되었습니다');
process.exit(0);
});
});
// ecosystem.config.js - PM2 설정
module.exports = {
apps: [{
name: 'sveltekit-app',
script: './build/index.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
PORT: 3000
},
error_file: './logs/err.log',
out_file: './logs/out.log',
log_date_format: 'YYYY-MM-DD HH:mm:ss',
max_memory_restart: '500M',
// 무중단 재시작 설정
wait_ready: true,
listen_timeout: 3000,
kill_timeout: 5000
}]
};
18.2 주요 플랫폼 배포
Vercel 배포
Vercel은 SvelteKit의 공식 후원사로 자동 최적화, 엣지 함수, ISR 등 고급 기능을 제공합니다. Git 푸시만으로 자동 배포되며, 프리뷰 배포와 롤백 기능으로 안전한 배포 프로세스를 구축할 수 있습니다. 서버리스 아키텍처로 자동 스케일링되어 트래픽 급증에도 안정적으로 대응합니다.
// svelte.config.js - Vercel 어댑터 설정
import adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter({
runtime: 'nodejs20.x',
regions: ['iad1'],
maxDuration: 10,
memory: 1024,
// 엣지 함수 설정
edge: false,
split: false,
// 이미지 최적화
images: {
sizes: [640, 828, 1200, 1920],
formats: ['image/avif', 'image/webp'],
minimumCacheTTL: 300,
},
}),
},
};
// src/routes/blog/[slug]/+page.server.js - ISR 설정
export const config = {
isr: {
expiration: 60,
bypassToken: process.env.BYPASS_TOKEN,
allowQuery: ['preview'],
},
};
export async function load({ params, setHeaders }) {
const post = await getPost(params.slug);
// 캐시 제어
setHeaders({
'cache-control': 'public, max-age=3600',
});
return {
post,
};
}
{
"rewrites": [
{
"source": "/api/:path*",
"destination": "https://api.example.com/:path*"
}
],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Content-Type-Options",
"value": "nosniff"
},
{
"key": "X-Frame-Options",
"value": "DENY"
}
]
}
],
"functions": {
"src/routes/api/webhook/+server.js": {
"maxDuration": 30
}
}
}
Netlify 배포
Netlify는 강력한 빌드 시스템과 엣지 함수를 제공하는 정적 사이트 호스팅 플랫폼입니다. 폼 처리, Identity 서비스, Split Testing 등 추가 기능을 쉽게 통합할 수 있습니다. Deno 기반 엣지 함수로 사용자와 가까운 위치에서 SSR을 실행하여 성능을 최적화합니다.
// svelte.config.js - Netlify 어댑터 설정
import adapter from '@sveltejs/adapter-netlify';
export default {
kit: {
adapter: adapter({
edge: true,
split: false,
}),
},
};
# netlify.toml - Netlify 설정
[build]
command = "npm run build"
publish = "build"
[build.environment]
NODE_VERSION = "20"
[[redirects]]
from = "/api/*"
to = "/.netlify/functions/:splat"
status = 200
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-Content-Type-Options = "nosniff"
Referrer-Policy = "strict-origin-when-cross-origin"
[functions]
directory = "netlify/functions"
node_bundler = "esbuild"
[[edge_functions]]
path = "/api/*"
function = "api-handler"
<!-- src/routes/contact/+page.svelte - Netlify Forms -->
<script>
export const prerender = true;
</script>
<form
name="contact"
method="POST"
data-netlify="true"
data-netlify-honeypot="bot-field"
>
<input type="hidden" name="form-name" value="contact" />
<!-- 허니팟 필드 (봇 방지) -->
<p style="display: none">
<label>
이 필드를 채우지 마세요:
<input name="bot-field" />
</label>
</p>
<label>
이름:
<input type="text" name="name" required />
</label>
<label>
이메일:
<input type="email" name="email" required />
</label>
<label>
메시지:
<textarea name="message" required></textarea>
</label>
<button type="submit">전송</button>
</form>
AWS 배포
AWS는 다양한 서비스를 조합하여 확장 가능한 배포 환경을 구축할 수 있습니다. EC2 인스턴스, ECS 컨테이너, Lambda 함수 등 요구사항에 맞는 서비스를 선택할 수 있습니다. CloudFront CDN, RDS 데이터베이스, S3 스토리지 등과 통합하여 엔터프라이즈급 인프라를 구성할 수 있습니다.
// AWS Lambda용 서버리스 핸들러
import { handler } from './build/handler.js';
import serverlessExpress from '@codegenie/serverless-express';
export const lambdaHandler = serverlessExpress({
app: handler,
});
# serverless.yml - Serverless Framework 설정
service: sveltekit-app
provider:
name: aws
runtime: nodejs20.x
region: ap-northeast-2
stage: ${opt:stage, 'dev'}
environment:
NODE_ENV: production
DATABASE_URL: ${env:DATABASE_URL}
iam:
role:
statements:
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource: arn:aws:s3:::${self:custom.bucket}/*
custom:
bucket: ${self:service}-${self:provider.stage}-assets
functions:
app:
handler: lambda.handler
events:
- http:
path: /
method: ANY
- http:
path: /{proxy+}
method: ANY
timeout: 30
memorySize: 1024
plugins:
- serverless-plugin-optimize
- serverless-offline
18.3 Docker와 컨테이너화
Dockerfile 작성
Docker를 사용하면 애플리케이션과 실행 환경을 하나의 이미지로 패키징할 수 있습니다. 멀티스테이지 빌드를 통해 최종 이미지 크기를 최소화하고 보안을 강화할 수 있습니다. 개발 환경과 프로덕션 환경의 일관성을 보장하여 "내 컴퓨터에서는 되는데" 문제를 해결합니다.
# Dockerfile - 프로덕션용 멀티스테이지 빌드
# 빌드 스테이지
FROM node:20-alpine AS builder
WORKDIR /app
# 의존성 캐싱을 위해 package 파일만 먼저 복사
COPY package*.json ./
RUN npm ci --only=production
# 개발 의존성 설치 (빌드에 필요)
RUN npm ci
# 소스 코드 복사 및 빌드
COPY . .
RUN npm run build
# 프로덕션 의존성만 남기기
RUN npm prune --production
# 실행 스테이지
FROM node:20-alpine AS runner
WORKDIR /app
# 필요한 파일만 복사
COPY --from=builder /app/build build/
COPY --from=builder /app/package.json .
COPY --from=builder /app/node_modules node_modules/
# 비루트 사용자로 실행
USER node
# 환경 변수 설정
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
# 헬스체크
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {r.statusCode === 200 ? process.exit(0) : process.exit(1)})"
CMD ["node", "build"]
# Dockerfile.dev - 개발용 컨테이너
FROM node:20-alpine
WORKDIR /app
# pnpm 설치 (더 빠른 패키지 관리)
RUN npm install -g pnpm
# 의존성 파일 복사
COPY package.json pnpm-lock.yaml ./
# 의존성 설치
RUN pnpm install
# 소스 코드는 볼륨으로 마운트
EXPOSE 5173
EXPOSE 24678
CMD ["pnpm", "dev", "--host", "0.0.0.0"]
최적화된 이미지 빌드
Docker 이미지를 최적화하면 배포 속도가 향상되고 스토리지 비용이 절감됩니다. 레이어 캐싱을 활용하고 불필요한 파일을 제외하여 이미지 크기를 최소화합니다. 보안 스캐닝과 취약점 패치를 통해 안전한 컨테이너 환경을 유지합니다.
# .dockerignore - 빌드에서 제외할 파일
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.env.*
!.env.example
.vscode
.idea
*.swp
*.swo
.DS_Store
Thumbs.db
.svelte-kit
build
.vercel
.netlify
coverage
.nyc_output
*.log
# docker-compose.yml - 개발 환경 구성
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- '5173:5173'
- '24678:24678'
volumes:
- .:/app
- /app/node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=postgres://user:pass@db:5432/myapp
depends_on:
- db
networks:
- app-network
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=myapp
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- '5432:5432'
networks:
- app-network
volumes:
postgres_data:
networks:
app-network:
driver: bridge
컨테이너 오케스트레이션
프로덕션 환경에서는 Kubernetes나 Docker Swarm을 사용하여 컨테이너를 관리합니다. 자동 스케일링, 로드 밸런싱, 무중단 배포 등 고급 기능을 활용할 수 있습니다. 헬스체크와 모니터링을 통해 서비스의 안정성을 보장합니다.
# k8s-deployment.yaml - Kubernetes 배포 설정
apiVersion: apps/v1
kind: Deployment
metadata:
name: sveltekit-app
labels:
app: sveltekit
spec:
replicas: 3
selector:
matchLabels:
app: sveltekit
template:
metadata:
labels:
app: sveltekit
spec:
containers:
- name: app
image: myregistry/sveltekit-app:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: 'production'
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
resources:
requests:
memory: '256Mi'
cpu: '250m'
limits:
memory: '512Mi'
cpu: '500m'
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: sveltekit-service
spec:
selector:
app: sveltekit
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancer
#!/bin/bash
# deploy.sh - 무중단 배포 스크립트
# 변수 설정
APP_NAME="sveltekit-app"
IMAGE_TAG=$(git rev-parse --short HEAD)
REGISTRY="myregistry"
# Docker 이미지 빌드
echo "이미지 빌드 중..."
docker build -t $REGISTRY/$APP_NAME:$IMAGE_TAG .
docker tag $REGISTRY/$APP_NAME:$IMAGE_TAG $REGISTRY/$APP_NAME:latest
# 레지스트리에 푸시
echo "이미지 푸시 중..."
docker push $REGISTRY/$APP_NAME:$IMAGE_TAG
docker push $REGISTRY/$APP_NAME:latest
# Kubernetes 배포
echo "Kubernetes 배포 중..."
kubectl set image deployment/$APP_NAME app=$REGISTRY/$APP_NAME:$IMAGE_TAG
# 배포 상태 확인
kubectl rollout status deployment/$APP_NAME
echo "배포 완료!"
정리
SvelteKit의 배포 시스템을 완전히 마스터했습니다! 이제 다음과 같은 핵심 개념들을 이해했습니다:
핵심 요약
- 어댑터 시스템: 다양한 플랫폼에 최적화된 빌드를 생성하는 플러그인 시스템
- 플랫폼별 배포: Vercel, Netlify, AWS 등 주요 플랫폼의 특징과 최적화 방법
- Docker 컨테이너화: 멀티스테이지 빌드와 오케스트레이션을 통한 확장 가능한 배포
적절한 배포 방법 선택 가이드
프로젝트 유형 | 권장 어댑터 | 배포 플랫폼 |
---|---|---|
정적 사이트 | adapter-static | GitHub Pages, Netlify |
서버리스 앱 | adapter-vercel | Vercel, Netlify |
풀스택 앱 | adapter-node | AWS, Docker, VPS |
엔터프라이즈 | adapter-node | Kubernetes, ECS |
실무 활용 팁
- 개발 초기에는 adapter-auto로 시작하고 프로덕션 전에 특정 어댑터로 전환
- 정적 콘텐츠는 CDN을 통해 서빙하여 성능 최적화
- Docker 이미지는 멀티스테이지 빌드로 크기 최소화
- 환경 변수는 빌드 시점과 런타임을 구분하여 관리
다음 단계: 19장 "실무 패턴과 아키텍처"에서는 대규모 SvelteKit 애플리케이션을 위한 설계 패턴과 모범 사례를 알아보겠습니다. 확장 가능하고 유지보수가 쉬운 애플리케이션 구조를 설계해보세요!