> ## 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.

# 데이터베이스 인증 정보

> PostgreSQL 연결 정보 안전하게 설정하기

Sonamu는 PostgreSQL 데이터베이스를 사용합니다. 데이터베이스 연결 정보는 환경변수로 관리하여 코드에서 분리하고, 환경별로 다른 DB를 사용할 수 있습니다.

<Info>Sonamu v2부터 PostgreSQL을 공식 지원하며, MySQL에서 마이그레이션을 지원합니다.</Info>

## 필수 환경변수

데이터베이스 연결에 필요한 환경변수입니다.

```bash title=".env" theme={null}
# 데이터베이스 호스트
DB_HOST=localhost

# 포트 (PostgreSQL 기본값: 5432)
DB_PORT=5432

# 사용자 이름
DB_USER=postgres

# 비밀번호
DB_PASSWORD=your-db-password

# 데이터베이스 이름
DATABASE_NAME=myproject_dev
```

<Warning>`DB_PASSWORD`는 절대 코드에 하드코딩하지 마세요! 환경변수로만 관리해야 합니다.</Warning>

## sonamu.config.ts 설정

### 기본 설정

```typescript title="sonamu.config.ts" theme={null}
import { defineConfig } from "sonamu";

export default defineConfig({
  database: {
    database: "pg", // PostgreSQL
    name: process.env.DATABASE_NAME ?? "myproject",
    defaultOptions: {
      connection: {
        host: process.env.DB_HOST || "0.0.0.0",
        port: Number(process.env.DB_PORT) || 5432,
        user: process.env.DB_USER || "postgres",
        password: process.env.DB_PASSWORD,
      },
    },
  },

  // ...
});
```

<Info>
  `password`는 기본값을 설정하지 마세요. 환경변수가 없으면 `undefined`로 두어 명시적으로 에러가
  발생하도록 합니다.
</Info>

### 연결 풀 설정

```typescript theme={null}
database: {
  database: "pg",
  name: process.env.DATABASE_NAME ?? "myproject",
  defaultOptions: {
    connection: {
      host: process.env.DB_HOST || "0.0.0.0",
      port: Number(process.env.DB_PORT) || 5432,
      user: process.env.DB_USER || "postgres",
      password: process.env.DB_PASSWORD,
    },
    pool: {
      min: 2,
      max: 10,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    },
  },
}
```

**연결 풀 옵션:**

| 옵션                        | 기본값   | 설명               |
| ------------------------- | ----- | ---------------- |
| `min`                     | 2     | 최소 연결 수          |
| `max`                     | 10    | 최대 연결 수          |
| `idleTimeoutMillis`       | 10000 | 유휴 연결 제거 시간 (ms) |
| `connectionTimeoutMillis` | 0     | 연결 대기 시간 (ms)    |

<Tip>트래픽이 많은 프로덕션 환경에서는 `max`를 20-50으로 증가시키세요.</Tip>

## 환경별 설정

<Tabs>
  <Tab title="로컬 개발" icon="laptop-code">
    ```bash title=".env.development" theme={null}
    # 로컬 PostgreSQL
    DB_HOST=localhost
    DB_PORT=5432
    DB_USER=postgres
    DB_PASSWORD=your-dev-password
    DATABASE_NAME=myproject_dev
    ```

    **Docker Compose로 PostgreSQL 실행:**

    ```yaml title="docker-compose.yml" theme={null}
    version: '3.8'
    services:
      postgres:
        image: postgres:16
        ports:
          - "5432:5432"
        environment:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: your-dev-password
          POSTGRES_DB: myproject_dev
        volumes:
          - postgres_data:/var/lib/postgresql/data

    volumes:
      postgres_data:
    ```

    ```bash theme={null}
    # 시작
    docker-compose up -d

    # 중지
    docker-compose down
    ```
  </Tab>

  <Tab title="스테이징" icon="flask">
    ```bash title=".env.staging" theme={null}
    # 원격 데이터베이스
    DB_HOST=staging-db.mycompany.com
    DB_PORT=5432
    DB_USER=myproject_staging
    DB_PASSWORD=your-staging-password  # 최소 32자 강력한 비밀번호
    DATABASE_NAME=myproject_staging
    ```

    **특징:**

    * 전용 DB 사용자 (읽기/쓰기 권한만)
    * SSL 연결 권장
    * 프로덕션과 유사한 데이터 볼륨

    **SSL 설정 (선택):**

    ```typescript theme={null}
    connection: {
      host: process.env.DB_HOST,
      port: Number(process.env.DB_PORT),
      user: process.env.DB_USER,
      password: process.env.DB_PASSWORD,
      ssl: {
        rejectUnauthorized: false,  // 자체 서명 인증서
      },
    }
    ```
  </Tab>

  <Tab title="프로덕션" icon="server">
    ```bash title=".env.production" theme={null}
    # 고가용성 데이터베이스
    DB_HOST=prod-db-primary.mycompany.com
    DB_PORT=5432
    DB_USER=myproject_prod
    DB_PASSWORD=your-production-password  # 최소 64자 매우 강력한 비밀번호
    DATABASE_NAME=myproject_prod
    ```

    **연결 풀 최적화:**

    ```typescript theme={null}
    pool: {
      min: 5,
      max: 50,
      idleTimeoutMillis: 30000,
      connectionTimeoutMillis: 2000,
    }
    ```

    **읽기 복제본 설정 (선택):**

    ```typescript theme={null}
    database: {
      database: "pg",
      name: process.env.DATABASE_NAME ?? "myproject",
      defaultOptions: {
        connection: {
          host: process.env.DB_HOST,
          port: Number(process.env.DB_PORT),
          user: process.env.DB_USER,
          password: process.env.DB_PASSWORD,
        },
      },
      environments: {
        production: {
          connection: {
            host: process.env.DB_HOST,
            port: Number(process.env.DB_PORT),
            user: process.env.DB_USER,
            password: process.env.DB_PASSWORD,
          },
          pool: {
            min: 5,
            max: 50,
          },
        },
        // 읽기 전용 복제본 (Slave DB)
        production_slave: {
          connection: {
            host: process.env.DB_READ_HOST,
            port: Number(process.env.DB_READ_PORT),
            user: process.env.DB_READ_USER,
            password: process.env.DB_READ_PASSWORD,
          },
          pool: {
            min: 3,
            max: 30,
          },
        },
      },
    }
    ```

    <Info>
      Sonamu는 `environments.production_slave` 설정을 자동으로 인식하여 읽기 전용 쿼리에 사용합니다. 자세한 내용은 [데이터베이스 설정](/ko/configuration/sonamu-config/database)을 참고하세요.
    </Info>
  </Tab>
</Tabs>

## PostgreSQL 사용자 생성

<Steps>
  <Step title="PostgreSQL 접속" icon="terminal">
    ```bash theme={null}
    # 로컬
    psql -U postgres

    # 원격
    psql -h db.example.com -U postgres
    ```
  </Step>

  <Step title="데이터베이스 생성" icon="database">
    `sql CREATE DATABASE myproject_dev; `
  </Step>

  <Step title="사용자 생성 및 권한 부여" icon="user-plus">
    ```sql theme={null}
    -- 사용자 생성
    CREATE USER myproject_user WITH PASSWORD 'your-secure-password';

    -- 데이터베이스 권한 부여
    GRANT ALL PRIVILEGES ON DATABASE myproject_dev TO myproject_user;

    -- 스키마 권한 부여
    \c myproject_dev
    GRANT ALL PRIVILEGES ON SCHEMA public TO myproject_user;
    GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO myproject_user;
    GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO myproject_user;
    ```
  </Step>

  <Step title="연결 테스트" icon="check">
    ```bash theme={null}
    psql -h localhost -U myproject_user -d myproject_dev
    ```

    비밀번호 입력 후 접속되면 성공!
  </Step>
</Steps>

## 보안 모범 사례

<Accordion title="강력한 비밀번호" icon="key">
  ### 비밀번호 요구사항

  **개발 환경:**

  * 최소 8자 이상
  * 영문, 숫자 혼합

  **스테이징/프로덕션:**

  * 최소 32자 이상
  * 영문 대소문자, 숫자, 특수문자 혼합
  * 사전에 없는 무작위 문자열

  ### 비밀번호 생성

  ```bash theme={null}
  # 32자 랜덤 비밀번호
  openssl rand -base64 24

  # 또는
  pwgen -s 32 1
  ```

  **예시:**

  ```bash theme={null}
  DB_PASSWORD=your-32-character-secure-password
  ```
</Accordion>

<Accordion title="최소 권한 원칙" icon="shield">
  ### 권한 분리

  ```sql theme={null}
  -- ✅ 애플리케이션 사용자 (읽기/쓰기만)
  CREATE USER app_user WITH PASSWORD 'your-app-password';
  GRANT CONNECT ON DATABASE myproject TO app_user;
  GRANT USAGE ON SCHEMA public TO app_user;
  GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;

  -- ✅ 읽기 전용 사용자 (분석/리포팅)
  CREATE USER readonly_user WITH PASSWORD 'your-readonly-password';
  GRANT CONNECT ON DATABASE myproject TO readonly_user;
  GRANT USAGE ON SCHEMA public TO readonly_user;
  GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly_user;

  -- ❌ SUPERUSER 권한은 절대 사용 금지
  -- CREATE USER admin WITH SUPERUSER;
  ```

  <Danger>
    프로덕션 애플리케이션에 SUPERUSER 권한을 주지 마세요! 보안 위험이 매우 큽니다.
  </Danger>
</Accordion>

<Accordion title="SSL/TLS 암호화" icon="lock">
  ### SSL 연결 설정

  **필수 환경:**

  * 프로덕션 (필수)
  * 스테이징 (권장)
  * 개발 (선택)

  ```typescript theme={null}
  connection: {
    host: process.env.DB_HOST,
    port: Number(process.env.DB_PORT),
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    ssl: process.env.NODE_ENV === 'production' ? {
      rejectUnauthorized: true,
      ca: fs.readFileSync('/path/to/ca-certificate.crt').toString(),
    } : false,
  }
  ```

  ### AWS RDS SSL

  ```bash theme={null}
  # AWS RDS CA 인증서 다운로드
  wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem

  # .env에 경로 추가
  DB_SSL_CA=/path/to/global-bundle.pem
  ```

  ```typescript theme={null}
  ssl: {
    rejectUnauthorized: true,
    ca: fs.readFileSync(process.env.DB_SSL_CA!).toString(),
  }
  ```
</Accordion>

<Accordion title="연결 정보 암호화" icon="file-shield">
  ### Vault 사용 (권장)

  ```bash theme={null}
  # HashiCorp Vault에서 비밀번호 가져오기
  export DB_PASSWORD=$(vault kv get -field=password secret/database/prod)
  ```

  ### AWS Secrets Manager

  ```typescript theme={null}
  import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";

  const client = new SecretsManagerClient({ region: "ap-northeast-2" });
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: "prod/database/credentials" }),
  );

  const secrets = JSON.parse(response.SecretString!);

  export default defineConfig({
    database: {
      defaultOptions: {
        connection: {
          host: secrets.host,
          port: secrets.port,
          user: secrets.username,
          password: secrets.password,
        },
      },
    },
  });
  ```
</Accordion>

## 연결 테스트

<CodeGroup>
  ```bash CLI theme={null}
  # psql로 직접 연결 테스트
  psql "postgresql://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DATABASE_NAME"

  # 연결 성공 시

  # myproject_dev=>

  ```

  ```typescript Node.js theme={null}
  // 프로그래밍 방식 테스트
  import pg from "pg";

  const client = new pg.Client({
    host: process.env.DB_HOST,
    port: Number(process.env.DB_PORT),
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DATABASE_NAME,
  });

  try {
    await client.connect();
    const result = await client.query("SELECT NOW()");
    console.log("✅ 연결 성공:", result.rows[0]);
  } catch (error) {
    console.error("❌ 연결 실패:", error);
  } finally {
    await client.end();
  }
  ```

  ```typescript Sonamu theme={null}
  // Sonamu에서 연결 확인
  import { db } from "sonamu";

  // 쿼리 실행으로 연결 테스트
  const result = await db.query("SELECT NOW() as current_time");
  console.log("✅ DB 연결 성공:", result.rows[0]);
  ```
</CodeGroup>

## 문제 해결

<Accordion title="연결 거부 (ECONNREFUSED)" icon="ban">
  **증상:**

  ```
  Error: connect ECONNREFUSED 127.0.0.1:5432
  ```

  **원인:**

  1. PostgreSQL이 실행 중이 아님
  2. 잘못된 호스트/포트
  3. 방화벽 차단

  **해결:**

  ```bash theme={null}
  # 1. PostgreSQL 실행 확인
  sudo systemctl status postgresql

  # 또는 (macOS)
  brew services list | grep postgresql

  # 2. PostgreSQL 시작
  sudo systemctl start postgresql

  # 또는 (macOS)
  brew services start postgresql

  # 3. 포트 확인
  netstat -an | grep 5432

  # 4. .env 파일 확인
  cat .env | grep DB_
  ```
</Accordion>

<Accordion title="인증 실패 (authentication failed)" icon="user-lock">
  **증상:**

  ```
  error: password authentication failed for user "myproject_user"
  ```

  **원인:**

  1. 잘못된 사용자 이름/비밀번호
  2. 사용자가 존재하지 않음
  3. `pg_hba.conf` 설정 문제

  **해결:**

  ```bash theme={null}
  # 1. 사용자 확인
  psql -U postgres -c "\du"

  # 2. 비밀번호 재설정
  psql -U postgres
  ```

  ```sql theme={null}
  ALTER USER myproject_user WITH PASSWORD 'your-new-password';
  ```

  ```bash theme={null}
  # 3. pg_hba.conf 확인 (Linux)
  sudo cat /etc/postgresql/16/main/pg_hba.conf

  # 로컬 연결 허용 확인
  # local   all   all   trust
  # host    all   all   127.0.0.1/32   md5
  ```
</Accordion>

<Accordion title="연결 시간 초과 (timeout)" icon="clock">
  **증상:**

  ```
  Error: Connection timeout
  ```

  **원인:**

  1. 네트워크 문제
  2. DB 서버 과부하
  3. 연결 풀 고갈

  **해결:**

  ```typescript theme={null}
  // 1. 타임아웃 증가
  connection: {
    connectionTimeoutMillis: 10000,  // 10초
  }

  // 2. 연결 풀 크기 증가
  pool: {
    max: 20,
  }

  // 3. 네트워크 확인
  // ping DB 서버
  // telnet db.example.com 5432
  ```
</Accordion>

<Accordion title="너무 많은 연결 (too many connections)" icon="users">
  **증상:**

  ```
  FATAL: sorry, too many clients already
  ```

  **원인:**

  * PostgreSQL의 `max_connections` 한계 초과

  **해결:**

  ```sql theme={null}
  -- 현재 연결 수 확인
  SELECT count(*) FROM pg_stat_activity;

  -- max_connections 확인
  SHOW max_connections;

  -- max_connections 증가 (postgresql.conf)
  -- max_connections = 200
  ```

  ```typescript theme={null}
  // 애플리케이션 연결 풀 제한
  pool: {
    max: 10,  // max_connections보다 충분히 작게
  }
  ```

  <Warning>
    `max_connections`를 무작정 늘리면 메모리 사용량이 증가합니다. 연결 풀 크기를 먼저 조정하세요.
  </Warning>
</Accordion>

## 성능 최적화

### 연결 풀 튜닝

```typescript theme={null}
// 트래픽 패턴에 따른 설정

// 낮은 트래픽 (개발)
pool: {
  min: 2,
  max: 10,
}

// 중간 트래픽 (스테이징)
pool: {
  min: 5,
  max: 20,
}

// 높은 트래픽 (프로덕션)
pool: {
  min: 10,
  max: 50,
}
```

**계산 공식:**

```
max = (서버 CPU 코어 수 × 2) + 디스크 수
```

예: 8 코어 CPU, SSD 1개 → max = (8 × 2) + 1 = 17

### 쿼리 성능 모니터링

```typescript theme={null}
// 느린 쿼리 로깅
database: {
  defaultOptions: {
    connection: {
      // ...
    },
    // 1초 이상 걸리는 쿼리 로깅
    log: {
      warn(message) {
        if (message.includes('slow query')) {
          console.warn('🐌', message);
        }
      },
    },
  },
}
```

## 다음 단계

<CardGroup cols={2}>
  <Card title="API 키 관리" icon="key" href="/ko/configuration/environment-variables/api-keys">
    외부 API 키를 안전하게 설정하세요
  </Card>

  <Card title=".env 설정" icon="file-code" href="/ko/configuration/environment-variables/env-setup">
    환경변수 기본 설정을 학습하세요
  </Card>

  <Card title="마이그레이션" icon="database" href="/ko/database/migrations">
    데이터베이스 스키마를 관리하세요
  </Card>

  <Card title="데이터베이스 설정" icon="gear" href="/ko/configuration/sonamu-config/database">
    데이터베이스 상세 설정을 확인하세요
  </Card>
</CardGroup>
