# Phase 1: Raw Query 기반 구조 사용 가이드

## 📋 개요

Phase 1에서 구현한 Raw Query 기반 데이터베이스 아키텍처 사용 방법입니다.

---

## 🏗️ 구현된 모듈

### 1. **DatabaseManager** (`src/database/db.ts`)

PostgreSQL 연결 풀 기반 핵심 모듈

**주요 함수:**
- `query<T>(sql, params)` - 기본 쿼리 실행
- `queryOne<T>(sql, params)` - 단일 행 조회
- `transaction(callback)` - 트랜잭션 실행
- `getPool()` - 연결 풀 가져오기
- `getPoolStatus()` - 연결 풀 상태 확인

### 2. **QueryBuilder** (`src/utils/queryBuilder.ts`)

동적 쿼리 생성 유틸리티

**주요 메서드:**
- `QueryBuilder.select(tableName, options)` - SELECT 쿼리
- `QueryBuilder.insert(tableName, data, options)` - INSERT 쿼리
- `QueryBuilder.update(tableName, data, where, options)` - UPDATE 쿼리
- `QueryBuilder.delete(tableName, where, options)` - DELETE 쿼리
- `QueryBuilder.count(tableName, where)` - COUNT 쿼리
- `QueryBuilder.exists(tableName, where)` - EXISTS 쿼리

### 3. **DatabaseValidator** (`src/utils/databaseValidator.ts`)

SQL Injection 방지 및 입력 검증

**주요 메서드:**
- `validateTableName(tableName)` - 테이블명 검증
- `validateColumnName(columnName)` - 컬럼명 검증
- `validateWhereClause(where)` - WHERE 조건 검증
- `sanitizeInput(input)` - 입력 값 Sanitize

### 4. **타입 정의** (`src/types/database.ts`)

TypeScript 타입 안전성 보장

---

## 🚀 사용 예제

### 1. 기본 쿼리 실행

```typescript
import { query, queryOne } from '../database/db';

// 여러 행 조회
const users = await query<User>(
  'SELECT * FROM users WHERE status = $1',
  ['active']
);

// 단일 행 조회
const user = await queryOne<User>(
  'SELECT * FROM users WHERE user_id = $1',
  ['user123']
);

if (!user) {
  throw new Error('사용자를 찾을 수 없습니다.');
}
```

### 2. QueryBuilder 사용

#### SELECT

```typescript
import { query } from '../database/db';
import { QueryBuilder } from '../utils/queryBuilder';

// 기본 SELECT
const { query: sql, params } = QueryBuilder.select('users', {
  where: { status: 'active' },
  orderBy: 'created_at DESC',
  limit: 10,
});

const users = await query(sql, params);

// 복잡한 SELECT (JOIN, WHERE, ORDER BY)
const { query: sql2, params: params2 } = QueryBuilder.select('users', {
  columns: ['users.user_id', 'users.username', 'departments.dept_name'],
  joins: [
    {
      type: 'LEFT',
      table: 'departments',
      on: 'users.dept_id = departments.dept_id',
    },
  ],
  where: { 'users.status': 'active' },
  orderBy: ['users.created_at DESC', 'users.username ASC'],
  limit: 20,
  offset: 0,
});

const result = await query(sql2, params2);
```

#### INSERT

```typescript
import { query } from '../database/db';
import { QueryBuilder } from '../utils/queryBuilder';

// 기본 INSERT
const { query: sql, params } = QueryBuilder.insert(
  'users',
  {
    user_id: 'new_user',
    username: 'John Doe',
    email: 'john@example.com',
    status: 'active',
  },
  {
    returning: ['id', 'user_id'],
  }
);

const [newUser] = await query(sql, params);
console.log('생성된 사용자 ID:', newUser.id);

// UPSERT (INSERT ... ON CONFLICT)
const { query: sql2, params: params2 } = QueryBuilder.insert(
  'users',
  {
    user_id: 'user123',
    username: 'Jane',
    email: 'jane@example.com',
  },
  {
    onConflict: {
      columns: ['user_id'],
      action: 'DO UPDATE',
      updateSet: ['username', 'email'],
    },
    returning: ['*'],
  }
);

const [upsertedUser] = await query(sql2, params2);
```

#### UPDATE

```typescript
import { query } from '../database/db';
import { QueryBuilder } from '../utils/queryBuilder';

const { query: sql, params } = QueryBuilder.update(
  'users',
  {
    username: 'Updated Name',
    email: 'updated@example.com',
    updated_at: new Date(),
  },
  {
    user_id: 'user123',
  },
  {
    returning: ['*'],
  }
);

const [updatedUser] = await query(sql, params);
```

#### DELETE

```typescript
import { query } from '../database/db';
import { QueryBuilder } from '../utils/queryBuilder';

const { query: sql, params } = QueryBuilder.delete(
  'users',
  {
    user_id: 'user_to_delete',
  },
  {
    returning: ['user_id', 'username'],
  }
);

const [deletedUser] = await query(sql, params);
console.log('삭제된 사용자:', deletedUser.username);
```

### 3. 트랜잭션 사용

```typescript
import { transaction } from '../database/db';

// 복잡한 트랜잭션 처리
const result = await transaction(async (client) => {
  // 1. 사용자 생성
  const userResult = await client.query(
    'INSERT INTO users (user_id, username, email) VALUES ($1, $2, $3) RETURNING id',
    ['new_user', 'John', 'john@example.com']
  );

  const userId = userResult.rows[0].id;

  // 2. 역할 할당
  await client.query(
    'INSERT INTO user_roles (user_id, role_id) VALUES ($1, $2)',
    [userId, 'admin']
  );

  // 3. 로그 생성
  await client.query(
    'INSERT INTO audit_logs (action, user_id, details) VALUES ($1, $2, $3)',
    ['USER_CREATED', userId, JSON.stringify({ username: 'John' })]
  );

  return { success: true, userId };
});

console.log('트랜잭션 완료:', result);
```

### 4. JSON 필드 쿼리 (JSONB)

```typescript
import { query } from '../database/db';
import { QueryBuilder } from '../utils/queryBuilder';

// JSON 필드 쿼리 (config->>'type' = 'form')
const { query: sql, params } = QueryBuilder.select('screen_management', {
  columns: ['*'],
  where: {
    company_code: 'COMPANY_001',
    "config->>'type'": 'form',
  },
});

const screens = await query(sql, params);
```

### 5. 동적 테이블 쿼리

```typescript
import { query } from '../database/db';
import { DatabaseValidator } from '../utils/databaseValidator';

async function queryDynamicTable(tableName: string, filters: Record<string, any>) {
  // 테이블명 검증 (SQL Injection 방지)
  if (!DatabaseValidator.validateTableName(tableName)) {
    throw new Error('유효하지 않은 테이블명입니다.');
  }

  // WHERE 조건 검증
  if (!DatabaseValidator.validateWhereClause(filters)) {
    throw new Error('유효하지 않은 WHERE 조건입니다.');
  }

  const { query: sql, params } = QueryBuilder.select(tableName, {
    where: filters,
  });

  return await query(sql, params);
}

// 사용 예
const data = await queryDynamicTable('company_data_001', {
  status: 'active',
  region: 'Seoul',
});
```

---

## 🔐 보안 고려사항

### 1. **항상 Parameterized Query 사용**

```typescript
// ❌ 위험: SQL Injection 취약
const userId = req.params.userId;
const sql = `SELECT * FROM users WHERE user_id = '${userId}'`;
const users = await query(sql);

// ✅ 안전: Parameterized Query
const userId = req.params.userId;
const users = await query('SELECT * FROM users WHERE user_id = $1', [userId]);
```

### 2. **식별자 검증**

```typescript
import { DatabaseValidator } from '../utils/databaseValidator';

// 테이블명/컬럼명 검증
if (!DatabaseValidator.validateTableName(tableName)) {
  throw new Error('유효하지 않은 테이블명입니다.');
}

if (!DatabaseValidator.validateColumnName(columnName)) {
  throw new Error('유효하지 않은 컬럼명입니다.');
}
```

### 3. **입력 값 Sanitize**

```typescript
import { DatabaseValidator } from '../utils/databaseValidator';

const sanitizedData = DatabaseValidator.sanitizeInput(userInput);
```

---

## 📊 성능 최적화 팁

### 1. **연결 풀 모니터링**

```typescript
import { getPoolStatus } from '../database/db';

const status = getPoolStatus();
console.log('연결 풀 상태:', {
  total: status.totalCount,
  idle: status.idleCount,
  waiting: status.waitingCount,
});
```

### 2. **배치 INSERT**

```typescript
import { transaction } from '../database/db';

// 대량 데이터 삽입 시 트랜잭션 사용
await transaction(async (client) => {
  for (const item of largeDataset) {
    await client.query('INSERT INTO items (name, value) VALUES ($1, $2)', [
      item.name,
      item.value,
    ]);
  }
});
```

### 3. **인덱스 활용 쿼리**

```typescript
// WHERE 절에 인덱스 컬럼 사용
const { query: sql, params } = QueryBuilder.select('users', {
  where: {
    user_id: 'user123', // 인덱스 컬럼
  },
});
```

---

## 🧪 테스트 실행

```bash
# 테스트 실행
npm test -- database.test.ts

# 특정 테스트만 실행
npm test -- database.test.ts -t "QueryBuilder"
```

---

## 🚨 에러 핸들링

```typescript
import { query } from '../database/db';

try {
  const users = await query('SELECT * FROM users WHERE status = $1', ['active']);
  return users;
} catch (error: any) {
  console.error('쿼리 실행 실패:', error.message);

  // PostgreSQL 에러 코드 확인
  if (error.code === '23505') {
    throw new Error('중복된 값이 존재합니다.');
  }

  if (error.code === '23503') {
    throw new Error('외래 키 제약 조건 위반입니다.');
  }

  throw error;
}
```

---

## 📝 다음 단계 (Phase 2)

Phase 1 기반 구조가 완성되었으므로, Phase 2에서는:

1. **screenManagementService.ts** 전환 (46개 호출)
2. **tableManagementService.ts** 전환 (35개 호출)
3. **dataflowService.ts** 전환 (31개 호출)

등 핵심 서비스를 Raw Query로 전환합니다.

---

**작성일**: 2025-09-30
**버전**: 1.0.0
**담당**: Backend Development Team