refactor: shared 모듈 추출 Phase 1~4 (notifyHelper, errors, logger, auth, dbPool)

Phase 1: notifyHelper.js → shared/utils/ (4개 서비스 중복 제거)
Phase 2: auth.js → shared/middleware/ (system1/system2 통합)
Phase 3: errors.js + logger.js → shared/utils/ (system1/system2 통합)
Phase 4: DB pool → shared/config/database.js (Group B 4개 서비스 통합)

- Docker 빌드 컨텍스트를 루트로 변경 (6개 API 서비스)
- 기존 파일은 re-export 패턴으로 consumer 변경 0개 유지
- .dockerignore 추가로 빌드 최적화

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-03-17 19:07:22 +09:00
parent 84cf222b81
commit 0c149673fb
34 changed files with 861 additions and 1825 deletions

199
shared/utils/logger.js Normal file
View File

@@ -0,0 +1,199 @@
/**
* 로깅 유틸리티
*
* 애플리케이션 전체에서 사용하는 통합 로거
*
* @author TK-FB-Project
* @since 2025-12-11
*/
const fs = require('fs');
const path = require('path');
/**
* 로그 레벨 정의
*/
const LogLevel = {
ERROR: 'ERROR',
WARN: 'WARN',
INFO: 'INFO',
DEBUG: 'DEBUG'
};
/**
* 로그 레벨별 이모지
*/
const LogEmoji = {
ERROR: '❌',
WARN: '⚠️',
INFO: '',
DEBUG: '🔍'
};
/**
* 로그 레벨별 색상 (콘솔)
*/
const LogColor = {
ERROR: '\x1b[31m', // Red
WARN: '\x1b[33m', // Yellow
INFO: '\x1b[36m', // Cyan
DEBUG: '\x1b[90m', // Gray
RESET: '\x1b[0m'
};
class Logger {
constructor() {
// process.cwd() = /usr/src/app (컨테이너 WORKDIR)
// __dirname 대신 사용하여 shared/ 위치와 무관하게 서비스의 logs/ 디렉토리에 기록
this.logDir = path.join(process.cwd(), 'logs');
this.logFile = path.join(this.logDir, 'app.log');
this.errorFile = path.join(this.logDir, 'error.log');
this.ensureLogDirectory();
}
/**
* 로그 디렉토리 생성
*/
ensureLogDirectory() {
if (!fs.existsSync(this.logDir)) {
fs.mkdirSync(this.logDir, { recursive: true });
}
}
/**
* 타임스탬프 생성
*/
getTimestamp() {
return new Date().toISOString();
}
/**
* 로그 포맷팅
*/
formatLog(level, message, context = {}) {
const timestamp = this.getTimestamp();
const emoji = LogEmoji[level] || '';
const contextStr = Object.keys(context).length > 0
? `\n Context: ${JSON.stringify(context, null, 2)}`
: '';
return `[${timestamp}] [${level}] ${emoji} ${message}${contextStr}`;
}
/**
* 콘솔에 컬러 로그 출력
*/
logToConsole(level, message, context = {}) {
const color = LogColor[level] || LogColor.RESET;
const formattedLog = this.formatLog(level, message, context);
if (level === LogLevel.ERROR) {
console.error(`${color}${formattedLog}${LogColor.RESET}`);
} else if (level === LogLevel.WARN) {
console.warn(`${color}${formattedLog}${LogColor.RESET}`);
} else {
console.log(`${color}${formattedLog}${LogColor.RESET}`);
}
}
/**
* 파일에 로그 기록
*/
logToFile(level, message, context = {}) {
const formattedLog = this.formatLog(level, message, context);
const logEntry = `${formattedLog}\n`;
try {
// 모든 로그를 app.log에 기록
fs.appendFileSync(this.logFile, logEntry, 'utf8');
// 에러는 error.log에도 기록
if (level === LogLevel.ERROR) {
fs.appendFileSync(this.errorFile, logEntry, 'utf8');
}
} catch (err) {
console.error('로그 파일 기록 실패:', err);
}
}
/**
* 로그 기록 메인 함수
*/
log(level, message, context = {}) {
// 개발 환경에서는 콘솔에 출력
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV !== 'production') {
this.logToConsole(level, message, context);
}
// 프로덕션에서는 파일에만 기록
if (process.env.NODE_ENV === 'production') {
this.logToFile(level, message, context);
}
}
/**
* 에러 로그
*/
error(message, context = {}) {
this.log(LogLevel.ERROR, message, context);
}
/**
* 경고 로그
*/
warn(message, context = {}) {
this.log(LogLevel.WARN, message, context);
}
/**
* 정보 로그
*/
info(message, context = {}) {
this.log(LogLevel.INFO, message, context);
}
/**
* 디버그 로그
*/
debug(message, context = {}) {
// DEBUG 로그는 개발 환경에서만 출력
if (process.env.NODE_ENV === 'development') {
this.log(LogLevel.DEBUG, message, context);
}
}
/**
* HTTP 요청 로그
*/
http(method, url, statusCode, duration, user = 'anonymous') {
const level = statusCode >= 400 ? LogLevel.ERROR : LogLevel.INFO;
const message = `${method} ${url} - ${statusCode} (${duration}ms)`;
const context = {
method,
url,
statusCode,
duration,
user
};
this.log(level, message, context);
}
/**
* 데이터베이스 쿼리 로그
*/
query(sql, params = [], duration = 0) {
if (process.env.NODE_ENV === 'development') {
this.debug('DB Query', {
sql,
params,
duration: `${duration}ms`
});
}
}
}
// 싱글톤 인스턴스 생성 및 내보내기
const logger = new Logger();
module.exports = logger;