메인 콘텐츠로 건너뛰기
Sonamu는 PostgreSQL 데이터베이스를 사용합니다. 데이터베이스 연결 정보는 환경변수로 관리하여 코드에서 분리하고, 환경별로 다른 DB를 사용할 수 있습니다.
Sonamu v2부터 PostgreSQL을 공식 지원하며, MySQL에서 마이그레이션을 지원합니다.

필수 환경변수

데이터베이스 연결에 필요한 환경변수입니다.
.env
# 데이터베이스 호스트
DB_HOST=localhost

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

# 사용자 이름
DB_USER=postgres

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

# 데이터베이스 이름
DATABASE_NAME=myproject_dev
DB_PASSWORD는 절대 코드에 하드코딩하지 마세요! 환경변수로만 관리해야 합니다.

sonamu.config.ts 설정

기본 설정

sonamu.config.ts
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,
      },
    },
  },
  
  // ...
});
password는 기본값을 설정하지 마세요. 환경변수가 없으면 undefined로 두어 명시적으로 에러가 발생하도록 합니다.

연결 풀 설정

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,
    },
  },
}
연결 풀 옵션:
옵션기본값설명
min2최소 연결 수
max10최대 연결 수
idleTimeoutMillis10000유휴 연결 제거 시간 (ms)
connectionTimeoutMillis0연결 대기 시간 (ms)
트래픽이 많은 프로덕션 환경에서는 max를 20-50으로 증가시키세요.

환경별 설정

.env.development
# 로컬 PostgreSQL
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=your-dev-password
DATABASE_NAME=myproject_dev
Docker Compose로 PostgreSQL 실행:
docker-compose.yml
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:
# 시작
docker-compose up -d

# 중지
docker-compose down

PostgreSQL 사용자 생성

PostgreSQL 접속

# 로컬
psql -U postgres

# 원격
psql -h db.example.com -U postgres

데이터베이스 생성

CREATE DATABASE myproject_dev;

사용자 생성 및 권한 부여

-- 사용자 생성
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;

연결 테스트

psql -h localhost -U myproject_user -d myproject_dev
비밀번호 입력 후 접속되면 성공!

보안 모범 사례

비밀번호 요구사항

개발 환경:
  • 최소 8자 이상
  • 영문, 숫자 혼합
스테이징/프로덕션:
  • 최소 32자 이상
  • 영문 대소문자, 숫자, 특수문자 혼합
  • 사전에 없는 무작위 문자열

비밀번호 생성

# 32자 랜덤 비밀번호
openssl rand -base64 24

# 또는
pwgen -s 32 1
예시:
DB_PASSWORD=your-32-character-secure-password

권한 분리

-- ✅ 애플리케이션 사용자 (읽기/쓰기만)
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;
프로덕션 애플리케이션에 SUPERUSER 권한을 주지 마세요! 보안 위험이 매우 큽니다.

SSL 연결 설정

필수 환경:
  • 프로덕션 (필수)
  • 스테이징 (권장)
  • 개발 (선택)
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

# AWS RDS CA 인증서 다운로드
wget https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem

# .env에 경로 추가
DB_SSL_CA=/path/to/global-bundle.pem
ssl: {
  rejectUnauthorized: true,
  ca: fs.readFileSync(process.env.DB_SSL_CA!).toString(),
}

Vault 사용 (권장)

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

AWS Secrets Manager

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,
      },
    },
  },
});

연결 테스트

# psql로 직접 연결 테스트
psql "postgresql://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DATABASE_NAME"

# 연결 성공 시
# myproject_dev=>

문제 해결

증상:
Error: connect ECONNREFUSED 127.0.0.1:5432
원인:
  1. PostgreSQL이 실행 중이 아님
  2. 잘못된 호스트/포트
  3. 방화벽 차단
해결:
# 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_
증상:
error: password authentication failed for user "myproject_user"
원인:
  1. 잘못된 사용자 이름/비밀번호
  2. 사용자가 존재하지 않음
  3. pg_hba.conf 설정 문제
해결:
# 1. 사용자 확인
psql -U postgres -c "\du"

# 2. 비밀번호 재설정
psql -U postgres
ALTER USER myproject_user WITH PASSWORD 'your-new-password';
# 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
증상:
Error: Connection timeout
원인:
  1. 네트워크 문제
  2. DB 서버 과부하
  3. 연결 풀 고갈
해결:
// 1. 타임아웃 증가
connection: {
  connectionTimeoutMillis: 10000,  // 10초
}

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

// 3. 네트워크 확인
// ping DB 서버
// telnet db.example.com 5432
증상:
FATAL: sorry, too many clients already
원인:
  • PostgreSQL의 max_connections 한계 초과
해결:
-- 현재 연결 수 확인
SELECT count(*) FROM pg_stat_activity;

-- max_connections 확인
SHOW max_connections;

-- max_connections 증가 (postgresql.conf)
-- max_connections = 200
// 애플리케이션 연결 풀 제한
pool: {
  max: 10,  // max_connections보다 충분히 작게
}
max_connections를 무작정 늘리면 메모리 사용량이 증가합니다. 연결 풀 크기를 먼저 조정하세요.

성능 최적화

연결 풀 튜닝

// 트래픽 패턴에 따른 설정

// 낮은 트래픽 (개발)
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

쿼리 성능 모니터링

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

다음 단계