import { Pool, PoolClient, QueryResult } from 'pg'; import config from '../config/environment'; /** * PostgreSQL Raw Query 서비스 * Prisma 대신 직접 pg 라이브러리를 사용 */ export class PostgreSQLService { private static pool: Pool; /** * 데이터베이스 연결 풀 초기화 */ static initialize() { if (!this.pool) { this.pool = new Pool({ connectionString: config.databaseUrl, max: 20, // 최대 연결 수 idleTimeoutMillis: 30000, connectionTimeoutMillis: 2000, }); // 연결 풀 이벤트 리스너 this.pool.on('connect', () => { console.log('🔗 PostgreSQL 연결 성공'); }); this.pool.on('error', (err) => { console.error('❌ PostgreSQL 연결 오류:', err); }); } } /** * 연결 풀 가져오기 */ static getPool(): Pool { if (!this.pool) { this.initialize(); } return this.pool; } /** * 단일 쿼리 실행 */ static async query(text: string, params?: any[]): Promise { const pool = this.getPool(); const start = Date.now(); try { const result = await pool.query(text, params); const duration = Date.now() - start; if (config.debug) { console.log('🔍 Query executed:', { text, duration: `${duration}ms`, rows: result.rowCount }); } return result; } catch (error) { console.error('❌ Query error:', { text, params, error }); throw error; } } /** * 트랜잭션 실행 */ static async transaction(callback: (client: PoolClient) => Promise): Promise { const pool = this.getPool(); const client = await pool.connect(); try { await client.query('BEGIN'); const result = await callback(client); await client.query('COMMIT'); return result; } catch (error) { await client.query('ROLLBACK'); throw error; } finally { client.release(); } } /** * 연결 테스트 */ static async testConnection(): Promise { try { const result = await this.query('SELECT NOW() as current_time'); console.log('✅ PostgreSQL 연결 테스트 성공:', result.rows[0]); return true; } catch (error) { console.error('❌ PostgreSQL 연결 테스트 실패:', error); return false; } } /** * 연결 풀 종료 */ static async close(): Promise { if (this.pool) { await this.pool.end(); console.log('🔒 PostgreSQL 연결 풀 종료'); } } } // 애플리케이션 시작 시 초기화 PostgreSQLService.initialize(); // 프로세스 종료 시 연결 정리 process.on('SIGINT', async () => { await PostgreSQLService.close(); process.exit(0); }); process.on('SIGTERM', async () => { await PostgreSQLService.close(); process.exit(0); }); process.on('beforeExit', async () => { await PostgreSQLService.close(); });