# TBM 시스템 배포 가이드 ## 📋 개요 TBM (Tool Box Meeting) 시스템은 아침 안전 회의 및 팀 구성 관리를 위한 기능입니다. **배포일**: 2026-01-20 **버전**: 1.0.0 --- ## 🗄️ 데이터베이스 마이그레이션 ### 필수 마이그레이션 파일 본 서버에 배포 시 반드시 실행해야 할 마이그레이션: 1. **`20260120000000_create_tbm_system.js`** - TBM 시스템 테이블 생성 2. **`20260120000001_add_tbm_page.js`** - TBM 페이지 등록 ### 생성되는 테이블 #### 1. `tbm_sessions` - TBM 세션 (아침 미팅) ```sql CREATE TABLE `tbm_sessions` ( `session_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `session_date` DATE NOT NULL COMMENT 'TBM 날짜', `leader_id` INT NOT NULL COMMENT '팀장 worker_id', `project_id` INT NULL COMMENT '프로젝트 ID', `work_location` VARCHAR(200) NULL COMMENT '작업 장소', `work_description` TEXT NULL COMMENT '작업 내용', `safety_notes` TEXT NULL COMMENT '안전 관련 특이사항', `status` ENUM('draft', 'completed', 'cancelled') DEFAULT 'draft' COMMENT '상태', `start_time` TIME NULL COMMENT 'TBM 시작 시간', `end_time` TIME NULL COMMENT 'TBM 종료 시간', `created_by` INT NOT NULL COMMENT '생성자 user_id', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX `idx_session_date_leader` (`session_date`, `leader_id`), FOREIGN KEY (`leader_id`) REFERENCES `workers` (`worker_id`), FOREIGN KEY (`project_id`) REFERENCES `projects` (`project_id`) ON DELETE SET NULL, FOREIGN KEY (`created_by`) REFERENCES `users` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` #### 2. `tbm_team_assignments` - TBM 팀 구성 ```sql CREATE TABLE `tbm_team_assignments` ( `assignment_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `session_id` INT UNSIGNED NOT NULL COMMENT 'TBM 세션 ID', `worker_id` INT NOT NULL COMMENT '팀원 worker_id', `assigned_role` VARCHAR(100) NULL COMMENT '역할/담당', `work_detail` TEXT NULL COMMENT '세부 작업 내용', `is_present` BOOLEAN DEFAULT TRUE COMMENT '출석 여부', `absence_reason` TEXT NULL COMMENT '결석 사유', `assigned_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY `uk_session_worker` (`session_id`, `worker_id`), FOREIGN KEY (`session_id`) REFERENCES `tbm_sessions` (`session_id`) ON DELETE CASCADE, FOREIGN KEY (`worker_id`) REFERENCES `workers` (`worker_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` #### 3. `tbm_safety_checks` - 안전 체크리스트 마스터 ```sql CREATE TABLE `tbm_safety_checks` ( `check_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `check_category` VARCHAR(50) NOT NULL COMMENT '카테고리 (PPE, EQUIPMENT, ENVIRONMENT, EMERGENCY)', `check_item` VARCHAR(200) NOT NULL COMMENT '체크 항목', `description` TEXT NULL COMMENT '설명', `display_order` INT DEFAULT 0 COMMENT '표시 순서', `is_required` BOOLEAN DEFAULT TRUE COMMENT '필수 체크 여부', `is_active` BOOLEAN DEFAULT TRUE COMMENT '활성 여부', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX `idx_check_category` (`check_category`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` **초기 데이터 (17개 항목)**: - PPE (개인 보호 장비): 5개 - EQUIPMENT (장비 점검): 4개 - ENVIRONMENT (작업 환경): 4개 - EMERGENCY (비상 대응): 3개 #### 4. `tbm_safety_records` - 안전 체크 기록 ```sql CREATE TABLE `tbm_safety_records` ( `record_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `session_id` INT UNSIGNED NOT NULL COMMENT 'TBM 세션 ID', `check_id` INT UNSIGNED NOT NULL COMMENT '체크 항목 ID', `is_checked` BOOLEAN DEFAULT FALSE COMMENT '체크 여부', `notes` TEXT NULL COMMENT '비고/특이사항', `checked_by` INT NULL COMMENT '체크한 user_id', `checked_at` TIMESTAMP NULL COMMENT '체크 시간', UNIQUE KEY `uk_session_check` (`session_id`, `check_id`), FOREIGN KEY (`session_id`) REFERENCES `tbm_sessions` (`session_id`) ON DELETE CASCADE, FOREIGN KEY (`check_id`) REFERENCES `tbm_safety_checks` (`check_id`), FOREIGN KEY (`checked_by`) REFERENCES `users` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` #### 5. `team_handovers` - 작업 인계 ```sql CREATE TABLE `team_handovers` ( `handover_id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `session_id` INT UNSIGNED NOT NULL COMMENT 'TBM 세션 ID', `from_leader_id` INT NOT NULL COMMENT '인계자 worker_id', `to_leader_id` INT NOT NULL COMMENT '인수자 worker_id', `handover_date` DATE NOT NULL COMMENT '인계 날짜', `handover_time` TIME NULL COMMENT '인계 시간', `reason` ENUM('half_day', 'early_leave', 'emergency', 'other') NOT NULL COMMENT '인계 사유', `handover_notes` TEXT NULL COMMENT '인계 내용', `worker_ids` TEXT NULL COMMENT '인계하는 작업자 IDs (JSON array)', `is_confirmed` BOOLEAN DEFAULT FALSE COMMENT '인수 확인 여부', `confirmed_at` TIMESTAMP NULL COMMENT '인수 확인 시간', `confirmed_by` INT NULL COMMENT '인수 확인자 user_id', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, INDEX `idx_session_handover_date` (`session_id`, `handover_date`), FOREIGN KEY (`session_id`) REFERENCES `tbm_sessions` (`session_id`) ON DELETE CASCADE, FOREIGN KEY (`from_leader_id`) REFERENCES `workers` (`worker_id`), FOREIGN KEY (`to_leader_id`) REFERENCES `workers` (`worker_id`), FOREIGN KEY (`confirmed_by`) REFERENCES `users` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` --- ## 🚀 배포 절차 ### 1. 데이터베이스 마이그레이션 실행 ```bash # 본 서버에서 실행 cd /path/to/api.hyungi.net # 환경 변수 설정 (필요 시) export DB_HOST=your_db_host export DB_PORT=your_db_port export DB_USER=your_db_user export DB_PASSWORD=your_db_password export DB_NAME=hyungi # 마이그레이션 실행 npm run db:migrate # 또는 직접 실행 npx knex migrate:latest --knexfile knexfile.js ``` ### 2. 마이그레이션 확인 ```bash # 마이그레이션 상태 확인 npx knex migrate:status --knexfile knexfile.js # 테이블 생성 확인 mysql -u root -p -e "SHOW TABLES LIKE 'tbm%'" hyungi mysql -u root -p -e "SHOW TABLES LIKE 'team_handovers'" hyungi # 안전 체크리스트 초기 데이터 확인 mysql -u root -p -e "SELECT check_category, COUNT(*) as count FROM tbm_safety_checks GROUP BY check_category" hyungi ``` 예상 결과: ``` +----------------+-------+ | check_category | count | +----------------+-------+ | PPE | 5 | | EQUIPMENT | 4 | | ENVIRONMENT | 4 | | EMERGENCY | 3 | +----------------+-------+ ``` ### 3. API 서버 재시작 ```bash # PM2 사용 시 pm2 restart api-hyungi # 또는 직접 재시작 pm2 stop api-hyungi pm2 start ecosystem.config.js --env production ``` ### 4. 페이지 권한 확인 ```bash # TBM 페이지가 pages 테이블에 등록되었는지 확인 mysql -u root -p -e "SELECT * FROM pages WHERE page_key='tbm'\G" hyungi ``` 예상 결과: ``` page_id: [auto_increment] page_key: tbm page_name: TBM 관리 page_path: /pages/work/tbm.html category: work description: Tool Box Meeting - 아침 안전 회의 및 팀 구성 관리 is_admin_only: 0 display_order: 10 ``` --- ## 📡 API 엔드포인트 ### TBM 세션 관리 - `POST /api/tbm/sessions` - TBM 세션 생성 - `GET /api/tbm/sessions/date/:date` - 특정 날짜의 TBM 세션 목록 - `GET /api/tbm/sessions/:sessionId` - TBM 세션 상세 조회 - `PUT /api/tbm/sessions/:sessionId` - TBM 세션 수정 - `POST /api/tbm/sessions/:sessionId/complete` - TBM 세션 완료 ### 팀 구성 관리 - `POST /api/tbm/sessions/:sessionId/team` - 팀원 추가 (단일) - `POST /api/tbm/sessions/:sessionId/team/batch` - 팀원 일괄 추가 - `GET /api/tbm/sessions/:sessionId/team` - 팀 구성 조회 - `DELETE /api/tbm/sessions/:sessionId/team/:workerId` - 팀원 제거 ### 안전 체크리스트 - `GET /api/tbm/safety-checks` - 모든 안전 체크 항목 조회 - `GET /api/tbm/sessions/:sessionId/safety` - 안전 체크 기록 조회 - `POST /api/tbm/sessions/:sessionId/safety` - 안전 체크 일괄 저장 ### 작업 인계 - `POST /api/tbm/handovers` - 작업 인계 생성 - `POST /api/tbm/handovers/:handoverId/confirm` - 작업 인계 확인 - `GET /api/tbm/handovers/date/:date` - 특정 날짜의 인계 목록 - `GET /api/tbm/handovers/pending` - 나에게 온 미확인 인계 건 ### 통계 및 리포트 - `GET /api/tbm/statistics/tbm?startDate=&endDate=` - TBM 통계 - `GET /api/tbm/statistics/leaders?startDate=&endDate=` - 리더별 통계 --- ## 🔐 권한 설정 ### 1. 관리자가 페이지 권한 설정 1. 관리자 계정으로 로그인 2. `/pages/admin/page-access.html` 접속 3. 권한을 부여할 사용자 선택 4. "TBM 관리" 페이지 체크 5. 저장 ### 2. 기본 권한 (권장) - **그룹장 (Leader)**: TBM 페이지 접근 권한 부여 필요 - **관리자 (Admin)**: 자동으로 모든 페이지 접근 가능 - **일반 작업자 (User)**: 필요에 따라 부여 --- ## 📁 파일 구조 ### 백엔드 (API) ``` api.hyungi.net/ ├── db/migrations/ │ ├── 20260120000000_create_tbm_system.js # TBM 테이블 생성 │ └── 20260120000001_add_tbm_page.js # TBM 페이지 등록 ├── models/ │ └── tbmModel.js # TBM 데이터 모델 ├── controllers/ │ └── tbmController.js # TBM 컨트롤러 ├── routes/ │ └── tbmRoutes.js # TBM 라우트 └── config/ └── routes.js # 라우트 등록 (수정됨) ``` ### 프론트엔드 (Web UI) ``` web-ui/ ├── pages/work/ │ └── tbm.html # TBM 페이지 └── js/ └── tbm.js # TBM JavaScript (예정) ``` --- ## ⚠️ 주의사항 ### 1. 외래 키 제약 조건 - `workers` 테이블의 `worker_id`는 **signed INT(11)** - `users` 테이블의 `user_id`는 **signed INT(11)** - `projects` 테이블의 PK는 `project_id` (NOT `id`) - 외래 키 컬럼은 반드시 **signed INT**로 선언 (unsigned 사용 금지) ### 2. 데이터 정합성 - TBM 세션 삭제 시 관련 팀 구성, 안전 체크 기록 자동 삭제 (CASCADE) - 작업자 삭제 시 관련 TBM 세션도 삭제됨 (workers 테이블의 CASCADE 설정) ### 3. 백업 마이그레이션 실행 전 **반드시 데이터베이스 백업**: ```bash mysqldump -u root -p hyungi > backup_before_tbm_$(date +%Y%m%d_%H%M%S).sql ``` ### 4. 롤백 문제 발생 시 롤백: ```bash # 한 단계 롤백 npx knex migrate:rollback --knexfile knexfile.js # 또는 백업 복구 mysql -u root -p hyungi < backup_before_tbm_YYYYMMDD_HHMMSS.sql ``` --- ## ✅ 배포 체크리스트 배포 전 확인: - [ ] 데이터베이스 백업 완료 - [ ] 마이그레이션 파일 본 서버에 복사 완료 - [ ] 환경 변수 설정 확인 (DB 접속 정보) - [ ] 마이그레이션 실행 완료 - [ ] 5개 테이블 생성 확인 - [ ] tbm_safety_checks 초기 데이터 (17개) 확인 - [ ] pages 테이블에 TBM 페이지 등록 확인 - [ ] API 서버 재시작 완료 - [ ] API 엔드포인트 테스트 (최소 1개) - [ ] TBM 페이지 접속 테스트 - [ ] 권한 설정 테스트 (그룹장 계정) --- ## 🔍 테스트 방법 ### 1. API 테스트 ```bash # 안전 체크 항목 조회 curl -X GET http://localhost:20005/api/tbm/safety-checks \ -H "Authorization: Bearer YOUR_TOKEN" # 오늘 날짜의 TBM 세션 조회 curl -X GET http://localhost:20005/api/tbm/sessions/date/2026-01-20 \ -H "Authorization: Bearer YOUR_TOKEN" ``` ### 2. 웹 페이지 테스트 1. 그룹장 계정으로 로그인 2. `/pages/work/tbm.html` 접속 3. "새 TBM 시작" 버튼 클릭 4. TBM 세션 생성 및 팀 구성 --- ## 📞 문제 해결 ### 문제 1: 마이그레이션 실패 (errno: 150) **원인**: 외래 키 제약 조건 오류 (데이터 타입 불일치) **해결**: 마이그레이션 파일에서 외래 키 컬럼을 signed INT로 수정 ### 문제 2: API 엔드포인트 404 **원인**: 라우트 등록 누락 또는 서버 미재시작 **해결**: ```bash pm2 logs api-hyungi --lines 50 # 로그 확인 pm2 restart api-hyungi # 서버 재시작 ``` ### 문제 3: TBM 페이지 접근 불가 **원인**: 페이지 권한 미설정 **해결**: 관리자가 `/pages/admin/page-access.html`에서 권한 부여 --- ## 📝 변경 이력 ### v1.0.0 (2026-01-20) - TBM 시스템 초기 배포 - 5개 테이블 생성 - 17개 안전 체크리스트 항목 - API 엔드포인트 17개 - 페이지 권한 시스템 연동 --- ## 📚 관련 문서 - [작업자-계정 연동 가이드](./worker-account-integration.md) - [페이지 권한 관리 가이드](./page-access-management.md) - [데이터베이스 스키마](./database-schema.md) --- **작성자**: Claude **최종 수정일**: 2026-01-20