fix: 부적합 제출 버그 수정 및 UI 개선

- 부적합 API 호출 형식 수정 (카테고리/아이템 추가 시)
- 부적합 저장 시 내부 플래그 제거 후 백엔드 전송
- 기본 부적합 객체 구조 수정 (category_id, item_id 추가)
- 날씨 API 시간대 수정 (UTC → KST 변환)
- 신고 카테고리 관리 페이지 추가 (/pages/admin/issue-categories.html)
- 부적합 입력 UI 개선 (대분류→소분류 캐스케이딩 선택)
- 저장된 부적합 분리 표시 및 수정/삭제 기능
- 디버깅 로그 추가

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-02-03 09:31:26 +09:00
parent 4b158de1eb
commit c42c9f4fa3
29 changed files with 7042 additions and 809 deletions

View File

@@ -310,6 +310,96 @@ Synology NAS(MySQL 8.0)의 `Strict Mode`로 인해 `db.execute()` 사용 시 `In
---
## 🕐 시간대(Timezone) 처리 가이드
### 핵심 원칙
이 프로젝트는 **한국 시간(KST, UTC+9)** 기준으로 운영됩니다.
### 문제 상황
서버가 UTC 시간대로 설정된 경우, `NOW()`, `CURRENT_TIMESTAMP`, `new Date()`를 사용하면 **한국 시간과 9시간 차이**가 발생합니다.
```
예시: 한국 시간 2026-02-03 08:00 AM에 신고 등록
- UTC 시간: 2026-02-02 11:00 PM
- DB 저장: 2026-02-02 (잘못된 날짜!)
```
### 해결 방법
#### 백엔드 (Node.js)
**공용 유틸리티 사용** (`utils/dateUtils.js`):
```javascript
const { getKoreaDatetime, getKoreaDateString } = require('../utils/dateUtils');
// 비즈니스 날짜 저장 시
const reportDate = getKoreaDatetime(); // '2026-02-03 08:00:00'
await db.query('INSERT INTO reports (report_date, ...) VALUES (?, ...)', [reportDate, ...]);
// 날짜만 필요한 경우
const today = getKoreaDateString(); // '2026-02-03'
```
**제공되는 함수들**:
| 함수 | 반환값 | 용도 |
|------|--------|------|
| `getKoreaDatetime()` | `'2026-02-03 08:00:00'` | DB DATETIME 저장 |
| `getKoreaDateString()` | `'2026-02-03'` | DB DATE 저장 |
| `getKoreaTimeString()` | `'08:00:00'` | DB TIME 저장 |
| `getKoreaYear()` | `2026` | 연도 |
| `getKoreaMonth()` | `2` | 월 (1-12) |
| `toKoreaDatetime(date)` | `'2026-02-03 08:00:00'` | Date 객체 변환 |
#### 프론트엔드 (JavaScript)
```javascript
// 로컬 시간대 기준 날짜 문자열
function getLocalDateString() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// ❌ 잘못된 방법 (UTC 변환됨)
const today = new Date().toISOString().split('T')[0];
// ✅ 올바른 방법 (로컬 시간)
const today = getLocalDateString();
```
### 컬럼별 적용 기준
| 컬럼 유형 | 처리 방법 | 비고 |
|-----------|-----------|------|
| `created_at`, `updated_at` | `NOW()` 또는 `CURRENT_TIMESTAMP` 사용 가능 | 감사용 메타데이터, UTC로 저장해도 무방 |
| `report_date`, `session_date` | **반드시 `getKoreaDatetime()` 사용** | 비즈니스 날짜, 사용자에게 표시됨 |
| `visit_date`, `attendance_date` | **반드시 한국 시간 기준** | 필터링/조회에 사용됨 |
| API 응답 `timestamp` | `new Date().toISOString()` 사용 가능 | 디버깅용 |
### 마이그레이션 주의사항
새 테이블 생성 시 비즈니스 날짜 컬럼은 **default 값을 사용하지 말고** 애플리케이션에서 명시적으로 설정:
```javascript
// ❌ 잘못된 방법
table.datetime('report_date').defaultTo(knex.fn.now());
// ✅ 올바른 방법
table.datetime('report_date').notNullable(); // default 없음
// 애플리케이션에서 getKoreaDatetime()으로 값 설정
```
### 기존 데이터 보정 (필요시)
UTC로 잘못 저장된 데이터를 한국 시간으로 보정:
```sql
-- 주의: 백업 후 실행
UPDATE work_issue_reports
SET report_date = DATE_ADD(report_date, INTERVAL 9 HOUR)
WHERE report_date < '2026-02-03';
```
---
## 🧪 테스트 가이드 (Jest)
### 중요도