/** * 에러 처리 모듈 (ErrorHandler.js) * ======================================== * 에러를 한곳에서 처리하고 로깅합니다. * * [초보자용] * ErrorHandler.safeExecute(() => 위험한함수(), 기본값) * ErrorHandler.handle(error, { context: '내모듈' }) * * @module ErrorHandler */ class ErrorHandler { constructor() { this.errorLog = []; this.maxLogSize = 100; this.onErrorCallbacks = []; } /** * 에러 처리 * @param {Error|string} error - 에러 객체 또는 메시지 * @param {Object} context - 컨텍스트 정보 * @param {boolean} showToUser - 사용자에게 표시할지 여부 */ static handle(error, context = {}, showToUser = false) { const errorInfo = this._normalizeError(error, context); // 콘솔에 로그 console.error('[ErrorHandler]', errorInfo); // 에러 로그에 추가 if (this.instance) { this.instance._addToLog(errorInfo); } // 콜백 실행 if (this.instance) { this.instance.onErrorCallbacks.forEach(callback => { try { callback(errorInfo); } catch (e) { console.error('[ErrorHandler] Error in callback:', e); } }); } // 사용자에게 표시 if (showToUser) { this._showToUser(errorInfo); } return errorInfo; } /** * 에러를 정규화 * @private */ static _normalizeError(error, context) { const errorInfo = { message: '', stack: '', timestamp: new Date().toISOString(), context: {}, ...context, }; if (error instanceof Error) { errorInfo.message = error.message; errorInfo.stack = error.stack; errorInfo.name = error.name; } else if (typeof error === 'string') { errorInfo.message = error; } else { errorInfo.message = 'Unknown error'; errorInfo.originalError = error; } return errorInfo; } /** * 사용자에게 에러 표시 * @private */ static _showToUser(errorInfo) { // ModalBase가 있으면 사용, 없으면 alert if (typeof AlertModal !== 'undefined') { const alert = new AlertModal({ title: '오류 발생', message: errorInfo.message || '알 수 없는 오류가 발생했습니다.', }); alert.show(); } else if (typeof ModalBase !== 'undefined') { // 간단한 알림 모달 생성 const modal = new ModalBase(); modal.create({ content: `
${errorInfo.message || '오류가 발생했습니다.'}
`, }); modal.open(); } else { // 최후의 수단: alert alert(errorInfo.message || '오류가 발생했습니다.'); } } /** * 에러 로그에 추가 * @private */ _addToLog(errorInfo) { this.errorLog.push(errorInfo); // 최대 크기 초과 시 오래된 항목 제거 if (this.errorLog.length > this.maxLogSize) { this.errorLog.shift(); } } /** * 에러 콜백 등록 * @param {Function} callback - 콜백 함수 */ onError(callback) { if (typeof callback === 'function') { this.onErrorCallbacks.push(callback); } } /** * 에러 콜백 제거 * @param {Function} callback - 콜백 함수 */ offError(callback) { const index = this.onErrorCallbacks.indexOf(callback); if (index > -1) { this.onErrorCallbacks.splice(index, 1); } } /** * 에러 로그 가져오기 * @param {number} limit - 최대 개수 * @returns {Array} */ getErrorLog(limit = null) { if (limit) { return this.errorLog.slice(-limit); } return [...this.errorLog]; } /** * 에러 로그 초기화 */ clearErrorLog() { this.errorLog = []; } /** * 안전한 함수 실행 (에러 처리 포함) * @param {Function} fn - 실행할 함수 * @param {*} defaultValue - 에러 발생 시 반환할 기본값 * @param {Object} context - 컨텍스트 정보 * @returns {*} */ static safeExecute(fn, defaultValue = null, context = {}) { try { return fn(); } catch (error) { this.handle(error, context, false); return defaultValue; } } /** * 안전한 비동기 함수 실행 (에러 처리 포함) * @param {Function} fn - 실행할 함수 * @param {*} defaultValue - 에러 발생 시 반환할 기본값 * @param {Object} context - 컨텍스트 정보 * @returns {Promise} */ static async safeExecuteAsync(fn, defaultValue = null, context = {}) { try { return await fn(); } catch (error) { this.handle(error, context, false); return defaultValue; } } /** * 전역 에러 핸들러 설정 */ static setupGlobalHandlers() { // 전역 에러 핸들러 window.addEventListener('error', (event) => { this.handle(event.error || event.message, { type: 'global', filename: event.filename, lineno: event.lineno, colno: event.colno, }, false); }); // Promise rejection 핸들러 window.addEventListener('unhandledrejection', (event) => { this.handle(event.reason, { type: 'unhandledRejection', }, false); }); } } // 싱글톤 인스턴스 ErrorHandler.instance = new ErrorHandler(); // ES6 모듈 내보내기 if (typeof module !== "undefined" && module.exports) { module.exports = ErrorHandler; }