From bc5df775953cf3a0232d33e627ecde155bc9301f Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Fri, 19 Dec 2025 10:33:29 +0900 Subject: [PATCH] refactor(db): Replace SELECT * with explicit columns in models Replaced `SELECT *` statements across multiple data models with explicit column lists to improve query performance, reduce data transfer, and increase code clarity. This is part of the Phase 2 refactoring plan. - Refactored queries in the following models: - projectModel - toolsModel - attendanceModel - dailyIssueReportModel - issueTypeModel - workReportModel - userModel - dailyWorkReportModel fix(api): Add missing volume mounts to docker-compose Modified docker-compose.yml to mount the `config`, `middlewares`, `utils`, and `services` directories into the API container. This fixes a `MODULE_NOT_FOUND` error that caused the container to crash on startup. feat(db): Add migration for missing project columns Created a new database migration to add `is_active`, `project_status`, and `completed_date` columns to the `projects` table, resolving an inconsistency between the model code and the schema. docs: Add deployment notes Added a new markdown file to document the testing (macOS, Docker Desktop) and production (Synology NAS, Container Manager) environments. --- .../20251219010000_add_columns_to_projects.js | 23 +++++++++++++++++++ api.hyungi.net/models/attendanceModel.js | 8 +++---- .../models/dailyIssueReportModel.js | 2 +- api.hyungi.net/models/dailyWorkReportModel.js | 10 ++++---- api.hyungi.net/models/issueTypeModel.js | 2 +- api.hyungi.net/models/projectModel.js | 6 ++--- api.hyungi.net/models/toolsModel.js | 4 ++-- api.hyungi.net/models/userModel.js | 2 +- api.hyungi.net/models/workReportModel.js | 4 ++-- docker-compose.yml | 4 ++++ docs/deployment_notes.md | 13 +++++++++++ 11 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 api.hyungi.net/db/migrations/20251219010000_add_columns_to_projects.js create mode 100644 docs/deployment_notes.md diff --git a/api.hyungi.net/db/migrations/20251219010000_add_columns_to_projects.js b/api.hyungi.net/db/migrations/20251219010000_add_columns_to_projects.js new file mode 100644 index 0000000..c618cc6 --- /dev/null +++ b/api.hyungi.net/db/migrations/20251219010000_add_columns_to_projects.js @@ -0,0 +1,23 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = function(knex) { + return knex.schema.table('projects', function (table) { + table.boolean('is_active').defaultTo(true).after('pm'); + table.string('project_status').defaultTo('active').after('is_active'); + table.date('completed_date').nullable().after('project_status'); + }); +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = function(knex) { + return knex.schema.table('projects', function (table) { + table.dropColumn('is_active'); + table.dropColumn('project_status'); + table.dropColumn('completed_date'); + }); +}; diff --git a/api.hyungi.net/models/attendanceModel.js b/api.hyungi.net/models/attendanceModel.js index e34b2b5..0f7a8c4 100644 --- a/api.hyungi.net/models/attendanceModel.js +++ b/api.hyungi.net/models/attendanceModel.js @@ -137,7 +137,7 @@ class AttendanceModel { // 휴가 유형 정보 조회 const [vacationTypes] = await db.execute( - 'SELECT * FROM vacation_types WHERE type_code = ?', + 'SELECT id, type_code, type_name, hours_deduction, description, is_active, created_at, updated_at FROM vacation_types WHERE type_code = ?', [vacationType] ); @@ -222,7 +222,7 @@ class AttendanceModel { static async getAttendanceTypes() { const db = await getDb(); const [rows] = await db.execute( - 'SELECT * FROM work_attendance_types WHERE is_active = TRUE ORDER BY id' + 'SELECT id, type_code, type_name, description, is_active, created_at, updated_at FROM work_attendance_types WHERE is_active = TRUE ORDER BY id' ); return rows; } @@ -231,7 +231,7 @@ class AttendanceModel { static async getVacationTypes() { const db = await getDb(); const [rows] = await db.execute( - 'SELECT * FROM vacation_types WHERE is_active = TRUE ORDER BY hours_deduction DESC' + 'SELECT id, type_code, type_name, hours_deduction, description, is_active, created_at, updated_at FROM vacation_types WHERE is_active = TRUE ORDER BY hours_deduction DESC' ); return rows; } @@ -242,7 +242,7 @@ class AttendanceModel { const currentYear = year || new Date().getFullYear(); const [rows] = await db.execute(` - SELECT * FROM worker_vacation_balance + SELECT id, worker_id, year, total_annual_leave, used_annual_leave, notes, created_at, updated_at FROM worker_vacation_balance WHERE worker_id = ? AND year = ? `, [workerId, currentYear]); diff --git a/api.hyungi.net/models/dailyIssueReportModel.js b/api.hyungi.net/models/dailyIssueReportModel.js index e333c26..8d7f119 100644 --- a/api.hyungi.net/models/dailyIssueReportModel.js +++ b/api.hyungi.net/models/dailyIssueReportModel.js @@ -66,7 +66,7 @@ const getAllByDate = async (date) => { const getById = async (id, callback) => { try { const db = await getDb(); - const [rows] = await db.query(`SELECT * FROM DailyIssueReports WHERE id = ?`, [id]); + const [rows] = await db.query(`SELECT id, date, worker_id, project_id, issue_type_id, description, created_at, start_time, end_time FROM DailyIssueReports WHERE id = ?`, [id]); callback(null, rows[0]); } catch (err) { callback(err); diff --git a/api.hyungi.net/models/dailyWorkReportModel.js b/api.hyungi.net/models/dailyWorkReportModel.js index 2b8b442..f9e799e 100644 --- a/api.hyungi.net/models/dailyWorkReportModel.js +++ b/api.hyungi.net/models/dailyWorkReportModel.js @@ -7,7 +7,7 @@ const { getDb } = require('../dbPool'); const getAllWorkTypes = async (callback) => { try { const db = await getDb(); - const [rows] = await db.query('SELECT * FROM work_types ORDER BY name ASC'); + const [rows] = await db.query('SELECT id, name, description, category, created_at, updated_at FROM work_types ORDER BY name ASC'); callback(null, rows); } catch (err) { console.error('작업 유형 조회 오류:', err); @@ -18,7 +18,7 @@ const getAllWorkTypes = async (callback) => { const getAllWorkStatusTypes = async (callback) => { try { const db = await getDb(); - const [rows] = await db.query('SELECT * FROM work_status_types ORDER BY id ASC'); + const [rows] = await db.query('SELECT id, name, description, is_error, created_at FROM work_status_types ORDER BY id ASC'); callback(null, rows); } catch (err) { console.error('업무 상태 유형 조회 오류:', err); @@ -29,7 +29,7 @@ const getAllWorkStatusTypes = async (callback) => { const getAllErrorTypes = async (callback) => { try { const db = await getDb(); - const [rows] = await db.query('SELECT * FROM error_types ORDER BY name ASC'); + const [rows] = await db.query('SELECT id, name, description, severity, solution_guide, created_at, updated_at FROM error_types ORDER BY name ASC'); callback(null, rows); } catch (err) { console.error('에러 유형 조회 오류:', err); @@ -689,7 +689,7 @@ const removeByDateAndWorker = async (date, worker_id, deletedBy, callback) => { // 삭제 전 정보 저장 (감사 로그용) const [reportInfos] = await conn.query( - 'SELECT * FROM daily_work_reports WHERE report_date = ? AND worker_id = ?', + 'SELECT id, report_date, worker_id, project_id, work_type_id, work_status_id, error_type_id, work_hours, created_at, updated_at, created_by, updated_by FROM daily_work_reports WHERE report_date = ? AND worker_id = ?', [date, worker_id] ); @@ -948,7 +948,7 @@ const removeReportById = async (reportId, deletedByUserId) => { await conn.beginTransaction(); // 감사 로그를 위해 삭제 전 정보 조회 - const [reportInfo] = await conn.query('SELECT * FROM daily_work_reports WHERE id = ?', [reportId]); + const [reportInfo] = await conn.query('SELECT id, report_date, worker_id, project_id, work_type_id, work_status_id, error_type_id, work_hours, created_at, updated_at, created_by, updated_by FROM daily_work_reports WHERE id = ?', [reportId]); // 실제 삭제 작업 const [result] = await conn.query('DELETE FROM daily_work_reports WHERE id = ?', [reportId]); diff --git a/api.hyungi.net/models/issueTypeModel.js b/api.hyungi.net/models/issueTypeModel.js index a63612c..5547bd9 100644 --- a/api.hyungi.net/models/issueTypeModel.js +++ b/api.hyungi.net/models/issueTypeModel.js @@ -18,7 +18,7 @@ const create = async (type, callback) => { const getAll = async (callback) => { try { const db = await getDb(); - const [rows] = await db.query(`SELECT * FROM IssueTypes ORDER BY category, subcategory`); + const [rows] = await db.query(`SELECT issue_type_id, category, subcategory FROM IssueTypes ORDER BY category, subcategory`); callback(null, rows); } catch (err) { callback(err); diff --git a/api.hyungi.net/models/projectModel.js b/api.hyungi.net/models/projectModel.js index 8c5fd63..ea47c0c 100644 --- a/api.hyungi.net/models/projectModel.js +++ b/api.hyungi.net/models/projectModel.js @@ -29,7 +29,7 @@ const getAll = async (callback) => { try { const db = await getDb(); const [rows] = await db.query( - `SELECT * FROM projects ORDER BY project_id DESC` + `SELECT project_id, job_no, project_name, contract_date, due_date, delivery_method, site, pm, is_active, project_status, completed_date, created_at, updated_at FROM projects ORDER BY project_id DESC` ); callback(null, rows); } catch (err) { @@ -42,7 +42,7 @@ const getActiveProjects = async (callback) => { try { const db = await getDb(); const [rows] = await db.query( - `SELECT * FROM projects + `SELECT project_id, job_no, project_name, contract_date, due_date, delivery_method, site, pm, is_active, project_status, completed_date, created_at, updated_at FROM projects WHERE is_active = TRUE ORDER BY project_name ASC` ); @@ -56,7 +56,7 @@ const getById = async (project_id, callback) => { try { const db = await getDb(); const [rows] = await db.query( - `SELECT * FROM projects WHERE project_id = ?`, + `SELECT project_id, job_no, project_name, contract_date, due_date, delivery_method, site, pm, is_active, project_status, completed_date, created_at, updated_at FROM projects WHERE project_id = ?`, [project_id] ); callback(null, rows[0]); diff --git a/api.hyungi.net/models/toolsModel.js b/api.hyungi.net/models/toolsModel.js index cbae251..032cf97 100644 --- a/api.hyungi.net/models/toolsModel.js +++ b/api.hyungi.net/models/toolsModel.js @@ -4,7 +4,7 @@ const { getDb } = require('../dbPool'); const getAll = async (callback) => { try { const db = await getDb(); - const [rows] = await db.query('SELECT * FROM Tools'); + const [rows] = await db.query('SELECT id, name, location, stock, status, factory_id, map_x, map_y, map_zone, map_note FROM Tools'); callback(null, rows); } catch (err) { callback(err); @@ -15,7 +15,7 @@ const getAll = async (callback) => { const getById = async (id, callback) => { try { const db = await getDb(); - const [rows] = await db.query('SELECT * FROM Tools WHERE id = ?', [id]); + const [rows] = await db.query('SELECT id, name, location, stock, status, factory_id, map_x, map_y, map_zone, map_note FROM Tools WHERE id = ?', [id]); callback(null, rows[0]); } catch (err) { callback(err); diff --git a/api.hyungi.net/models/userModel.js b/api.hyungi.net/models/userModel.js index 56ac942..e495f3d 100644 --- a/api.hyungi.net/models/userModel.js +++ b/api.hyungi.net/models/userModel.js @@ -5,7 +5,7 @@ const findByUsername = async (username) => { try { const db = await getDb(); const [rows] = await db.query( - 'SELECT * FROM users WHERE username = ?', [username] + 'SELECT user_id, username, password, name, email, role, access_level, worker_id, is_active, last_login_at, password_changed_at, failed_login_attempts, locked_until, created_at, updated_at FROM users WHERE username = ?', [username] ); return rows[0]; } catch (err) { diff --git a/api.hyungi.net/models/workReportModel.js b/api.hyungi.net/models/workReportModel.js index b9348e8..db0cf48 100644 --- a/api.hyungi.net/models/workReportModel.js +++ b/api.hyungi.net/models/workReportModel.js @@ -110,7 +110,7 @@ const getByRange = async (start, end, callback) => { try { const db = await getDb(); const [rows] = await db.query( - `SELECT * FROM WorkReports + `SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports WHERE \`date\` BETWEEN ? AND ? ORDER BY \`date\` ASC`, [start, end] @@ -128,7 +128,7 @@ const getById = async (id, callback) => { try { const db = await getDb(); const [rows] = await db.query( - `SELECT * FROM WorkReports WHERE id = ?`, + `SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports WHERE id = ?`, [id] ); callback(null, rows[0]); diff --git a/docker-compose.yml b/docker-compose.yml index 446d196..a624df1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -55,6 +55,10 @@ services: - ./api.hyungi.net/routes:/usr/src/app/routes - ./api.hyungi.net/controllers:/usr/src/app/controllers - ./api.hyungi.net/models:/usr/src/app/models + - ./api.hyungi.net/config:/usr/src/app/config + - ./api.hyungi.net/middlewares:/usr/src/app/middlewares + - ./api.hyungi.net/utils:/usr/src/app/utils + - ./api.hyungi.net/services:/usr/src/app/services - ./api.hyungi.net/index.js:/usr/src/app/index.js logging: driver: "json-file" diff --git a/docs/deployment_notes.md b/docs/deployment_notes.md new file mode 100644 index 0000000..da2d653 --- /dev/null +++ b/docs/deployment_notes.md @@ -0,0 +1,13 @@ +# 배포 관련 참고 사항 (Deployment Notes) + +**작성일**: 2025-12-19 + +## 테스트 환경 (Development/Test Environment) + +* 현재 이 프로젝트는 **MacBook Pro**에서 **Docker Desktop**을 통해 테스트 서버로 운영되고 있습니다. +* 개발 및 테스트 시에는 `docker-compose.yml`을 사용하여 로컬 환경에서 데이터베이스 및 API 서버를 실행합니다. + +## 프로덕션 환경 (Production Environment) + +* 이 FB 프로그램은 최종적으로 회사 **Synology NAS 923+**에 **Container Manager**를 통해 설치 및 배포될 예정입니다. +* 배포 시에는 Synology Container Manager의 환경 설정에 맞게 Docker Compose 파일을 조정해야 할 수 있습니다. 특히 볼륨 마운트 경로, 네트워크 설정, 환경 변수 관리 등에 유의해야 합니다.