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:
@@ -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)
|
||||
|
||||
### 중요도
|
||||
|
||||
Reference in New Issue
Block a user