fix: 작업자/프로젝트 관리 페이지 모듈 로딩 및 DB 스키마 동기화 수정

## 수정 내용

### 1. JavaScript 모듈 로딩 문제 수정
- ES6 import 사용 파일에 type="module" 속성 추가
- api-config.js, load-navbar.js, worker-management.js, project-management.js

### 2. DB 스키마 불일치 해결
- workers 테이블 실제 구조에 맞게 코드 수정
- 존재하지 않는 컬럼 제거: phone_number, email, hire_date, department, notes
- 실제 컬럼 사용: join_date, salary, annual_leave

### 3. 백엔드 수정
- workerModel.js: create, update 함수를 실제 테이블 구조에 맞게 수정
- workerController.js: 상세 로깅 추가

### 4. 프론트엔드 수정
- worker-management.js: 데이터 전송 구조 수정
- api-config.js: 에러 로깅 개선
- HTML 파일: 스크립트 type="module" 추가 및 버전 업데이트

### 5. 개발 문서
- 개발로그 추가: 2026-01-19_작업자관리_스키마_동기화.md

## 영향 범위
- 작업자 관리 페이지: 상태 변경 기능 정상화
- 프로젝트 관리 페이지: 모듈 로딩 오류 수정

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-01-19 08:35:36 +09:00
parent 79bd9324ca
commit 344ad35651
7 changed files with 471 additions and 73 deletions

View File

@@ -120,8 +120,16 @@ async function apiCall(url, method = 'GET', data = null) {
try {
const errorData = await response.json();
errorMessage = errorData.error || errorData.message || errorMessage;
console.error('📋 서버 에러 상세:', errorData);
} catch (e) {
// JSON 파싱 실패시 기본 메시지 사용
// JSON 파싱 실패시 텍스트로 시도
try {
const errorText = await response.text();
console.error('📋 서버 에러 텍스트:', errorText);
errorMessage = errorText || errorMessage;
} catch (e2) {
console.error('📋 에러 파싱 실패');
}
}
throw new Error(errorMessage);
}
@@ -129,15 +137,16 @@ async function apiCall(url, method = 'GET', data = null) {
const result = await response.json();
console.log(`✅ API 성공: ${fullUrl}`);
return result;
} catch (error) {
console.error(`❌ API 오류 (${fullUrl}):`, error);
console.error('❌ 에러 전체 내용:', JSON.stringify(error, null, 2));
// 네트워크 오류 vs 서버 오류 구분
if (error.name === 'TypeError' && error.message.includes('fetch')) {
throw new Error('네트워크 연결 오류입니다. 인터넷 연결을 확인해주세요.');
}
throw error;
}
}

View File

@@ -429,24 +429,30 @@ function openWorkerModal(worker = null) {
// 수정 모드
modalTitle.textContent = '작업자 정보 수정';
deleteBtn.style.display = 'inline-flex';
// 폼에 데이터 채우기
// 폼에 데이터 채우기 (실제 테이블 구조에 맞게)
document.getElementById('workerId').value = worker.worker_id;
document.getElementById('workerName').value = worker.worker_name || '';
document.getElementById('jobType').value = worker.job_type || 'worker';
document.getElementById('phoneNumber').value = worker.phone_number || '';
document.getElementById('email').value = worker.email || '';
document.getElementById('hireDate').value = worker.hire_date || '';
document.getElementById('department').value = worker.department || '';
document.getElementById('notes').value = worker.notes || '';
document.getElementById('jobType').value = worker.job_type || '';
// 옵션 필드들 - 존재하는 경우에만 설정
const joinDateElem = document.getElementById('joinDate');
if (joinDateElem) joinDateElem.value = worker.join_date || '';
const salaryElem = document.getElementById('salary');
if (salaryElem) salaryElem.value = worker.salary || '';
const annualLeaveElem = document.getElementById('annualLeave');
if (annualLeaveElem) annualLeaveElem.value = worker.annual_leave || '';
// is_active 값 처리 (DB에서 0/1로 오는 경우 대비)
const isActiveValue = worker.status !== 'inactive' && worker.is_active !== 0 && worker.is_active !== false;
document.getElementById('isActive').checked = isActiveValue;
console.log('🔧 작업자 로드:', {
worker_id: worker.worker_id,
worker_name: worker.worker_name,
job_type: worker.job_type,
status: worker.status,
is_active_raw: worker.is_active,
is_active_processed: isActiveValue
@@ -455,7 +461,7 @@ function openWorkerModal(worker = null) {
// 신규 등록 모드
modalTitle.textContent = '새 작업자 등록';
deleteBtn.style.display = 'none';
// 폼 초기화
document.getElementById('workerForm').reset();
document.getElementById('workerId').value = '';
@@ -496,19 +502,18 @@ function editWorker(workerId) {
async function saveWorker() {
try {
const form = document.getElementById('workerForm');
// 실제 테이블 구조에 맞는 필드만 사용
const workerData = {
worker_name: document.getElementById('workerName').value.trim(),
job_type: document.getElementById('jobType').value || 'worker',
phone_number: document.getElementById('phoneNumber').value.trim() || null,
email: document.getElementById('email').value.trim() || null,
hire_date: document.getElementById('hireDate').value || null,
department: document.getElementById('department').value.trim() || null,
notes: document.getElementById('notes').value.trim() || null,
job_type: document.getElementById('jobType').value || null,
join_date: document.getElementById('joinDate')?.value || null,
salary: document.getElementById('salary')?.value || null,
annual_leave: document.getElementById('annualLeave')?.value || null,
status: document.getElementById('isActive').checked ? 'active' : 'inactive'
};
console.log('💾 저장할 작업자 데이터:', workerData);
console.log('💾 저장할 작업자 데이터:', JSON.stringify(workerData, null, 2));
// 필수 필드 검증
if (!workerData.worker_name) {
@@ -560,15 +565,21 @@ async function toggleWorkerStatus(workerId) {
}
console.log(`🔄 작업자 상태 변경: ${worker.worker_name}${newStatus}`);
try {
// 실제 테이블 구조에 맞는 필드만 전송
const updateData = {
...worker,
worker_name: worker.worker_name,
job_type: worker.job_type || null,
status: newStatus,
is_active: newStatus === 'active' ? 1 : 0
join_date: worker.join_date || null,
salary: worker.salary || null,
annual_leave: worker.annual_leave || null
};
const response = await window.apiCall(`${window.API}/workers/${workerId}`, 'PUT', updateData);
console.log('📤 전송 데이터:', JSON.stringify(updateData, null, 2));
const response = await window.apiCall(`/workers/${workerId}`, 'PUT', updateData);
if (response) {
// 로컬 데이터 업데이트
@@ -732,3 +743,4 @@ window.filterWorkers = filterWorkers;
window.sortWorkers = sortWorkers;
window.refreshWorkerList = refreshWorkerList;
window.filterByStatus = filterByStatus;
window.toggleWorkerStatus = toggleWorkerStatus;

View File

@@ -207,8 +207,8 @@
</div>
<!-- JavaScript -->
<script src="/js/api-config.js?v=13"></script>
<script src="/js/load-navbar.js?v=4"></script>
<script src="/js/project-management.js?v=1"></script>
<script type="module" src="/js/api-config.js?v=3"></script>
<script type="module" src="/js/load-navbar.js?v=5"></script>
<script type="module" src="/js/project-management.js?v=2"></script>
</body>
</html>

View File

@@ -8,7 +8,7 @@
<link rel="stylesheet" href="/css/project-management.css?v=3">
<link rel="icon" type="image/png" href="/img/favicon.png">
<script src="/js/auth-check.js?v=1" defer></script>
<script src="/js/api-config.js?v=1" defer></script>
<script type="module" src="/js/api-config.js?v=3"></script>
</head>
<body>
<div class="work-report-container">
@@ -195,7 +195,7 @@
</main>
</div>
<script src="/js/load-navbar.js?v=4"></script>
<script src="/js/worker-management.js?v=3"></script>
<script type="module" src="/js/load-navbar.js?v=5"></script>
<script type="module" src="/js/worker-management.js?v=7"></script>
</body>
</html>