<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ko">
	<id>https://devcafe.co.kr/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kokoksh88</id>
	<title>데브카페 - 사용자 기여 [ko]</title>
	<link rel="self" type="application/atom+xml" href="https://devcafe.co.kr/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Kokoksh88"/>
	<link rel="alternate" type="text/html" href="https://devcafe.co.kr/w/%ED%8A%B9%EC%88%98:%EA%B8%B0%EC%97%AC/Kokoksh88"/>
	<updated>2026-05-18T02:37:23Z</updated>
	<subtitle>사용자 기여</subtitle>
	<generator>MediaWiki 1.42.1</generator>
	<entry>
		<id>https://devcafe.co.kr/w/index.php?title=DBA_Tune&amp;diff=2336</id>
		<title>DBA Tune</title>
		<link rel="alternate" type="text/html" href="https://devcafe.co.kr/w/index.php?title=DBA_Tune&amp;diff=2336"/>
		<updated>2025-11-12T14:44:34Z</updated>

		<summary type="html">&lt;p&gt;Kokoksh88: /* DBA Tunes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== DBA Tunes ==&lt;br /&gt;
&lt;br /&gt;
## 프로그램 수정 가이드 - 핵심 요약&lt;br /&gt;
&lt;br /&gt;
===프로그램 구조===&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
app.py                    # Flask 백엔드 서버 (API, 데이터베이스)&lt;br /&gt;
templates/index_ag.html   # 프론트엔드 메인 페이지&lt;br /&gt;
instance/sql_tuning.db    # SQLite 데이터베이스&lt;br /&gt;
uploads/                  # 첨부파일 저장 폴더&lt;br /&gt;
&lt;br /&gt;
DBATunePjt/&lt;br /&gt;
├── app.py                          # Flask 백엔드 애플리케이션 메인 파일&lt;br /&gt;
├── requirements.txt                # Python 의존성 패키지 목록&lt;br /&gt;
├── init_db.py                      # 데이터베이스 초기화 스크립트&lt;br /&gt;
├── reset_db.py                     # 데이터베이스 초기화/리셋 스크립트&lt;br /&gt;
│&lt;br /&gt;
├── templates/                      # HTML 템플릿 디렉토리 (백엔드에서 렌더링)&lt;br /&gt;
│   ├── index_ag.html              # ⭐ 메인 UI (AG Grid 사용)&lt;br /&gt;
│   └── index_simple.html          # 간단한 대체 UI&lt;br /&gt;
│&lt;br /&gt;
├── static/                         # 정적 리소스 디렉토리&lt;br /&gt;
│   └── luckysheet/                # Luckysheet 라이브러리 (현재 미사용)&lt;br /&gt;
│&lt;br /&gt;
├── instance/                       # 인스턴스 데이터 디렉토리&lt;br /&gt;
│   └── sql_tuning.db             # SQLite 데이터베이스 파일&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
## 🔧 핵심 파일 상세 설명&lt;br /&gt;
&lt;br /&gt;
### 1. **app.py** ⭐ (백엔드 핵심)&lt;br /&gt;
&lt;br /&gt;
**역할**: Flask 웹 애플리케이션 서버 및 REST API 제공&lt;br /&gt;
&lt;br /&gt;
#### 주요 구성 요소:&lt;br /&gt;
&lt;br /&gt;
##### 📦 데이터베이스 모델 (ORM)&lt;br /&gt;
&lt;br /&gt;
**TuningRequest 클래스** (`TN_REQ 테이블`)&lt;br /&gt;
- 튜닝 요청 정보를 저장하는 메인 테이블&lt;br /&gt;
- 필드 분류:&lt;br /&gt;
  - **기본 정보**: `REQ_NO` (PK), `REQ_USER`, `REQ_DT`, `DB_NAME`&lt;br /&gt;
  - **프로그램 정보**: `SQL_PROGRAM_ID`, `BUSINESS_TYPE`, `COLLECTION_TYPE`, `APP_SCREEN`&lt;br /&gt;
  - **실행 정보**: `ONLINE_BATCH`, `EXECUTION_FREQUENCY`, `DYNAMIC_SQL`&lt;br /&gt;
  - **SQL 정보**: `REQ_SQL_TEXT` (요청 SQL), `TUNED_SQL_TEXT` (완료 SQL)&lt;br /&gt;
  - **실행 계획**: `REQ_EXECUTION_PLAN`, `TUNED_EXECUTION_PLAN`&lt;br /&gt;
  - **성능 지표**: `TIME_BEFORE`, `TIME_AFTER` (응답속도), `BLOCKS_BEFORE`, `BLOCKS_AFTER`&lt;br /&gt;
  - **튜닝 상태**: `IMPROVEMENT_STATUS`, `HINT_APPLIED`, `INDEX_CREATED`, `SQL_CHANGED`&lt;br /&gt;
  - **개선율**: `IMPROVEMENT_RATE`, `COMPLETION_DATE`&lt;br /&gt;
&lt;br /&gt;
**TuningAction 클래스** (`TN_ACTN 테이블`)&lt;br /&gt;
- 각 튜닝 요청에 대한 액션 내역 기록&lt;br /&gt;
&lt;br /&gt;
##### 🔌 REST API 엔드포인트&lt;br /&gt;
&lt;br /&gt;
| 메서드 | 엔드포인트 | 기능 |&lt;br /&gt;
|--------|----------|------|&lt;br /&gt;
| `GET` | `/` | 메인 페이지 (index.html) 렌더링 |&lt;br /&gt;
| `GET` | `/ag` | AG Grid 메인 페이지 (index_ag.html) 렌더링 |&lt;br /&gt;
| `GET` | `/api/requests` | 모든 튜닝 요청 조회 |&lt;br /&gt;
| `POST` | `/api/requests` | 새 튜닝 요청 생성 |&lt;br /&gt;
| `PUT` | `/api/requests/&amp;lt;id&amp;gt;` | 특정 튜닝 요청 업데이트 |&lt;br /&gt;
| `DELETE` | `/api/requests/&amp;lt;id&amp;gt;` | 특정 튜닝 요청 삭제 |&lt;br /&gt;
| `GET` | `/api/actions` | 모든 액션 조회 |&lt;br /&gt;
| `POST` | `/api/actions` | 새 액션 생성 |&lt;br /&gt;
| `POST` | `/api/init-sample` | 샘플 데이터 초기화 |&lt;br /&gt;
&lt;br /&gt;
##### 🚀 애플리케이션 시작 로직&lt;br /&gt;
&lt;br /&gt;
```python&lt;br /&gt;
if __name__ == &#039;__main__&#039;:&lt;br /&gt;
    with app.app_context():&lt;br /&gt;
        db.create_all()  # 데이터베이스 테이블 생성&lt;br /&gt;
        # 데이터가 없으면 샘플 데이터 자동 추가&lt;br /&gt;
        if TuningRequest.query.count() == 0:&lt;br /&gt;
            # 샘플 데이터 삽입&lt;br /&gt;
    app.run(debug=True, host=&#039;0.0.0.0&#039;, port=5000)&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
**특징**:&lt;br /&gt;
- 자동 데이터베이스 초기화&lt;br /&gt;
- 서버 시작 시 샘플 데이터 자동 생성&lt;br /&gt;
- UTF-8 인코딩 지원 (한글 처리)&lt;br /&gt;
- CORS 활성화 (크로스 도메인 요청 허용)&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
### 2. **templates/index_ag.html** ⭐ (프론트엔드 핵심)&lt;br /&gt;
&lt;br /&gt;
**역할**: 메인 UI 인터페이스 (AG Grid 데이터 그리드 기반)&lt;br /&gt;
&lt;br /&gt;
#### 주요 기능:&lt;br /&gt;
&lt;br /&gt;
##### 🎨 UI 컴포넌트&lt;br /&gt;
&lt;br /&gt;
1. **헤더 (Header)**&lt;br /&gt;
   - 애플리케이션 제목: `[DBAWorks] SQL 튜닝 관리`&lt;br /&gt;
   - 네비게이션 메뉴: SQL 튜닝, 성능 관리, 디스크 관리, 오브젝트 관리, 권한 관리, 시스템 관리&lt;br /&gt;
&lt;br /&gt;
2. **도구모음 (Toolbar)**&lt;br /&gt;
   - 새 요청 추가: 새로운 튜닝 요청 추가&lt;br /&gt;
   - 저장: 그리드 변경사항 저장&lt;br /&gt;
   - 새로고침: 데이터 다시 로드&lt;br /&gt;
   - CSV 내보내기: 데이터 내보내기&lt;br /&gt;
   - 샘플 데이터 초기화: 샘플 데이터 다시 로드&lt;br /&gt;
   - **동적 컬럼 표시 버튼**:&lt;br /&gt;
     - 요청정보 확인: 요청 관련 컬럼만 표시&lt;br /&gt;
     - 튜닝결과 확인: 결과 관련 컬럼만 표시&lt;br /&gt;
     - 전체보기: 모든 컬럼 표시&lt;br /&gt;
   - 검색박스: 데이터 검색 기능&lt;br /&gt;
&lt;br /&gt;
3. **데이터 그리드 (AG Grid)**&lt;br /&gt;
   - 다중 선택 가능&lt;br /&gt;
   - 셀 편집 가능&lt;br /&gt;
   - 정렬/필터링 지원&lt;br /&gt;
   - 동적 컬럼 표시&lt;br /&gt;
&lt;br /&gt;
4. **상세 정보 패널 (Detail Panel)**&lt;br /&gt;
   - 📋 요청 정보: 요청자, DB명, 프로그램ID, 비즈니스 유형 등&lt;br /&gt;
   - 💾 SQL 정보 (편집 가능):&lt;br /&gt;
     - 튜닝 요청 SQL&lt;br /&gt;
     - 튜닝 요청 SQL 실행계획&lt;br /&gt;
     - 튜닝 완료 SQL&lt;br /&gt;
     - 튜닝 완료 SQL 실행계획&lt;br /&gt;
     - 성능 개선 지표 (응답속도, Block 수)&lt;br /&gt;
   - 🔧 튜닝 정보 (편집 가능):&lt;br /&gt;
     - 개선여부&lt;br /&gt;
     - 힌트적용&lt;br /&gt;
     - INDEX생성&lt;br /&gt;
     - SQL변경&lt;br /&gt;
&lt;br /&gt;
5. **모달 윈도우 (Modal)**&lt;br /&gt;
   - **새 요청 추가 모달**: 새로운 튜닝 요청 입력&lt;br /&gt;
     - 요청자, DB명, 프로그램ID 등 입력&lt;br /&gt;
     - SQL 입력 영역 (화면 50% 이상)&lt;br /&gt;
     - 닫기 버튼으로만 닫기 가능 (외부 클릭 시 닫히지 않음)&lt;br /&gt;
&lt;br /&gt;
##### 📊 컬럼 정의 (columnDefs)&lt;br /&gt;
&lt;br /&gt;
**기본 컬럼**:&lt;br /&gt;
- 선택 체크박스&lt;br /&gt;
- 상세보기 버튼&lt;br /&gt;
- 요청번호 (REQ_NO)&lt;br /&gt;
- 요청자 (REQ_USER)&lt;br /&gt;
- 요청일시 (REQ_DT)&lt;br /&gt;
- 개선여부 (IMPROVEMENT_STATUS)&lt;br /&gt;
&lt;br /&gt;
**요청정보 컬럼**:&lt;br /&gt;
- DB명, 프로그램ID, 비즈니스 유형, 수집 유형, 애플리케이션 화면, 온라인/배치, 실행 빈도, 동적SQL&lt;br /&gt;
&lt;br /&gt;
**튜닝결과 컬럼**:&lt;br /&gt;
- 개선여부, 힌트적용, INDEX생성, SQL변경, Block 전/후, 응답속도 전/후, 개선율, 완료일&lt;br /&gt;
&lt;br /&gt;
##### ⚙️ 주요 JavaScript 함수&lt;br /&gt;
&lt;br /&gt;
| 함수 | 설명 |&lt;br /&gt;
|------|------|&lt;br /&gt;
| `loadData()` | `/api/requests`에서 데이터 로드 (에러 처리 포함) |&lt;br /&gt;
| `addRow()` | 새 요청 추가 모달 열기 |&lt;br /&gt;
| `saveNewRequest()` | 새 요청 저장 (POST 요청) |&lt;br /&gt;
| `saveChanges()` | 그리드 변경사항 저장 (PUT 요청) |&lt;br /&gt;
| `saveDetailChanges()` | 상세 패널 변경사항 저장 |&lt;br /&gt;
| `showDetailPanel(rowData)` | 상세 정보 패널 표시 |&lt;br /&gt;
| `closeDetailPanel()` | 상세 정보 패널 닫기 |&lt;br /&gt;
| `showRequestColumns()` | 요청정보 컬럼만 표시 |&lt;br /&gt;
| `showResultColumns()` | 튜닝결과 컬럼만 표시 |&lt;br /&gt;
| `showAllColumns()` | 모든 컬럼 표시 |&lt;br /&gt;
| `reloadData()` | 데이터 새로고침 |&lt;br /&gt;
| `exportCsv()` | CSV로 내보내기 |&lt;br /&gt;
| `initSample()` | 샘플 데이터 초기화 |&lt;br /&gt;
&lt;br /&gt;
##### 🎨 CSS 스타일&lt;br /&gt;
&lt;br /&gt;
**색상 스킴**:&lt;br /&gt;
- 헤더 배경: `#243447` (어두운 파란색)&lt;br /&gt;
- 강조 색상: `#2563eb` (파란색)&lt;br /&gt;
- 성공 색상: `#10b981` (초록색)&lt;br /&gt;
- 경고 색상: `#ec4899` (분홍색)&lt;br /&gt;
&lt;br /&gt;
**주요 클래스**:&lt;br /&gt;
- `.header`: 상단 헤더 스타일&lt;br /&gt;
- `.toolbar`: 도구 버튼 영역&lt;br /&gt;
- `.detail-panel`: 상세 정보 패널&lt;br /&gt;
- `.modal`: 모달 윈도우&lt;br /&gt;
- `.form-group`: 폼 입력 그룹&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
### 3. **run.bat** (Windows 실행 파일)&lt;br /&gt;
&lt;br /&gt;
**역할**: Windows에서 프로그램을 쉽게 실행&lt;br /&gt;
&lt;br /&gt;
#### 기능:&lt;br /&gt;
1. UTF-8 인코딩 설정 (한글 표시)&lt;br /&gt;
2. Python 설치 여부 확인&lt;br /&gt;
3. 가상환경 자동 생성/활성화&lt;br /&gt;
4. 필요한 패키지 자동 설치 (`requirements.txt`)&lt;br /&gt;
5. 애플리케이션 자동 시작&lt;br /&gt;
6. 사용자 친화적 메시지 출력 (4단계 진행률 표시)&lt;br /&gt;
&lt;br /&gt;
#### 사용 방법:&lt;br /&gt;
```bash&lt;br /&gt;
더블클릭 또는 PowerShell에서:&lt;br /&gt;
run.bat&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
### 4. **run.sh** (macOS/Linux 실행 파일)&lt;br /&gt;
&lt;br /&gt;
**역할**: macOS 및 Linux에서 프로그램을 쉽게 실행&lt;br /&gt;
&lt;br /&gt;
#### 기능:&lt;br /&gt;
- Windows의 `run.bat`와 동일한 기능을 쉘 스크립트로 구현&lt;br /&gt;
- `python3` 및 `python` 자동 감지&lt;br /&gt;
- 가상환경 자동 설정&lt;br /&gt;
- ANSI 색상 코드로 다양한 메시지 표시&lt;br /&gt;
&lt;br /&gt;
#### 사용 방법:&lt;br /&gt;
```bash&lt;br /&gt;
chmod +x run.sh  # 실행 권한 부여 (첫 번째만)&lt;br /&gt;
./run.sh         # 실행&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
### 5. **requirements.txt** (의존성 파일)&lt;br /&gt;
&lt;br /&gt;
**역할**: Python 패키지 버전 명시&lt;br /&gt;
&lt;br /&gt;
#### 주요 패키지:&lt;br /&gt;
```&lt;br /&gt;
Flask==2.3.3              # 웹 프레임워크&lt;br /&gt;
Flask-CORS==4.0.0         # CORS 지원&lt;br /&gt;
Flask-SQLAlchemy==3.0.5   # ORM 라이브러리&lt;br /&gt;
SQLAlchemy==2.0.21        # 데이터베이스 관리&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
### 6. **init_db.py** (데이터베이스 초기화)&lt;br /&gt;
&lt;br /&gt;
**역할**: 데이터베이스 수동 초기화&lt;br /&gt;
&lt;br /&gt;
#### 기능:&lt;br /&gt;
- 데이터베이스 테이블 생성&lt;br /&gt;
- 초기 샘플 데이터 삽입&lt;br /&gt;
&lt;br /&gt;
#### 사용 방법:&lt;br /&gt;
```bash&lt;br /&gt;
python init_db.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
### 7. **reset_db.py** (데이터베이스 리셋)&lt;br /&gt;
&lt;br /&gt;
**역할**: 데이터베이스 초기화 및 재설정&lt;br /&gt;
&lt;br /&gt;
#### 기능:&lt;br /&gt;
- 기존 데이터베이스 삭제&lt;br /&gt;
- 새 데이터베이스 생성&lt;br /&gt;
- 샘플 데이터 다시 삽입&lt;br /&gt;
&lt;br /&gt;
#### 사용 방법:&lt;br /&gt;
```bash&lt;br /&gt;
python reset_db.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 🗄️ 데이터베이스 구조&lt;br /&gt;
&lt;br /&gt;
### TN_REQ 테이블 (튜닝 요청)&lt;br /&gt;
&lt;br /&gt;
| 컬럼명 | 데이터타입 | 설명 | 예시 |&lt;br /&gt;
|--------|-----------|------|------|&lt;br /&gt;
| REQ_NO | INTEGER (PK) | 요청 번호 | 1 |&lt;br /&gt;
| REQ_USER | VARCHAR(100) | 요청자 | Kim |&lt;br /&gt;
| REQ_DT | DATETIME | 요청 일시 | 2024-11-12 10:30:00 |&lt;br /&gt;
| DB_NAME | VARCHAR(50) | 데이터베이스명 | Oracle |&lt;br /&gt;
| SQL_PROGRAM_ID | VARCHAR(200) | 프로그램ID | PGM001 |&lt;br /&gt;
| BUSINESS_TYPE | VARCHAR(50) | 비즈니스 유형 | Sales |&lt;br /&gt;
| COLLECTION_TYPE | VARCHAR(100) | 수집 유형 | Real-time |&lt;br /&gt;
| APP_SCREEN | VARCHAR(100) | 애플리케이션 화면 | Main |&lt;br /&gt;
| ONLINE_BATCH | VARCHAR(20) | 온라인/배치 | Online |&lt;br /&gt;
| EXECUTION_FREQUENCY | VARCHAR(50) | 실행 빈도 | Daily |&lt;br /&gt;
| DYNAMIC_SQL | VARCHAR(1) | 동적SQL 여부 | Y/N |&lt;br /&gt;
| REQ_SQL_TEXT | TEXT | 요청 SQL | SELECT * FROM ... |&lt;br /&gt;
| REQ_EXECUTION_PLAN | TEXT | 요청 SQL 실행계획 | EXPLAIN 결과 |&lt;br /&gt;
| TUNED_SQL_TEXT | TEXT | 튜닝 완료 SQL | SELECT ... WHERE ... |&lt;br /&gt;
| TUNED_EXECUTION_PLAN | TEXT | 완료 SQL 실행계획 | EXPLAIN 결과 |&lt;br /&gt;
| TIME_BEFORE | FLOAT | 응답속도 (전) | 1.5 초 |&lt;br /&gt;
| TIME_AFTER | FLOAT | 응답속도 (후) | 0.3 초 |&lt;br /&gt;
| BLOCKS_BEFORE | INTEGER | Block 수 (전) | 100 |&lt;br /&gt;
| BLOCKS_AFTER | INTEGER | Block 수 (후) | 50 |&lt;br /&gt;
| IMPROVEMENT_RATE | VARCHAR(50) | 개선율 | 5배 |&lt;br /&gt;
| IMPROVEMENT_STATUS | VARCHAR(1) | 개선 완료 여부 | Y/N |&lt;br /&gt;
| HINT_APPLIED | VARCHAR(1) | 힌트 적용 | Y/N |&lt;br /&gt;
| INDEX_CREATED | VARCHAR(1) | 인덱스 생성 | Y/N |&lt;br /&gt;
| SQL_CHANGED | VARCHAR(1) | SQL 변경 | Y/N |&lt;br /&gt;
| COMPLETION_DATE | DATE | 완료 날짜 | 2024-11-12 |&lt;br /&gt;
&lt;br /&gt;
### TN_ACTN 테이블 (액션 내역)&lt;br /&gt;
&lt;br /&gt;
| 컬럼명 | 데이터타입 | 설명 |&lt;br /&gt;
|--------|-----------|------|&lt;br /&gt;
| REQ_NO | INTEGER (FK) | 요청 번호 (참조) |&lt;br /&gt;
| ACTION_ID | INTEGER (PK) | 액션 ID |&lt;br /&gt;
| ACTION_TYPE | VARCHAR(50) | 액션 유형 |&lt;br /&gt;
| ACTION_DESC | TEXT | 액션 설명 |&lt;br /&gt;
| ACTION_DT | DATETIME | 액션 일시 |&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 🔄 데이터 흐름 (Data Flow)&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
┌─────────────┐&lt;br /&gt;
│   브라우저   │ (http://localhost:5000)&lt;br /&gt;
└──────┬──────┘&lt;br /&gt;
       │ JavaScript Fetch API&lt;br /&gt;
       ▼&lt;br /&gt;
┌─────────────────────────────────────┐&lt;br /&gt;
│   Flask 백엔드 (app.py)              │&lt;br /&gt;
│  ┌─────────────────────────────────┤&lt;br /&gt;
│  │ REST API Endpoints:              │&lt;br /&gt;
│  │ • GET  /api/requests             │&lt;br /&gt;
│  │ • POST /api/requests             │&lt;br /&gt;
│  │ • PUT  /api/requests/&amp;lt;id&amp;gt;        │&lt;br /&gt;
│  │ • DELETE /api/requests/&amp;lt;id&amp;gt;      │&lt;br /&gt;
│  └─────────────────────────────────┤&lt;br /&gt;
└──────┬──────────────────────────────┘&lt;br /&gt;
       │ SQL Queries&lt;br /&gt;
       ▼&lt;br /&gt;
┌─────────────────────────────┐&lt;br /&gt;
│   SQLite 데이터베이스       │&lt;br /&gt;
│  ┌──────────────────────┐  │&lt;br /&gt;
│  │ TN_REQ 테이블        │  │&lt;br /&gt;
│  │ (튜닝 요청)          │  │&lt;br /&gt;
│  ├──────────────────────┤  │&lt;br /&gt;
│  │ TN_ACTN 테이블       │  │&lt;br /&gt;
│  │ (액션 내역)          │  │&lt;br /&gt;
│  └──────────────────────┘  │&lt;br /&gt;
└─────────────────────────────┘&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 상세 데이터 흐름:&lt;br /&gt;
&lt;br /&gt;
1. **데이터 조회**:&lt;br /&gt;
   ```&lt;br /&gt;
   브라우저 → loadData() → GET /api/requests → DB 쿼리 &lt;br /&gt;
   → JSON 응답 → AG Grid 표시&lt;br /&gt;
   ```&lt;br /&gt;
&lt;br /&gt;
2. **새 요청 추가**:&lt;br /&gt;
   ```&lt;br /&gt;
   모달 입력 → saveNewRequest() → POST /api/requests &lt;br /&gt;
   → DB INSERT → 그리드 새로고침&lt;br /&gt;
   ```&lt;br /&gt;
&lt;br /&gt;
3. **데이터 업데이트**:&lt;br /&gt;
   ```&lt;br /&gt;
   그리드/패널 편집 → saveChanges()/saveDetailChanges() &lt;br /&gt;
   → PUT /api/requests/&amp;lt;id&amp;gt; → DB UPDATE → 반영&lt;br /&gt;
   ```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 🚀 애플리케이션 시작 프로세스&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
┌─────────────────────────────────────┐&lt;br /&gt;
│  run.bat / run.sh 실행              │&lt;br /&gt;
└────────────┬────────────────────────┘&lt;br /&gt;
             │&lt;br /&gt;
             ▼&lt;br /&gt;
┌─────────────────────────────────────┐&lt;br /&gt;
│ [1/4] Python 설치 확인              │&lt;br /&gt;
└────────────┬────────────────────────┘&lt;br /&gt;
             │&lt;br /&gt;
             ▼&lt;br /&gt;
┌─────────────────────────────────────┐&lt;br /&gt;
│ [2/4] 가상환경 생성/활성화          │&lt;br /&gt;
└────────────┬────────────────────────┘&lt;br /&gt;
             │&lt;br /&gt;
             ▼&lt;br /&gt;
┌─────────────────────────────────────┐&lt;br /&gt;
│ [3/4] 패키지 설치 (requirements.txt) │&lt;br /&gt;
└────────────┬────────────────────────┘&lt;br /&gt;
             │&lt;br /&gt;
             ▼&lt;br /&gt;
┌─────────────────────────────────────┐&lt;br /&gt;
│ [4/4] app.py 실행                   │&lt;br /&gt;
│ ├─ db.create_all()                  │&lt;br /&gt;
│ ├─ 샘플 데이터 확인                 │&lt;br /&gt;
│ └─ Flask 서버 시작 (포트 5000)      │&lt;br /&gt;
└────────────┬────────────────────────┘&lt;br /&gt;
             │&lt;br /&gt;
             ▼&lt;br /&gt;
┌─────────────────────────────────────┐&lt;br /&gt;
│ http://localhost:5000 접속 가능     │&lt;br /&gt;
└─────────────────────────────────────┘&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 📱 주요 기능 설명&lt;br /&gt;
&lt;br /&gt;
### 1. 새 요청 추가 프로세스&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
1. &amp;quot;새 요청 추가&amp;quot; 버튼 클릭&lt;br /&gt;
   ↓&lt;br /&gt;
2. 모달 윈도우 열기 (addRequestModal)&lt;br /&gt;
   ├─ 요청자 입력&lt;br /&gt;
   ├─ DB명 입력&lt;br /&gt;
   ├─ 프로그램ID 입력&lt;br /&gt;
   ├─ 비즈니스 유형 선택&lt;br /&gt;
   ├─ ... (기타 필드)&lt;br /&gt;
   ├─ SQL 입력 (화면 50% 이상)&lt;br /&gt;
   │&lt;br /&gt;
3. &amp;quot;저장&amp;quot; 버튼 클릭&lt;br /&gt;
   ├─ 입력값 검증&lt;br /&gt;
   ├─ POST /api/requests 요청&lt;br /&gt;
   ├─ DB 저장&lt;br /&gt;
   │&lt;br /&gt;
4. 그리드 자동 새로고침&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 2. 상세 정보 확인 및 편집&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
1. 그리드에서 행 선택 또는 &amp;quot;상세보기&amp;quot; 버튼 클릭&lt;br /&gt;
   ↓&lt;br /&gt;
2. 상세 정보 패널 표시&lt;br /&gt;
   ├─ 📋 요청 정보 (읽기 전용)&lt;br /&gt;
   ├─ 💾 SQL 정보 (편집 가능)&lt;br /&gt;
   │  ├─ SQL 텍스트&lt;br /&gt;
   │  ├─ 실행 계획&lt;br /&gt;
   │  └─ 성능 지표&lt;br /&gt;
   ├─ 🔧 튜닝 정보 (편집 가능)&lt;br /&gt;
   │&lt;br /&gt;
3. 필요시 값 편집&lt;br /&gt;
   ↓&lt;br /&gt;
4. &amp;quot;💾 저장&amp;quot; 버튼 클릭&lt;br /&gt;
   ├─ 변경사항 수집&lt;br /&gt;
   ├─ PUT /api/requests/&amp;lt;id&amp;gt; 요청&lt;br /&gt;
   ├─ DB 업데이트&lt;br /&gt;
   │&lt;br /&gt;
5. 저장 완료 알림&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 3. 동적 컬럼 표시&lt;br /&gt;
&lt;br /&gt;
```&lt;br /&gt;
&amp;quot;요청정보 확인&amp;quot; 버튼 클릭&lt;br /&gt;
├─ 기본 컬럼 유지&lt;br /&gt;
├─ 요청정보 컬럼만 표시&lt;br /&gt;
│  └─ DB명, 프로그램ID, 비즈니스 유형 등&lt;br /&gt;
│&lt;br /&gt;
&amp;quot;튜닝결과 확인&amp;quot; 버튼 클릭&lt;br /&gt;
├─ 기본 컬럼 유지&lt;br /&gt;
├─ 결과 컬럼만 표시&lt;br /&gt;
│  └─ 개선여부, 힌트적용, INDEX생성 등&lt;br /&gt;
│&lt;br /&gt;
&amp;quot;전체보기&amp;quot; 버튼 클릭&lt;br /&gt;
└─ 모든 컬럼 표시&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 🔐 보안 기능&lt;br /&gt;
&lt;br /&gt;
1. **CORS 활성화**&lt;br /&gt;
   - Cross-Origin 요청 지원&lt;br /&gt;
   - 프론트엔드와 백엔드 분리 가능&lt;br /&gt;
&lt;br /&gt;
2. **UTF-8 인코딩**&lt;br /&gt;
   - 한글 문자 안전 처리&lt;br /&gt;
   - 터미널 및 데이터베이스에서 정상 표시&lt;br /&gt;
&lt;br /&gt;
3. **SECRET_KEY**&lt;br /&gt;
   - Flask 세션 보안&lt;br /&gt;
   - 쿠키 암호화&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 🐛 에러 처리&lt;br /&gt;
&lt;br /&gt;
### 백엔드 (app.py)&lt;br /&gt;
```python&lt;br /&gt;
try:&lt;br /&gt;
    # API 로직&lt;br /&gt;
    data = [req.to_dict() for req in requests]&lt;br /&gt;
    return jsonify(data)&lt;br /&gt;
except Exception as e:&lt;br /&gt;
    # 오류 로깅&lt;br /&gt;
    print(f&#039;[오류] {str(e)}&#039;)&lt;br /&gt;
    return jsonify({&#039;error&#039;: str(e)}), 500&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 프론트엔드 (index_ag.html)&lt;br /&gt;
```javascript&lt;br /&gt;
try {&lt;br /&gt;
    const r = await fetch(&#039;/api/requests&#039;);&lt;br /&gt;
    if (!r.ok) {&lt;br /&gt;
        const errorText = await r.text();&lt;br /&gt;
        alert(&#039;데이터 로드 실패: &#039; + r.status);&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
    const data = await r.json();&lt;br /&gt;
    gridOptions.api.setRowData(data);&lt;br /&gt;
} catch (err) {&lt;br /&gt;
    alert(&#039;오류 발생: &#039; + err.message);&lt;br /&gt;
    console.error(err);&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 📊 시스템 요구사항&lt;br /&gt;
&lt;br /&gt;
### 최소 요구사항&lt;br /&gt;
- **OS**: Windows 10, macOS 10.14+, Linux (Ubuntu 18.04+)&lt;br /&gt;
- **Python**: 3.8 이상&lt;br /&gt;
- **RAM**: 512MB 이상&lt;br /&gt;
- **디스크**: 200MB 이상&lt;br /&gt;
&lt;br /&gt;
### 권장 사양&lt;br /&gt;
- **OS**: Windows 11, macOS 12+, Ubuntu 22.04+&lt;br /&gt;
- **Python**: 3.10 이상&lt;br /&gt;
- **RAM**: 2GB 이상&lt;br /&gt;
- **디스크**: 1GB 이상&lt;br /&gt;
&lt;br /&gt;
### 필요한 소프트웨어&lt;br /&gt;
- Python 3.8+&lt;br /&gt;
- 웹 브라우저 (Chrome, Firefox, Safari, Edge)&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 📝 파일별 수정 이력&lt;br /&gt;
&lt;br /&gt;
### app.py&lt;br /&gt;
- UTF-8 인코딩 추가&lt;br /&gt;
- 새로운 컬럼 추가 (REQ_EXECUTION_PLAN, TUNED_EXECUTION_PLAN)&lt;br /&gt;
- 에러 처리 강화&lt;br /&gt;
- 자동 샘플 데이터 초기화&lt;br /&gt;
&lt;br /&gt;
### index_ag.html&lt;br /&gt;
- AG Grid 도입&lt;br /&gt;
- 모달 기반 새 요청 추가&lt;br /&gt;
- 상세 정보 패널 (편집 가능)&lt;br /&gt;
- 동적 컬럼 관리&lt;br /&gt;
- 한글 입출력 지원&lt;br /&gt;
&lt;br /&gt;
### run.bat&lt;br /&gt;
- UTF-8 인코딩 설정 (chcp 65001)&lt;br /&gt;
- 4단계 진행 표시&lt;br /&gt;
- Python 버전 감지&lt;br /&gt;
- 가상환경 자동 관리&lt;br /&gt;
&lt;br /&gt;
### run.sh&lt;br /&gt;
- macOS/Linux 호환성&lt;br /&gt;
- Python3 자동 감지&lt;br /&gt;
- ANSI 색상 코드 사용&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 🎯 주요 API 응답 형식&lt;br /&gt;
&lt;br /&gt;
### GET /api/requests 응답&lt;br /&gt;
```json&lt;br /&gt;
[&lt;br /&gt;
    {&lt;br /&gt;
        &amp;quot;REQ_NO&amp;quot;: 1,&lt;br /&gt;
        &amp;quot;REQ_USER&amp;quot;: &amp;quot;Kim&amp;quot;,&lt;br /&gt;
        &amp;quot;REQ_DT&amp;quot;: &amp;quot;2024-11-12 10:30:00&amp;quot;,&lt;br /&gt;
        &amp;quot;DB_NAME&amp;quot;: &amp;quot;Oracle&amp;quot;,&lt;br /&gt;
        &amp;quot;SQL_PROGRAM_ID&amp;quot;: &amp;quot;PGM001&amp;quot;,&lt;br /&gt;
        &amp;quot;REQ_SQL_TEXT&amp;quot;: &amp;quot;SELECT * FROM ...&amp;quot;,&lt;br /&gt;
        &amp;quot;TUNED_SQL_TEXT&amp;quot;: &amp;quot;SELECT ... WHERE ...&amp;quot;,&lt;br /&gt;
        &amp;quot;REQ_EXECUTION_PLAN&amp;quot;: &amp;quot;EXPLAIN 결과&amp;quot;,&lt;br /&gt;
        &amp;quot;TUNED_EXECUTION_PLAN&amp;quot;: &amp;quot;EXPLAIN 결과&amp;quot;,&lt;br /&gt;
        &amp;quot;TIME_BEFORE&amp;quot;: 1.5,&lt;br /&gt;
        &amp;quot;TIME_AFTER&amp;quot;: 0.3,&lt;br /&gt;
        &amp;quot;BLOCKS_BEFORE&amp;quot;: 100,&lt;br /&gt;
        &amp;quot;BLOCKS_AFTER&amp;quot;: 50,&lt;br /&gt;
        &amp;quot;IMPROVEMENT_STATUS&amp;quot;: &amp;quot;Y&amp;quot;,&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
]&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### POST /api/requests 요청&lt;br /&gt;
```json&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;REQ_USER&amp;quot;: &amp;quot;Kim&amp;quot;,&lt;br /&gt;
    &amp;quot;DB_NAME&amp;quot;: &amp;quot;Oracle&amp;quot;,&lt;br /&gt;
    &amp;quot;SQL_PROGRAM_ID&amp;quot;: &amp;quot;PGM001&amp;quot;,&lt;br /&gt;
    &amp;quot;REQ_SQL_TEXT&amp;quot;: &amp;quot;SELECT * FROM ...&amp;quot;,&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### PUT /api/requests/&amp;lt;id&amp;gt; 요청&lt;br /&gt;
```json&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;TUNED_SQL_TEXT&amp;quot;: &amp;quot;SELECT ... WHERE ...&amp;quot;,&lt;br /&gt;
    &amp;quot;REQ_EXECUTION_PLAN&amp;quot;: &amp;quot;EXPLAIN 결과&amp;quot;,&lt;br /&gt;
    &amp;quot;TIME_BEFORE&amp;quot;: 1.5,&lt;br /&gt;
    &amp;quot;TIME_AFTER&amp;quot;: 0.3,&lt;br /&gt;
    &amp;quot;IMPROVEMENT_STATUS&amp;quot;: &amp;quot;Y&amp;quot;,&lt;br /&gt;
    ...&lt;br /&gt;
}&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 🤝 개발 팀 노트&lt;br /&gt;
&lt;br /&gt;
### 주요 설계 원칙&lt;br /&gt;
1. **모듈화**: 각 기능을 독립적으로 개발/유지보수&lt;br /&gt;
2. **확장성**: 새로운 기능 추가 용이&lt;br /&gt;
3. **사용자 친화**: 직관적인 UI/UX&lt;br /&gt;
4. **성능**: 빠른 데이터 로드 및 응답&lt;br /&gt;
&lt;br /&gt;
### 향후 개선 계획&lt;br /&gt;
- 다중 언어 지원 (영어, 중국어 등)&lt;br /&gt;
- 고급 분석 및 리포팅&lt;br /&gt;
- 사용자 권한 관리&lt;br /&gt;
- 변경 이력 추적&lt;br /&gt;
- API 인증 (JWT)&lt;br /&gt;
- 데이터 내보내기 다양화 (Excel, PDF 등)&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 📞 문제 해결&lt;br /&gt;
&lt;br /&gt;
### 포트 5000이 이미 사용 중인 경우&lt;br /&gt;
```bash&lt;br /&gt;
# 포트 변경&lt;br /&gt;
# app.py의 마지막 줄:&lt;br /&gt;
app.run(debug=True, host=&#039;0.0.0.0&#039;, port=5001)&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 데이터베이스 손상 시&lt;br /&gt;
```bash&lt;br /&gt;
python reset_db.py&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 패키지 설치 오류&lt;br /&gt;
```bash&lt;br /&gt;
pip install --upgrade pip&lt;br /&gt;
pip install -r requirements.txt --force-reinstall&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
### 한글 출력 오류 (Windows)&lt;br /&gt;
- `run.bat`는 자동으로 UTF-8 설정&lt;br /&gt;
- 수동 설정: `chcp 65001`&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
## 📚 참고 자료&lt;br /&gt;
&lt;br /&gt;
### 사용 라이브러리 문서&lt;br /&gt;
- [Flask 공식 문서](https://flask.palletsprojects.com/)&lt;br /&gt;
- [SQLAlchemy 공식 문서](https://docs.sqlalchemy.org/)&lt;br /&gt;
- [AG Grid 공식 문서](https://www.ag-grid.com/)&lt;br /&gt;
- [Flask-CORS](https://flask-cors.readthedocs.io/)&lt;br /&gt;
&lt;br /&gt;
### 튜토리얼&lt;br /&gt;
- Flask 기초: https://flask.palletsprojects.com/tutorial/&lt;br /&gt;
- SQLAlchemy 기초: https://docs.sqlalchemy.org/tutorial/&lt;br /&gt;
- AG Grid 시작: https://www.ag-grid.com/getting-started/&lt;br /&gt;
&lt;br /&gt;
---&lt;br /&gt;
&lt;br /&gt;
**마지막 수정**: 2024-11-12  &lt;br /&gt;
**버전**: 1.0.0  &lt;br /&gt;
**작성자**: DBAWorks Team  &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===주요 수정 포인트===&lt;br /&gt;
&lt;br /&gt;
====A. 데이터베이스 필드 추가====&lt;br /&gt;
위치: `app.py` 24-82줄 (TuningRequest 클래스)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# 1. 필드 추가&lt;br /&gt;
class TuningRequest(db.Model):&lt;br /&gt;
    # ... 기존 필드들&lt;br /&gt;
    NEW_FIELD = db.Column(db.String(100))  # 새 필드 추가&lt;br /&gt;
&lt;br /&gt;
# 2. to_dict() 메서드에 추가&lt;br /&gt;
def to_dict(self):&lt;br /&gt;
    return {&lt;br /&gt;
        # ... 기존 필드들&lt;br /&gt;
        &#039;NEW_FIELD&#039;: self.NEW_FIELD,&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### B. 프론트엔드 컬럼 추가&lt;br /&gt;
위치: `index_ag.html` 227-238줄 (basicColumnDefs)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=js&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const basicColumnDefs = [&lt;br /&gt;
    // ... 기존 컬럼들&lt;br /&gt;
    { headerName: &#039;새 컬럼&#039;, field: &#039;NEW_FIELD&#039;, width: 100, editable: true },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### C. 새 요청 모달에 입력 필드 추가&lt;br /&gt;
위치: `index_ag.html` 113-197줄 (새 요청 추가 모달)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;form-field&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;새 필드&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;formNewField&amp;quot; placeholder=&amp;quot;새 필드 입력&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### D. 파일 크기 제한 변경&lt;br /&gt;
위치: `app.py` 15줄&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
app.config[&#039;MAX_CONTENT_LENGTH&#039;] = 50 * 1024 * 1024  # 50MB로 변경&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### E. 파일 타입 제한 추가&lt;br /&gt;
위치: `app.py` 276-318줄 (upload_attachment 함수)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ALLOWED_EXTENSIONS = {&#039;txt&#039;, &#039;pdf&#039;, &#039;png&#039;, &#039;jpg&#039;, &#039;sql&#039;}&lt;br /&gt;
file_extension = os.path.splitext(original_filename)[1][1:].lower()&lt;br /&gt;
&lt;br /&gt;
if file_extension not in ALLOWED_EXTENSIONS:&lt;br /&gt;
    return jsonify({&#039;error&#039;: &#039;허용되지 않는 파일 타입입니다.&#039;}), 400&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 주요 함수 위치===&lt;br /&gt;
&lt;br /&gt;
#### 백엔드 (app.py)&lt;br /&gt;
- 요청 생성: 156-188줄 (`create_request`)&lt;br /&gt;
- 요청 수정: 190-225줄 (`update_request`)&lt;br /&gt;
- 요청 삭제: 227-232줄 (`delete_request`)&lt;br /&gt;
- 파일 업로드: 276-318줄 (`upload_attachment`)&lt;br /&gt;
- 파일 다운로드: 329-341줄 (`download_attachment`)&lt;br /&gt;
- 파일 삭제: 343-361줄 (`delete_attachment`)&lt;br /&gt;
&lt;br /&gt;
#### 프론트엔드 (index_ag.html)&lt;br /&gt;
- 데이터 로드: 342-397줄 (`loadData`)&lt;br /&gt;
- 새 요청 추가: 402-425줄 (`addRow`)&lt;br /&gt;
- 새 요청 저장: 497-579줄 (`saveNewRequest`)&lt;br /&gt;
- 파일 업로드: 471-495줄 (`uploadFiles`)&lt;br /&gt;
- 상세 패널 표시: 809-1016줄 (`showDetailPanel`)&lt;br /&gt;
- 상세 정보 저장: 757-807줄 (`saveDetailChanges`)&lt;br /&gt;
&lt;br /&gt;
=== 스타일 수정===&lt;br /&gt;
위치: `index_ag.html` 9-76줄 (`&amp;lt;style&amp;gt;` 섹션)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* 색상 변경 */&lt;br /&gt;
.btn-primary {&lt;br /&gt;
    background:#2563eb;  /* 파란색 */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* 폰트 크기 변경 */&lt;br /&gt;
body {&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===API 엔드포인트 목록===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
GET    /api/requests                    # 요청 목록 조회&lt;br /&gt;
POST   /api/requests                    # 새 요청 생성&lt;br /&gt;
PUT    /api/requests/&amp;lt;req_no&amp;gt;           # 요청 수정&lt;br /&gt;
DELETE /api/requests/&amp;lt;req_no&amp;gt;           # 요청 삭제&lt;br /&gt;
GET    /api/requests/&amp;lt;req_no&amp;gt;/sql-text  # SQL 텍스트 조회&lt;br /&gt;
PUT    /api/requests/&amp;lt;req_no&amp;gt;/sql-text  # SQL 텍스트 수정&lt;br /&gt;
POST   /api/requests/&amp;lt;req_no&amp;gt;/attachments  # 파일 업로드&lt;br /&gt;
GET    /api/requests/&amp;lt;req_no&amp;gt;/attachments  # 파일 목록 조회&lt;br /&gt;
GET    /api/attachments/&amp;lt;attach_no&amp;gt;     # 파일 다운로드&lt;br /&gt;
DELETE /api/attachments/&amp;lt;attach_no&amp;gt;     # 파일 삭제&lt;br /&gt;
POST   /api/init-data                   # 샘플 데이터 초기화&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 데이터베이스 테이블===&lt;br /&gt;
&lt;br /&gt;
#### TN_REQ (TuningRequest)&lt;br /&gt;
- 주요 필드: REQ_NO, REQ_USER, REQ_DT, SQL_PROGRAM_ID 등&lt;br /&gt;
- 관계: 첨부파일(1:N), 액션(1:1)&lt;br /&gt;
&lt;br /&gt;
#### TN_ATTACH (Attachment)&lt;br /&gt;
- 주요 필드: ATTACH_NO, REQ_NO, ORIGINAL_FILENAME, STORED_FILENAME&lt;br /&gt;
- 관계: 요청(N:1)&lt;br /&gt;
&lt;br /&gt;
#### TN_ACTN (TuningAction)&lt;br /&gt;
- 주요 필드: REQ_NO, ACTION_DATE, ACTION_USER&lt;br /&gt;
- 관계: 요청(1:1)&lt;br /&gt;
&lt;br /&gt;
===수정 시 주의사항===&lt;br /&gt;
&lt;br /&gt;
1. 데이터베이스 필드 추가 시&lt;br /&gt;
   - 기존 데이터는 NULL로 설정됨&lt;br /&gt;
   - 백업 후 수정 권장&lt;br /&gt;
&lt;br /&gt;
2. 파일 업로드 시&lt;br /&gt;
   - 파일 크기 제한 확인 (기본 16MB)&lt;br /&gt;
   - 업로드 폴더 권한 확인&lt;br /&gt;
&lt;br /&gt;
3. 프론트엔드 수정 시&lt;br /&gt;
   - JavaScript 함수명 중복 주의&lt;br /&gt;
   - HTML ID 중복 주의&lt;br /&gt;
&lt;br /&gt;
4. API 수정 시&lt;br /&gt;
   - 응답 형식 일관성 유지&lt;br /&gt;
   - 에러 처리 추가 권장&lt;br /&gt;
&lt;br /&gt;
=== 빠른 수정 예시===&lt;br /&gt;
&lt;br /&gt;
#### 예시 1: 새 필드 추가&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# app.py - TuningRequest 클래스에 필드 추가&lt;br /&gt;
PRIORITY = db.Column(db.String(10))  # 우선순위&lt;br /&gt;
&lt;br /&gt;
# app.py - to_dict() 메서드에 추가&lt;br /&gt;
&#039;PRIORITY&#039;: self.PRIORITY,&lt;br /&gt;
&lt;br /&gt;
# index_ag.html - 컬럼 추가&lt;br /&gt;
{ headerName: &#039;우선순위&#039;, field: &#039;PRIORITY&#039;, width: 100, editable: true },&lt;br /&gt;
&lt;br /&gt;
# index_ag.html - 모달에 입력 필드 추가&lt;br /&gt;
&amp;lt;div class=&amp;quot;form-field&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;우선순위&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;select id=&amp;quot;formPriority&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&amp;quot;높음&amp;quot;&amp;gt;높음&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&amp;quot;보통&amp;quot;&amp;gt;보통&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&amp;quot;낮음&amp;quot;&amp;gt;낮음&amp;lt;/option&amp;gt;&lt;br /&gt;
    &amp;lt;/select&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### 예시 2: 파일 타입 제한&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# app.py - upload_attachment 함수에 추가&lt;br /&gt;
ALLOWED_EXTENSIONS = {&#039;txt&#039;, &#039;pdf&#039;, &#039;sql&#039;, &#039;jpg&#039;, &#039;png&#039;}&lt;br /&gt;
file_extension = os.path.splitext(original_filename)[1][1:].lower()&lt;br /&gt;
&lt;br /&gt;
if file_extension not in ALLOWED_EXTENSIONS:&lt;br /&gt;
    return jsonify({&#039;error&#039;: f&#039;{file_extension} 파일은 허용되지 않습니다.&#039;}), 400&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:python]]&lt;/div&gt;</summary>
		<author><name>Kokoksh88</name></author>
	</entry>
	<entry>
		<id>https://devcafe.co.kr/w/index.php?title=DBA_Tune&amp;diff=2335</id>
		<title>DBA Tune</title>
		<link rel="alternate" type="text/html" href="https://devcafe.co.kr/w/index.php?title=DBA_Tune&amp;diff=2335"/>
		<updated>2025-11-12T14:43:32Z</updated>

		<summary type="html">&lt;p&gt;Kokoksh88: /* 프로그램 구조 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== DBA Tunes ==&lt;br /&gt;
&lt;br /&gt;
## 프로그램 수정 가이드 - 핵심 요약&lt;br /&gt;
&lt;br /&gt;
===프로그램 구조===&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
app.py                    # Flask 백엔드 서버 (API, 데이터베이스)&lt;br /&gt;
templates/index_ag.html   # 프론트엔드 메인 페이지&lt;br /&gt;
instance/sql_tuning.db    # SQLite 데이터베이스&lt;br /&gt;
uploads/                  # 첨부파일 저장 폴더&lt;br /&gt;
&lt;br /&gt;
DBATunePjt/&lt;br /&gt;
├── app.py                          # Flask 백엔드 애플리케이션 메인 파일&lt;br /&gt;
├── requirements.txt                # Python 의존성 패키지 목록&lt;br /&gt;
├── init_db.py                      # 데이터베이스 초기화 스크립트&lt;br /&gt;
├── reset_db.py                     # 데이터베이스 초기화/리셋 스크립트&lt;br /&gt;
│&lt;br /&gt;
├── templates/                      # HTML 템플릿 디렉토리 (백엔드에서 렌더링)&lt;br /&gt;
│   ├── index_ag.html              # ⭐ 메인 UI (AG Grid 사용)&lt;br /&gt;
│   └── index_simple.html          # 간단한 대체 UI&lt;br /&gt;
│&lt;br /&gt;
├── static/                         # 정적 리소스 디렉토리&lt;br /&gt;
│   └── luckysheet/                # Luckysheet 라이브러리 (현재 미사용)&lt;br /&gt;
│&lt;br /&gt;
├── instance/                       # 인스턴스 데이터 디렉토리&lt;br /&gt;
│   └── sql_tuning.db             # SQLite 데이터베이스 파일&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===주요 수정 포인트===&lt;br /&gt;
&lt;br /&gt;
====A. 데이터베이스 필드 추가====&lt;br /&gt;
위치: `app.py` 24-82줄 (TuningRequest 클래스)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# 1. 필드 추가&lt;br /&gt;
class TuningRequest(db.Model):&lt;br /&gt;
    # ... 기존 필드들&lt;br /&gt;
    NEW_FIELD = db.Column(db.String(100))  # 새 필드 추가&lt;br /&gt;
&lt;br /&gt;
# 2. to_dict() 메서드에 추가&lt;br /&gt;
def to_dict(self):&lt;br /&gt;
    return {&lt;br /&gt;
        # ... 기존 필드들&lt;br /&gt;
        &#039;NEW_FIELD&#039;: self.NEW_FIELD,&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### B. 프론트엔드 컬럼 추가&lt;br /&gt;
위치: `index_ag.html` 227-238줄 (basicColumnDefs)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=js&amp;gt;&lt;br /&gt;
&lt;br /&gt;
const basicColumnDefs = [&lt;br /&gt;
    // ... 기존 컬럼들&lt;br /&gt;
    { headerName: &#039;새 컬럼&#039;, field: &#039;NEW_FIELD&#039;, width: 100, editable: true },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### C. 새 요청 모달에 입력 필드 추가&lt;br /&gt;
위치: `index_ag.html` 113-197줄 (새 요청 추가 모달)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;form-field&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;새 필드&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;formNewField&amp;quot; placeholder=&amp;quot;새 필드 입력&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### D. 파일 크기 제한 변경&lt;br /&gt;
위치: `app.py` 15줄&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
app.config[&#039;MAX_CONTENT_LENGTH&#039;] = 50 * 1024 * 1024  # 50MB로 변경&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### E. 파일 타입 제한 추가&lt;br /&gt;
위치: `app.py` 276-318줄 (upload_attachment 함수)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ALLOWED_EXTENSIONS = {&#039;txt&#039;, &#039;pdf&#039;, &#039;png&#039;, &#039;jpg&#039;, &#039;sql&#039;}&lt;br /&gt;
file_extension = os.path.splitext(original_filename)[1][1:].lower()&lt;br /&gt;
&lt;br /&gt;
if file_extension not in ALLOWED_EXTENSIONS:&lt;br /&gt;
    return jsonify({&#039;error&#039;: &#039;허용되지 않는 파일 타입입니다.&#039;}), 400&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 주요 함수 위치===&lt;br /&gt;
&lt;br /&gt;
#### 백엔드 (app.py)&lt;br /&gt;
- 요청 생성: 156-188줄 (`create_request`)&lt;br /&gt;
- 요청 수정: 190-225줄 (`update_request`)&lt;br /&gt;
- 요청 삭제: 227-232줄 (`delete_request`)&lt;br /&gt;
- 파일 업로드: 276-318줄 (`upload_attachment`)&lt;br /&gt;
- 파일 다운로드: 329-341줄 (`download_attachment`)&lt;br /&gt;
- 파일 삭제: 343-361줄 (`delete_attachment`)&lt;br /&gt;
&lt;br /&gt;
#### 프론트엔드 (index_ag.html)&lt;br /&gt;
- 데이터 로드: 342-397줄 (`loadData`)&lt;br /&gt;
- 새 요청 추가: 402-425줄 (`addRow`)&lt;br /&gt;
- 새 요청 저장: 497-579줄 (`saveNewRequest`)&lt;br /&gt;
- 파일 업로드: 471-495줄 (`uploadFiles`)&lt;br /&gt;
- 상세 패널 표시: 809-1016줄 (`showDetailPanel`)&lt;br /&gt;
- 상세 정보 저장: 757-807줄 (`saveDetailChanges`)&lt;br /&gt;
&lt;br /&gt;
=== 스타일 수정===&lt;br /&gt;
위치: `index_ag.html` 9-76줄 (`&amp;lt;style&amp;gt;` 섹션)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=html&amp;gt;&lt;br /&gt;
&lt;br /&gt;
/* 색상 변경 */&lt;br /&gt;
.btn-primary {&lt;br /&gt;
    background:#2563eb;  /* 파란색 */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* 폰트 크기 변경 */&lt;br /&gt;
body {&lt;br /&gt;
    font-size: 14px;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===API 엔드포인트 목록===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
GET    /api/requests                    # 요청 목록 조회&lt;br /&gt;
POST   /api/requests                    # 새 요청 생성&lt;br /&gt;
PUT    /api/requests/&amp;lt;req_no&amp;gt;           # 요청 수정&lt;br /&gt;
DELETE /api/requests/&amp;lt;req_no&amp;gt;           # 요청 삭제&lt;br /&gt;
GET    /api/requests/&amp;lt;req_no&amp;gt;/sql-text  # SQL 텍스트 조회&lt;br /&gt;
PUT    /api/requests/&amp;lt;req_no&amp;gt;/sql-text  # SQL 텍스트 수정&lt;br /&gt;
POST   /api/requests/&amp;lt;req_no&amp;gt;/attachments  # 파일 업로드&lt;br /&gt;
GET    /api/requests/&amp;lt;req_no&amp;gt;/attachments  # 파일 목록 조회&lt;br /&gt;
GET    /api/attachments/&amp;lt;attach_no&amp;gt;     # 파일 다운로드&lt;br /&gt;
DELETE /api/attachments/&amp;lt;attach_no&amp;gt;     # 파일 삭제&lt;br /&gt;
POST   /api/init-data                   # 샘플 데이터 초기화&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 데이터베이스 테이블===&lt;br /&gt;
&lt;br /&gt;
#### TN_REQ (TuningRequest)&lt;br /&gt;
- 주요 필드: REQ_NO, REQ_USER, REQ_DT, SQL_PROGRAM_ID 등&lt;br /&gt;
- 관계: 첨부파일(1:N), 액션(1:1)&lt;br /&gt;
&lt;br /&gt;
#### TN_ATTACH (Attachment)&lt;br /&gt;
- 주요 필드: ATTACH_NO, REQ_NO, ORIGINAL_FILENAME, STORED_FILENAME&lt;br /&gt;
- 관계: 요청(N:1)&lt;br /&gt;
&lt;br /&gt;
#### TN_ACTN (TuningAction)&lt;br /&gt;
- 주요 필드: REQ_NO, ACTION_DATE, ACTION_USER&lt;br /&gt;
- 관계: 요청(1:1)&lt;br /&gt;
&lt;br /&gt;
===수정 시 주의사항===&lt;br /&gt;
&lt;br /&gt;
1. 데이터베이스 필드 추가 시&lt;br /&gt;
   - 기존 데이터는 NULL로 설정됨&lt;br /&gt;
   - 백업 후 수정 권장&lt;br /&gt;
&lt;br /&gt;
2. 파일 업로드 시&lt;br /&gt;
   - 파일 크기 제한 확인 (기본 16MB)&lt;br /&gt;
   - 업로드 폴더 권한 확인&lt;br /&gt;
&lt;br /&gt;
3. 프론트엔드 수정 시&lt;br /&gt;
   - JavaScript 함수명 중복 주의&lt;br /&gt;
   - HTML ID 중복 주의&lt;br /&gt;
&lt;br /&gt;
4. API 수정 시&lt;br /&gt;
   - 응답 형식 일관성 유지&lt;br /&gt;
   - 에러 처리 추가 권장&lt;br /&gt;
&lt;br /&gt;
=== 빠른 수정 예시===&lt;br /&gt;
&lt;br /&gt;
#### 예시 1: 새 필드 추가&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# app.py - TuningRequest 클래스에 필드 추가&lt;br /&gt;
PRIORITY = db.Column(db.String(10))  # 우선순위&lt;br /&gt;
&lt;br /&gt;
# app.py - to_dict() 메서드에 추가&lt;br /&gt;
&#039;PRIORITY&#039;: self.PRIORITY,&lt;br /&gt;
&lt;br /&gt;
# index_ag.html - 컬럼 추가&lt;br /&gt;
{ headerName: &#039;우선순위&#039;, field: &#039;PRIORITY&#039;, width: 100, editable: true },&lt;br /&gt;
&lt;br /&gt;
# index_ag.html - 모달에 입력 필드 추가&lt;br /&gt;
&amp;lt;div class=&amp;quot;form-field&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;label&amp;gt;우선순위&amp;lt;/label&amp;gt;&lt;br /&gt;
    &amp;lt;select id=&amp;quot;formPriority&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&amp;quot;높음&amp;quot;&amp;gt;높음&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&amp;quot;보통&amp;quot;&amp;gt;보통&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&amp;quot;낮음&amp;quot;&amp;gt;낮음&amp;lt;/option&amp;gt;&lt;br /&gt;
    &amp;lt;/select&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
#### 예시 2: 파일 타입 제한&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
&lt;br /&gt;
# app.py - upload_attachment 함수에 추가&lt;br /&gt;
ALLOWED_EXTENSIONS = {&#039;txt&#039;, &#039;pdf&#039;, &#039;sql&#039;, &#039;jpg&#039;, &#039;png&#039;}&lt;br /&gt;
file_extension = os.path.splitext(original_filename)[1][1:].lower()&lt;br /&gt;
&lt;br /&gt;
if file_extension not in ALLOWED_EXTENSIONS:&lt;br /&gt;
    return jsonify({&#039;error&#039;: f&#039;{file_extension} 파일은 허용되지 않습니다.&#039;}), 400&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:python]]&lt;/div&gt;</summary>
		<author><name>Kokoksh88</name></author>
	</entry>
</feed>