diff options
Diffstat (limited to 'lib/oracle')
| -rw-r--r-- | lib/oracle/db.ts | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/lib/oracle/db.ts b/lib/oracle/db.ts new file mode 100644 index 00000000..57a5cd49 --- /dev/null +++ b/lib/oracle/db.ts @@ -0,0 +1,148 @@ +import oracledb, { Connection, PoolAttributes, Result } from 'oracledb'; + +// Oracle 자동 커밋 활성화 +oracledb.autoCommit = true; + +// 연결 구성 타입 +interface DbConfig extends PoolAttributes { + user: string; + password: string; + connectString: string; +} + +// 환경 변수에서 DB 설정 가져오기 +const dbConfig: DbConfig = { + user: process.env.ORACLE_USER || '', + password: process.env.ORACLE_PASSWORD || '', + connectString: process.env.ORACLE_CONNECTION_STRING || '' +}; + +// DB 연결 풀 +let pool: oracledb.Pool | null = null; + +/** + * DB 연결 풀 초기화 + */ +async function initialize(): Promise<void> { + try { + pool = await oracledb.createPool(dbConfig); + console.log('Oracle DB 연결 풀이 초기화되었습니다.'); + } catch (err) { + console.error('Oracle DB 풀 초기화 오류:', err); + throw err; + } +} + +/** + * DB 연결 가져오기 + */ +export async function getConnection(): Promise<Connection> { + if (!pool) { + await initialize(); + } + + if (!pool) { + throw new Error('DB 풀이 초기화되지 않았습니다.'); + } + + return pool.getConnection(); +} + +/** + * 쿼리 실행 함수 + */ +export async function executeQuery<T = any>( + query: string, + params: any[] = [], + options: oracledb.ExecuteOptions = {} +): Promise<Result<T>> { + let connection: Connection | undefined; + + try { + connection = await getConnection(); + + const result = await connection.execute<T>( + query, + params, + { + outFormat: oracledb.OUT_FORMAT_OBJECT, + ...options + } + ); + + return result; + } catch (err) { + console.error('쿼리 실행 오류:', err); + throw err; + } finally { + if (connection) { + try { + await connection.close(); + } catch (err) { + console.error('연결 종료 오류:', err); + } + } + } +} + +/** + * 테이블 데이터 가져오기 함수 + */ +export async function getTableData<T = any>(tableName: string): Promise<T[]> { + // SQL 인젝션 방지를 위한 테이블 이름 검증 + // 알파벳, 숫자, 밑줄(_), 달러 기호($), 해시(#) 만 허용 + const tableNameRegex = /^[a-zA-Z0-9_$#]+$/; + + if (!tableNameRegex.test(tableName)) { + throw new Error('유효하지 않은 테이블 이름입니다.'); + } + + const query = `SELECT * FROM ${tableName}`; + const result = await executeQuery<T>(query); + + return result.rows as T[] || []; +} + +/** + * 테이블 특정 컬럼 데이터 가져오기 함수 + */ +export async function getTableColumns<T = any>( + tableName: string, + columns: string[] +): Promise<T[]> { + // SQL 인젝션 방지 + const tableNameRegex = /^[a-zA-Z0-9_$#]+$/; + if (!tableNameRegex.test(tableName)) { + throw new Error('유효하지 않은 테이블 이름입니다.'); + } + + // 컬럼명 검증 + const columnNameRegex = /^[a-zA-Z0-9_$#]+$/; + const validColumns = columns.filter(col => columnNameRegex.test(col)); + + if (validColumns.length === 0) { + throw new Error('유효하지 않은 컬럼 이름입니다.'); + } + + const columnsStr = validColumns.join(', '); + const query = `SELECT ${columnsStr} FROM ${tableName}`; + + const result = await executeQuery<T>(query); + return result.rows as T[] || []; +} + +/** + * 풀 종료 함수 + */ +export async function closePool(): Promise<void> { + if (pool) { + try { + await pool.close(0); + pool = null; + console.log('Oracle DB 연결 풀이 종료되었습니다.'); + } catch (err) { + console.error('풀 종료 오류:', err); + throw err; + } + } +}
\ No newline at end of file |
