/** * Common Utilities * TBM/작업보고 공통 유틸리티 함수 */ class CommonUtils { /** * 서울 시간대(Asia/Seoul, UTC+9) 기준 오늘 날짜를 YYYY-MM-DD 형식으로 반환 */ getTodayKST() { const now = new Date(); const kstOffset = 9 * 60; const utc = now.getTime() + (now.getTimezoneOffset() * 60000); const kstTime = new Date(utc + (kstOffset * 60000)); const year = kstTime.getFullYear(); const month = String(kstTime.getMonth() + 1).padStart(2, '0'); const day = String(kstTime.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } /** * 날짜를 YYYY-MM-DD 형식으로 변환 (문자열 또는 Date 객체) */ formatDate(date) { if (!date) return ''; // 이미 YYYY-MM-DD 형식이면 그대로 반환 if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) { return date; } const dateObj = date instanceof Date ? date : new Date(date); const year = dateObj.getFullYear(); const month = String(dateObj.getMonth() + 1).padStart(2, '0'); const day = String(dateObj.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } /** * 요일 반환 (일/월/화/수/목/금/토) */ getDayOfWeek(date) { const dayNames = ['일', '월', '화', '수', '목', '금', '토']; const dateObj = date instanceof Date ? date : new Date(date instanceof String || typeof date === 'string' ? date + 'T00:00:00' : date); return dayNames[dateObj.getDay()]; } /** * 오늘인지 확인 */ isToday(date) { return this.formatDate(date) === this.getTodayKST(); } /** * UUID v4 생성 */ generateUUID() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0; const v = c === 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } /** * HTML 이스케이프 */ escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * 디바운스 */ debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * 쓰로틀 */ throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * 객체 깊은 복사 */ deepClone(obj) { return JSON.parse(JSON.stringify(obj)); } /** * 빈 값 확인 */ isEmpty(value) { if (value === null || value === undefined) return true; if (typeof value === 'string') return value.trim() === ''; if (Array.isArray(value)) return value.length === 0; if (typeof value === 'object') return Object.keys(value).length === 0; return false; } /** * 배열 그룹화 */ groupBy(array, key) { return array.reduce((result, item) => { const groupKey = typeof key === 'function' ? key(item) : item[key]; if (!result[groupKey]) { result[groupKey] = []; } result[groupKey].push(item); return result; }, {}); } } // 전역 인스턴스 생성 window.CommonUtils = new CommonUtils(); console.log('[Module] common/utils.js 로드 완료');