Tr_Code/DEPARTMENTS_API.md

6.4 KiB

📋 부서 관리 API 문서

개요

회원가입 시 부서 선택을 위한 동적 부서 목록 API입니다.


API 엔드포인트

GET /api/departments

부서 목록을 조회합니다.

인증: 불필요 (공개 API)

요청:

GET /api/departments HTTP/1.1
Host: localhost:5000

응답 (성공):

{
  "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
    }
  ]
}

응답 (실패):

{
  "success": false,
  "error": "오류 메시지"
}

프론트엔드 사용법

Vue.js (TWA 프론트엔드)

SignupView.vue에서 부서 목록 로드:

// 부서 목록 가져오기
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 템플릿 사용

<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 테이블

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()
);

초기 데이터

INSERT INTO public.departments (code, name, description) VALUES
    ('SPC', '신평차량', '신평차량사업소'),
    ('NPC', '노포차량', '노포차량사업소'),
    ('VHD', '차량처', '차량처')
ON CONFLICT (code) DO NOTHING;

부서 추가 방법

1. Supabase SQL 에디터에서 직접 추가

INSERT INTO public.departments (code, name, description) 
VALUES ('NEW001', '새로운부서', '부서 설명');

2. 관리자 페이지 (향후 구현)

관리자 권한이 있는 사용자가 웹 인터페이스를 통해 부서를 추가/수정/삭제할 수 있습니다.


필터링

활성화된 부서만 조회

현재 API는 모든 부서를 반환합니다. 비활성화된 부서를 제외하려면:

@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

에러 처리

프론트엔드 에러 처리

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)
}

성능 최적화

캐싱

부서 목록은 자주 변경되지 않으므로 캐싱을 적용할 수 있습니다:

// 간단한 메모리 캐시 (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로 테스트

curl -X GET http://localhost:5000/api/departments

브라우저 콘솔에서 테스트

fetch('/api/departments')
  .then(res => res.json())
  .then(data => console.log(data))

문제 해결

부서 목록이 비어있음

원인: departments 테이블에 데이터가 없음

해결:

SELECT * FROM public.departments;

-- 데이터가 없으면 초기 데이터 삽입
INSERT INTO public.departments (code, name, description) VALUES
    ('SPC', '신평차량', '신평차량사업소'),
    ('NPC', '노포차량', '노포차량사업소'),
    ('VHD', '차량처', '차량처')
ON CONFLICT (code) DO NOTHING;

API 호출 실패

원인: Flask 서버가 실행되지 않음 또는 CORS 오류

해결:

# 서버 실행 확인
python app.py

# CORS 설정 확인 (app.py)
CORS(app, origins=["*"], supports_credentials=True)

부산교통공사 차량처
1호선 고장코드 시스템