Files
TK-FB-Project/개발로그/2026-01-19_작업자관리_스키마_동기화.md
Hyungi Ahn 344ad35651 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>
2026-01-19 08:35:36 +09:00

11 KiB

개발로그 - 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 테이블 컬럼:

- 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

<!-- 수정 전 -->
<script src="/js/api-config.js?v=1" defer></script>
<script src="/js/load-navbar.js?v=4"></script>
<script src="/js/worker-management.js?v=3"></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/worker-management.js?v=7"></script>

2. 백엔드 Model 수정

파일: api.hyungi.net/models/workerModel.js

create 함수:

// 수정 전
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 함수:

// 수정 전: 고정된 컬럼 업데이트
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 함수:

// 수정 전
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 함수:

// 수정 전
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 함수:

// 수정 전
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

// 수정 후: 상세한 에러 로깅
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

// 작업자 수정 요청 로깅 추가
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의 모달 폼을 실제 테이블 구조에 맞게 수정 필요:

<!-- 제거할 필드 -->
<input id="phoneNumber">
<input id="email">
<input id="department">
<textarea id="notes">

<!-- 추가할 필드 -->
<input type="date" id="joinDate" name="join_date">
<input type="number" id="salary" name="salary" step="0.01">
<input type="number" id="annualLeave" name="annual_leave">

파일 위치: web-ui/pages/management/worker-management.html (130-180줄)

2. 프로젝트 관리 페이지 검증

프로젝트 관리 페이지도 동일한 문제가 있는지 확인 필요

3. 다른 페이지 검증

  • 작업 보고서 입력
  • 이슈 보고서
  • 대시보드

모든 페이지에서 작업자 정보를 사용하는 부분 검증 필요

배포 시 주의사항

본 서버 배포 전 확인 사항:

  1. 본 서버 DB의 workers 테이블 구조 확인
  2. 로컬 DB와 본 서버 DB 스키마 일치 여부 확인
  3. 마이그레이션 스크립트 작성 (필요 시)
  4. 백업 생성

DB 마이그레이션이 필요한 경우:

만약 본 서버에 phone_number, email 등의 컬럼이 있다면:

  1. 데이터 백업
  2. 컬럼 삭제 또는 코드 복원 결정
  3. 관련 데이터 마이그레이션

학습 내용

1. DB 스키마 검증의 중요성

  • 코드 작성 전 실제 DB 스키마 확인 필수
  • 개발 환경과 운영 환경의 스키마 동기화 중요

2. 에러 처리 개선

  • 상세한 로깅으로 디버깅 시간 단축
  • 클라이언트와 서버 양쪽 모두에 로깅 필요

3. 동적 SQL 생성

  • 필드별 선택적 업데이트 가능
  • undefined 필드 제외 처리로 유연성 향상

작업 시간

  • 문제 분석: 30분
  • 수정 작업: 45분
  • 테스트 및 문서화: 15분
  • 총 소요 시간: 약 1시간 30분

작업자

  • Claude Code (AI Assistant)
  • 날짜: 2026-01-19