# 개발로그 - 2026-01-19: 작업자 관리 스키마 동기화 ## 작업 요약 작업자 관리 페이지에서 발생하던 500 에러를 수정하였습니다. 문제의 원인은 **DB 테이블 스키마와 코드 간의 불일치**였습니다. ## 발견된 문제 ### 1. JavaScript 모듈 로딩 에러 - **증상**: `SyntaxError: Unexpected token '{'` 에러 - **원인**: ES6 `import` 구문을 사용하는 파일들이 `type="module"` 없이 로드됨 - **영향 파일**: - `load-navbar.js` - `api-config.js` - `worker-management.js` ### 2. 작업자 상태 변경 시 500 에러 - **증상**: 작업자 활성화/비활성화 시 서버 500 에러 - **에러 메시지**: `Unknown column 'phone_number' in 'field list'` - **원인**: 코드에서 사용하는 컬럼과 실제 DB 테이블 컬럼 불일치 ### 3. DB 스키마 불일치 상세 #### 실제 workers 테이블 컬럼: ```sql - worker_id (int, PK, auto_increment) - worker_name (varchar(100), NOT NULL) - join_date (date, nullable) - job_type (varchar(100), nullable) - salary (decimal(10,2), nullable) - annual_leave (int, nullable) - status (text, default 'active') - created_at (timestamp, default current_timestamp) - updated_at (timestamp, default current_timestamp on update) ``` #### 코드에서 사용하려던 컬럼 (존재하지 않음): ``` - phone_number ❌ - email ❌ - hire_date ❌ (join_date는 있음) - department ❌ - notes ❌ ``` ## 수정 내용 ### 1. HTML 파일 수정 **파일**: `web-ui/pages/management/worker-management.html` ```html ``` ### 2. 백엔드 Model 수정 **파일**: `api.hyungi.net/models/workerModel.js` #### create 함수: ```javascript // 수정 전 const create = async (worker, callback) => { const { worker_name, job_type = 'worker', phone_number = null, email = null, hire_date = null, department = null, notes = null, status = 'active' } = worker; await db.query( `INSERT INTO workers (worker_name, job_type, phone_number, email, hire_date, department, notes, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [worker_name, job_type, phone_number, email, formatDate(hire_date), department, notes, status] ); }; // 수정 후 const create = async (worker, callback) => { const { worker_name, job_type = null, join_date = null, salary = null, annual_leave = null, status = 'active' } = worker; await db.query( `INSERT INTO workers (worker_name, job_type, join_date, salary, annual_leave, status) VALUES (?, ?, ?, ?, ?, ?)`, [worker_name, job_type, formatDate(join_date), salary, annual_leave, status] ); }; ``` #### update 함수: ```javascript // 수정 전: 고정된 컬럼 업데이트 const update = async (worker, callback) => { const { worker_id, worker_name, job_type, status, phone_number, email, hire_date, department, notes } = worker; await db.query( `UPDATE workers SET worker_name = ?, job_type = ?, status = ?, phone_number = ?, email = ?, hire_date = ?, department = ?, notes = ? WHERE worker_id = ?`, [worker_name, job_type, status, phone_number, email, formatDate(hire_date), department, notes, worker_id] ); }; // 수정 후: 동적 컬럼 업데이트 const update = async (worker, callback) => { const { worker_id, worker_name, job_type, status, join_date, salary, annual_leave } = worker; // 업데이트할 필드만 동적으로 구성 const updates = []; const values = []; if (worker_name !== undefined) { updates.push('worker_name = ?'); values.push(worker_name); } if (job_type !== undefined) { updates.push('job_type = ?'); values.push(job_type); } if (status !== undefined) { updates.push('status = ?'); values.push(status); } if (join_date !== undefined) { updates.push('join_date = ?'); values.push(formatDate(join_date)); } if (salary !== undefined) { updates.push('salary = ?'); values.push(salary); } if (annual_leave !== undefined) { updates.push('annual_leave = ?'); values.push(annual_leave); } if (updates.length === 0) { callback(new Error('업데이트할 필드가 없습니다')); return; } values.push(worker_id); const query = `UPDATE workers SET ${updates.join(', ')} WHERE worker_id = ?`; await db.query(query, values); }; ``` ### 3. 프론트엔드 JavaScript 수정 **파일**: `web-ui/js/worker-management.js` #### toggleWorkerStatus 함수: ```javascript // 수정 전 const updateData = { ...worker, // 모든 필드 포함 (created_at, updated_at 등 포함) status: newStatus, is_active: newStatus === 'active' ? 1 : 0 }; // 수정 후 const updateData = { worker_name: worker.worker_name, job_type: worker.job_type || null, status: newStatus, join_date: worker.join_date || null, salary: worker.salary || null, annual_leave: worker.annual_leave || null }; ``` #### saveWorker 함수: ```javascript // 수정 전 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, status: document.getElementById('isActive').checked ? 'active' : 'inactive' }; // 수정 후 const workerData = { worker_name: document.getElementById('workerName').value.trim(), 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' }; ``` #### openWorkerModal 함수: ```javascript // 수정 전 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 || ''; // 수정 후 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 || ''; ``` ### 4. 에러 로깅 개선 **파일**: `web-ui/js/api-config.js` ```javascript // 수정 후: 상세한 에러 로깅 if (!response.ok) { let errorMessage = `HTTP ${response.status}`; try { const errorData = await response.json(); errorMessage = errorData.error || errorData.message || errorMessage; console.error('📋 서버 에러 상세:', errorData); } catch (e) { try { const errorText = await response.text(); console.error('📋 서버 에러 텍스트:', errorText); errorMessage = errorText || errorMessage; } catch (e2) { console.error('📋 에러 파싱 실패'); } } throw new Error(errorMessage); } ``` **파일**: `api.hyungi.net/controllers/workerController.js` ```javascript // 작업자 수정 요청 로깅 추가 console.log('🔧 작업자 수정 요청:', { worker_id: id, 받은데이터: req.body, 처리할데이터: workerData }); ``` ## 테스트 방법 1. 브라우저에서 강력 새로고침 (Ctrl+Shift+R 또는 Cmd+Shift+R) 2. 작업자 관리 페이지 접속 3. 작업자 상태 토글 (활성화/비활성화) 테스트 4. 브라우저 콘솔에서 로그 확인: - `📤 전송 데이터:` - 올바른 필드만 전송되는지 확인 - `📋 서버 에러 상세:` - 에러 발생 시 상세 내용 확인 ## 영향 범위 ### 수정된 파일: 1. `web-ui/pages/management/worker-management.html` 2. `web-ui/js/api-config.js` 3. `web-ui/js/worker-management.js` 4. `api.hyungi.net/models/workerModel.js` 5. `api.hyungi.net/controllers/workerController.js` ### 영향받는 기능: - ✅ 작업자 목록 조회 - ✅ 작업자 상태 변경 (활성화/비활성화) - ⚠️ 작업자 추가/수정 (HTML 폼 필드도 수정 필요) ## 향후 작업 필요 사항 ### 1. HTML 폼 업데이트 `worker-management.html`의 모달 폼을 실제 테이블 구조에 맞게 수정 필요: ```html