> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 배포

> 프로덕션 배포와 운영에 관한 질문

## 빌드와 배포

<AccordionGroup>
  <Accordion title="프로덕션 빌드를 만들려면?">
    **API 서버 빌드:**

    ```bash theme={null}
    cd api
    pnpm build
    ```

    빌드 결과물은 `api/dist` 디렉터리에 생성됩니다.

    **Web 프론트엔드 빌드:**

    ```bash theme={null}
    cd web
    pnpm build
    ```

    빌드 결과물은 `web/dist` 디렉터리에 생성됩니다.
  </Accordion>

  <Accordion title="프로덕션 환경에서 실행하려면?">
    **API 서버:**

    ```bash theme={null}
    cd api
    NODE_ENV=production node dist/index.js
    ```

    또는 PM2 사용:

    ```bash theme={null}
    pm2 start dist/index.js --name sonamu-api
    ```

    **Web 서버:**

    Nginx나 Apache로 정적 파일 서빙:

    ```nginx theme={null}
    # nginx.conf
    server {
      listen 80;
      server_name example.com;

      root /var/www/sonamu/web/dist;
      index index.html;

      location / {
        try_files $uri $uri/ /index.html;
      }

      location /api {
        proxy_pass http://localhost:34900;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## 환경 변수

<AccordionGroup>
  <Accordion title="환경 변수를 설정하려면?">
    Sonamu는 `NODE_ENV`에 따라 `.env.{environment}` 파일을 자동으로 로드합니다.

    **`.env.production` 파일 생성:**

    ```bash theme={null}
    # api/.env.production

    # Database (SONAMU_DB_ 접두사 사용)
    SONAMU_DB_HOST=prod-db.example.com
    SONAMU_DB_PORT=5432
    SONAMU_DB_NAME=myapp_production
    SONAMU_DB_USER=postgres
    SONAMU_DB_PASSWORD=secure_password

    # Server
    PORT=34900

    # CORS
    CORS_ORIGIN=https://example.com

    # Cache (Redis)
    REDIS_HOST=redis.example.com
    REDIS_PORT=6379
    REDIS_PASSWORD=redis_password
    ```

    Sonamu가 `SONAMU_DB_*` 환경변수를 읽어 DB 연결을 자동으로 구성하므로, `sonamu.config.ts`에서 직접 연결 정보를 지정할 필요가 없습니다. 기본값을 오버라이드하고 싶다면 `database.defaultOptions`를 사용합니다:

    ```typescript theme={null}
    import { defineConfig } from "sonamu";

    export default defineConfig({
      // ...
      database: {
        database: "pg",
        defaultOptions: {
          pool: { min: 2, max: 10 },
        },
      },
      server: {
        listen: {
          port: Number(process.env.PORT) || 34900,
        },
        // ...
      },
    });
    ```
  </Accordion>

  <Accordion title="민감한 정보를 안전하게 관리하려면?">
    **방법 1: 환경 변수**

    ```bash theme={null}
    # .env 파일은 .gitignore에 추가
    echo ".env" >> .gitignore
    echo ".env.*" >> .gitignore
    ```

    **방법 2: Secret 관리 도구**

    * AWS Secrets Manager
    * HashiCorp Vault
    * Azure Key Vault

    ```typescript theme={null}
    // AWS Secrets Manager 예시
    import { SecretsManager } from "@aws-sdk/client-secrets-manager";

    const client = new SecretsManager({ region: "us-east-1" });

    async function getSecret(secretName: string) {
      const response = await client.getSecretValue({ SecretId: secretName });
      return JSON.parse(response.SecretString!);
    }

    const dbSecret = await getSecret("prod/database");
    ```
  </Accordion>
</AccordionGroup>

***

## 데이터베이스

<AccordionGroup>
  <Accordion title="프로덕션 데이터베이스 마이그레이션을 실행하려면?">
    **1. 마이그레이션 상태 확인:**

    ```bash theme={null}
    NODE_ENV=production pnpm sonamu migrate status
    ```

    **2. Shadow DB에서 테스트 (권장):**

    ```bash theme={null}
    NODE_ENV=production pnpm sonamu migrate shadow-test
    ```

    **3. 실제 DB에 적용:**

    ```bash theme={null}
    NODE_ENV=production pnpm sonamu migrate run
    ```

    **4. 롤백 (필요시):**

    ```bash theme={null}
    NODE_ENV=production pnpm sonamu migrate rollback
    ```
  </Accordion>

  <Accordion title="데이터베이스 백업을 설정하려면?">
    **PostgreSQL 백업:**

    ```bash theme={null}
    # 수동 백업
    pg_dump -h prod-db.example.com -U postgres -d sonamu_prod > backup.sql

    # 압축 백업
    pg_dump -h prod-db.example.com -U postgres -d sonamu_prod | gzip > backup.sql.gz

    # 복원
    gunzip < backup.sql.gz | psql -h prod-db.example.com -U postgres -d sonamu_prod
    ```

    **자동 백업 (Cron):**

    ```bash theme={null}
    # crontab -e
    # 매일 새벽 2시 백업
    0 2 * * * /usr/bin/pg_dump -h prod-db.example.com -U postgres sonamu_prod | gzip > /backups/sonamu_$(date +\%Y\%m\%d).sql.gz

    # 30일 이상 된 백업 삭제
    0 3 * * * find /backups -name "sonamu_*.sql.gz" -mtime +30 -delete
    ```
  </Accordion>
</AccordionGroup>

***

## 모니터링과 로깅

<AccordionGroup>
  <Accordion title="로그를 설정하려면?">
    **Winston 사용:**

    ```typescript theme={null}
    // sonamu.config.ts
    import winston from "winston";

    export default {
      server: {
        logger: winston.createLogger({
          level: process.env.LOG_LEVEL || "info",
          format: winston.format.json(),
          transports: [
            new winston.transports.File({
              filename: "logs/error.log",
              level: "error",
            }),
            new winston.transports.File({
              filename: "logs/combined.log",
            }),
          ],
        }),
      },
    } satisfies SonamuConfig;
    ```

    **로그 사용:**

    ```typescript theme={null}
    class UserModelClass extends BaseModelClass {
      @api({ httpMethod: "POST" })
      async createUser(params: UserSaveParams): Promise<User> {
        this.logger.info("Creating user", { email: params.email });

        try {
          const user = await this.save(params);
          this.logger.info("User created", { userId: user.id });
          return user;
        } catch (error) {
          this.logger.error("Failed to create user", { error, params });
          throw error;
        }
      }
    }
    ```
  </Accordion>

  <Accordion title="헬스체크 엔드포인트를 만들려면?">
    ```typescript theme={null}
    // health.frame.ts
    import { BaseFrameClass, api } from "sonamu";

    class HealthFrameClass extends BaseFrameClass {
      @api({ httpMethod: "GET" }) // guards 생략 = 공개 API
      async healthCheck(): Promise<{
        status: string;
        timestamp: Date;
        uptime: number;
        database: string;
      }> {
        // DB 연결 확인
        let databaseStatus = "ok";
        try {
          await this.getDB("r").raw("SELECT 1");
        } catch (error) {
          databaseStatus = "error";
        }

        return {
          status: databaseStatus === "ok" ? "healthy" : "unhealthy",
          timestamp: new Date(),
          uptime: process.uptime(),
          database: databaseStatus,
        };
      }
    }

    export const HealthFrame = new HealthFrameClass();
    ```
  </Accordion>

  <Accordion title="에러 모니터링을 설정하려면?">
    **Sentry 통합:**

    ```typescript theme={null}
    // sonamu.config.ts
    import * as Sentry from "@sentry/node";

    if (process.env.NODE_ENV === "production") {
      Sentry.init({
        dsn: process.env.SENTRY_DSN,
        environment: process.env.NODE_ENV,
        tracesSampleRate: 1.0,
      });
    }

    export default {
      server: {
        lifecycle: {
          async onStart() {
            console.log("Server started");
          },
          async onError(error: Error) {
            if (process.env.NODE_ENV === "production") {
              Sentry.captureException(error);
            }
          },
        },
      },
    } satisfies SonamuConfig;
    ```
  </Accordion>
</AccordionGroup>

***

## 성능 최적화

<AccordionGroup>
  <Accordion title="프로덕션 성능을 최적화하려면?">
    **1. Connection Pool 설정:**

    ```typescript theme={null}
    export default {
      database: {
        connections: {
          main: {
            pool: {
              min: 2,
              max: 10,
              acquireTimeoutMillis: 30000,
              idleTimeoutMillis: 30000,
            },
          },
        },
      },
    } satisfies SonamuConfig;
    ```

    **2. 캐싱 활성화:**

    ```typescript theme={null}
    import { bentostore } from "bentocache";
    import { redisDriver } from "bentocache/drivers/redis";

    export default {
      server: {
        cache: {
          default: "redis",
          stores: {
            redis: bentostore().useL1Layer(
              redisDriver({
                connection: {
                  host: process.env.REDIS_HOST,
                  port: 6379,
                },
              }),
            ),
          },
        },
      },
    } satisfies SonamuConfig;
    ```

    **3. 압축 활성화:**

    ```typescript theme={null}
    import compress from "@fastify/compress";

    export default {
      server: {
        lifecycle: {
          async onStart(fastify) {
            await fastify.register(compress);
          },
        },
      },
    } satisfies SonamuConfig;
    ```
  </Accordion>

  <Accordion title="정적 파일 캐싱을 설정하려면?">
    **Nginx 설정:**

    ```nginx theme={null}
    server {
      # ...

      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
      }
    }
    ```
  </Accordion>
</AccordionGroup>

***

## Docker

<AccordionGroup>
  <Accordion title="Docker로 배포하려면?">
    **Dockerfile 생성:**

    ```dockerfile theme={null}
    # api/Dockerfile
    FROM node:18-alpine

    WORKDIR /app

    # pnpm 설치
    RUN npm install -g pnpm

    # Dependencies 복사 및 설치
    COPY package.json pnpm-lock.yaml ./
    RUN pnpm install --frozen-lockfile

    # 소스 코드 복사
    COPY . .

    # 빌드
    RUN pnpm build

    # 프로덕션 모드로 실행
    ENV NODE_ENV=production
    EXPOSE 34900

    CMD ["node", "dist/index.js"]
    ```

    **docker-compose.yml:**

    ```yaml theme={null}
    version: "3.8"

    services:
      postgres:
        image: postgres:15-alpine
        environment:
          POSTGRES_DB: sonamu_production
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: ${SONAMU_DB_PASSWORD}
        volumes:
          - postgres_data:/var/lib/postgresql/data
        ports:
          - "5432:5432"

      redis:
        image: redis:7-alpine
        ports:
          - "6379:6379"

      api:
        build: ./api
        environment:
          NODE_ENV: production
          SONAMU_DB_HOST: postgres
          SONAMU_DB_PORT: 5432
          SONAMU_DB_NAME: sonamu_production
          SONAMU_DB_USER: postgres
          SONAMU_DB_PASSWORD: ${SONAMU_DB_PASSWORD}
          REDIS_HOST: redis
          REDIS_PORT: 6379
        ports:
          - "34900:34900"
        depends_on:
          - postgres
          - redis

      web:
        build: ./web
        ports:
          - "80:80"
        depends_on:
          - api

    volumes:
      postgres_data:
    ```

    **실행:**

    ```bash theme={null}
    docker-compose up -d
    ```
  </Accordion>
</AccordionGroup>

***

## CI/CD

<AccordionGroup>
  <Accordion title="GitHub Actions로 자동 배포를 설정하려면?">
    **.github/workflows/deploy.yml:**

    ```yaml theme={null}
    name: Deploy

    on:
      push:
        branches: [main]

    jobs:
      deploy:
        runs-on: ubuntu-latest

        steps:
          - uses: actions/checkout@v3

          - name: Setup Node.js
            uses: actions/setup-node@v3
            with:
              node-version: "18"

          - name: Install pnpm
            run: npm install -g pnpm

          - name: Install dependencies
            run: pnpm install

          - name: Run tests
            run: pnpm test

          - name: Build
            run: pnpm build

          - name: Deploy to server
            uses: appleboy/ssh-action@master
            with:
              host: ${{ secrets.SERVER_HOST }}
              username: ${{ secrets.SERVER_USER }}
              key: ${{ secrets.SSH_PRIVATE_KEY }}
              script: |
                cd /var/www/sonamu
                git pull
                pnpm install
                pnpm build
                pm2 restart sonamu-api
    ```
  </Accordion>
</AccordionGroup>

***

## 보안

<AccordionGroup>
  <Accordion title="HTTPS를 설정하려면?">
    **Let's Encrypt + Nginx:**

    ```bash theme={null}
    # Certbot 설치
    sudo apt install certbot python3-certbot-nginx

    # 인증서 발급
    sudo certbot --nginx -d example.com -d www.example.com

    # 자동 갱신 테스트
    sudo certbot renew --dry-run
    ```

    **Nginx HTTPS 설정:**

    ```nginx theme={null}
    server {
      listen 443 ssl http2;
      server_name example.com;

      ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

      ssl_protocols TLSv1.2 TLSv1.3;
      ssl_ciphers HIGH:!aNULL:!MD5;

      # ...
    }

    # HTTP to HTTPS 리다이렉트
    server {
      listen 80;
      server_name example.com;
      return 301 https://$server_name$request_uri;
    }
    ```
  </Accordion>

  <Accordion title="CORS를 설정하려면?">
    ```typescript theme={null}
    // sonamu.config.ts
    export default {
      server: {
        cors: {
          origin: process.env.CORS_ORIGIN || "http://localhost:5173",
          credentials: true,
          methods: ["GET", "POST", "PUT", "DELETE", "PATCH"],
        },
      },
    } satisfies SonamuConfig;
    ```
  </Accordion>

  <Accordion title="Rate Limiting을 설정하려면?">
    ```typescript theme={null}
    import rateLimit from "@fastify/rate-limit";

    export default {
      server: {
        lifecycle: {
          async onStart(fastify) {
            await fastify.register(rateLimit, {
              max: 100, // 100 requests
              timeWindow: "1 minute",
            });
          },
        },
      },
    } satisfies SonamuConfig;
    ```
  </Accordion>
</AccordionGroup>

***

## Best Practices

<AccordionGroup>
  <Accordion title="프로덕션 체크리스트">
    **보안:**

    * [ ] HTTPS 설정
    * [ ] 환경 변수로 민감 정보 관리
    * [ ] CORS 올바르게 설정
    * [ ] Rate Limiting 설정
    * [ ] SQL Injection 방지 (Puri 사용)
    * [ ] XSS 방지 (입력 검증)

    **성능:**

    * [ ] Connection Pool 최적화
    * [ ] Redis 캐싱 활성화
    * [ ] 정적 파일 캐싱
    * [ ] DB 인덱스 최적화
    * [ ] N+1 쿼리 제거

    **모니터링:**

    * [ ] 헬스체크 엔드포인트
    * [ ] 에러 모니터링 (Sentry)
    * [ ] 로그 수집 (Winston)
    * [ ] DB 백업 자동화

    **배포:**

    * [ ] 프로덕션 환경 변수 설정
    * [ ] 마이그레이션 테스트 (Shadow DB)
    * [ ] CI/CD 파이프라인
    * [ ] 롤백 계획
  </Accordion>
</AccordionGroup>

***

## 관련 문서

* [환경 설정](/ko/configuration/environment-variables)
* [보안](/ko/advanced-features/security)
* [성능 최적화](/ko/troubleshooting/performance/query-optimization)
* [Docker 배포](/ko/deployment/docker)
