Tr_Code/DEPARTMENTS_API.md

312 lines
6.4 KiB
Markdown

# 📋 부서 관리 API 문서
## 개요
회원가입 시 부서 선택을 위한 동적 부서 목록 API입니다.
---
## API 엔드포인트
### GET `/api/departments`
부서 목록을 조회합니다.
**인증**: 불필요 (공개 API)
**요청**:
```http
GET /api/departments HTTP/1.1
Host: localhost:5000
```
**응답 (성공)**:
```json
{
"success": true,
"departments": [
{
"id": 1,
"code": "SPC",
"name": "신평차량",
"description": "신평차량사업소",
"is_active": true
},
{
"id": 2,
"code": "NPC",
"name": "노포차량",
"description": "노포차량사업소",
"is_active": true
},
{
"id": 3,
"code": "VHD",
"name": "차량처",
"description": "차량처",
"is_active": true
}
]
}
```
**응답 (실패)**:
```json
{
"success": false,
"error": "오류 메시지"
}
```
---
## 프론트엔드 사용법
### Vue.js (TWA 프론트엔드)
`SignupView.vue`에서 부서 목록 로드:
```typescript
// 부서 목록 가져오기
async function fetchDepartments() {
loadingDepartments.value = true
try {
const response = await fetch('/api/departments')
if (!response.ok) {
throw new Error('부서 목록을 가져오는데 실패했습니다.')
}
const data = await response.json()
departments.value = data.departments || []
} catch (error) {
console.error('부서 목록 로딩 실패:', error)
errorMessage.value = '부서 목록을 불러오는데 실패했습니다.'
} finally {
loadingDepartments.value = false
}
}
// 컴포넌트 마운트 시 로드
onMounted(() => {
fetchDepartments()
})
```
### HTML 템플릿 사용
```html
<select v-model="formData.departmentId" required :disabled="loadingDepartments">
<option value="">
{{ loadingDepartments ? '부서 목록 로딩 중...' : '부서를 선택하세요' }}
</option>
<option v-for="dept in departments" :key="dept.id" :value="dept.id">
{{ dept.name }}
</option>
</select>
```
---
## 데이터베이스 구조
### departments 테이블
```sql
CREATE TABLE public.departments (
id SERIAL PRIMARY KEY,
code VARCHAR(20) UNIQUE NOT NULL,
name VARCHAR(100) NOT NULL,
description TEXT,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
```
### 초기 데이터
```sql
INSERT INTO public.departments (code, name, description) VALUES
('SPC', '신평차량', '신평차량사업소'),
('NPC', '노포차량', '노포차량사업소'),
('VHD', '차량처', '차량처')
ON CONFLICT (code) DO NOTHING;
```
---
## 부서 추가 방법
### 1. Supabase SQL 에디터에서 직접 추가
```sql
INSERT INTO public.departments (code, name, description)
VALUES ('NEW001', '새로운부서', '부서 설명');
```
### 2. 관리자 페이지 (향후 구현)
관리자 권한이 있는 사용자가 웹 인터페이스를 통해 부서를 추가/수정/삭제할 수 있습니다.
---
## 필터링
### 활성화된 부서만 조회
현재 API는 모든 부서를 반환합니다. 비활성화된 부서를 제외하려면:
```python
@app.route("/api/departments")
def api_departments():
"""부서 목록 조회 API (활성화된 부서만)"""
try:
with build_pg_client() as c:
r = c.get(
"/departments",
params={
"select": "id,code,name,description,is_active",
"is_active": "eq.true", # 활성화된 부서만
"order": "name.asc"
}
)
r.raise_for_status()
departments = r.json() or []
return {
"success": True,
"departments": departments
}
except Exception as e:
return {
"success": False,
"error": str(e)
}, 500
```
---
## 에러 처리
### 프론트엔드 에러 처리
```typescript
try {
const response = await fetch('/api/departments')
if (!response.ok) {
throw new Error('부서 목록을 가져오는데 실패했습니다.')
}
const data = await response.json()
if (!data.success) {
throw new Error(data.error || '알 수 없는 오류')
}
departments.value = data.departments || []
} catch (error) {
console.error('부서 목록 로딩 실패:', error)
errorMessage.value = '부서 목록을 불러오는데 실패했습니다.'
setTimeout(() => errorMessage.value = '', 3000)
}
```
---
## 성능 최적화
### 캐싱
부서 목록은 자주 변경되지 않으므로 캐싱을 적용할 수 있습니다:
```typescript
// 간단한 메모리 캐시 (5분)
let cachedDepartments: Department[] | null = null
let cacheTime: number | null = null
const CACHE_DURATION = 5 * 60 * 1000 // 5분
async function fetchDepartments() {
const now = Date.now()
// 캐시가 유효한 경우
if (cachedDepartments && cacheTime && now - cacheTime < CACHE_DURATION) {
departments.value = cachedDepartments
return
}
// API 호출
loadingDepartments.value = true
try {
const response = await fetch('/api/departments')
const data = await response.json()
if (data.success) {
cachedDepartments = data.departments
cacheTime = now
departments.value = data.departments
}
} finally {
loadingDepartments.value = false
}
}
```
---
## 테스트
### cURL로 테스트
```bash
curl -X GET http://localhost:5000/api/departments
```
### 브라우저 콘솔에서 테스트
```javascript
fetch('/api/departments')
.then(res => res.json())
.then(data => console.log(data))
```
---
## 문제 해결
### 부서 목록이 비어있음
**원인**: `departments` 테이블에 데이터가 없음
**해결**:
```sql
SELECT * FROM public.departments;
-- 데이터가 없으면 초기 데이터 삽입
INSERT INTO public.departments (code, name, description) VALUES
('SPC', '신평차량', '신평차량사업소'),
('NPC', '노포차량', '노포차량사업소'),
('VHD', '차량처', '차량처')
ON CONFLICT (code) DO NOTHING;
```
### API 호출 실패
**원인**: Flask 서버가 실행되지 않음 또는 CORS 오류
**해결**:
```bash
# 서버 실행 확인
python app.py
# CORS 설정 확인 (app.py)
CORS(app, origins=["*"], supports_credentials=True)
```
---
**부산교통공사 차량처**
**1호선 고장코드 시스템**