refactor(backend): 로거 통일
- utils/logger.js 생성 (createLogger) - 서비스 레이어: logger 유틸리티 사용 - 라우트 레이어: fastify.log 사용 - console.error/log → 구조화된 로깅으로 변경 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f719fd9259
commit
7b227a6c56
9 changed files with 98 additions and 44 deletions
|
|
@ -195,7 +195,7 @@ export default async function photosRoutes(fastify) {
|
|||
reply.raw.end();
|
||||
} catch (error) {
|
||||
await connection.rollback();
|
||||
console.error('사진 업로드 오류:', error);
|
||||
fastify.log.error(`사진 업로드 오류: ${error.message}`);
|
||||
reply.raw.write(`data: ${JSON.stringify({ error: '사진 업로드 중 오류가 발생했습니다.' })}\n\n`);
|
||||
reply.raw.end();
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -261,6 +261,6 @@ async function saveSearchQueryAsync(fastify, query) {
|
|||
const service = new SuggestionService(fastify.db, fastify.redis);
|
||||
await service.saveSearchQuery(query);
|
||||
} catch (err) {
|
||||
console.error('[Search] 검색어 저장 실패:', err.message);
|
||||
fastify.log.error(`[Search] 검색어 저장 실패: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export default async function suggestionsRoutes(fastify) {
|
|||
suggestionService = new SuggestionService(db, redis);
|
||||
// 비동기 초기화 (형태소 분석기 로드)
|
||||
suggestionService.initialize().catch(err => {
|
||||
console.error('[Suggestions] 서비스 초기화 실패:', err.message);
|
||||
fastify.log.error(`[Suggestions] 서비스 초기화 실패: ${err.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -186,7 +186,7 @@ export default async function suggestionsRoutes(fastify) {
|
|||
|
||||
return { message: '사전이 저장되었습니다.' };
|
||||
} catch (error) {
|
||||
console.error('[Suggestions] 사전 저장 오류:', error.message);
|
||||
fastify.log.error(`[Suggestions] 사전 저장 오류: ${error.message}`);
|
||||
return reply.code(500).send({ error: '사전 저장 중 오류가 발생했습니다.' });
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
|
||||
import sharp from 'sharp';
|
||||
import config from '../config/index.js';
|
||||
import { createLogger } from '../utils/logger.js';
|
||||
|
||||
const logger = createLogger('S3');
|
||||
|
||||
// S3 클라이언트 생성
|
||||
const s3Client = new S3Client({
|
||||
|
|
@ -61,7 +64,7 @@ async function deleteFromS3(key) {
|
|||
Key: key,
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error(`S3 삭제 오류 (${key}):`, err.message);
|
||||
logger.error(`삭제 오류 (${key}): ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@
|
|||
*/
|
||||
import Inko from 'inko';
|
||||
import config, { CATEGORY_IDS } from '../../config/index.js';
|
||||
import { createLogger } from '../../utils/logger.js';
|
||||
|
||||
const inko = new Inko();
|
||||
const logger = createLogger('Meilisearch');
|
||||
const INDEX_NAME = 'schedules';
|
||||
const MIN_SCORE = config.meilisearch.minScore;
|
||||
|
||||
|
|
@ -111,7 +113,7 @@ export async function searchSchedules(meilisearch, db, query, options = {}) {
|
|||
hasMore: offset + paginatedHits.length < total,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('[Meilisearch] 검색 오류:', err.message);
|
||||
logger.error(`검색 오류: ${err.message}`);
|
||||
return { hits: [], total: 0, offset: 0, limit, hasMore: false };
|
||||
}
|
||||
}
|
||||
|
|
@ -183,9 +185,9 @@ export async function addOrUpdateSchedule(meilisearch, schedule) {
|
|||
};
|
||||
|
||||
await index.addDocuments([document]);
|
||||
console.log(`[Meilisearch] 일정 추가/업데이트: ${schedule.id}`);
|
||||
logger.info(`일정 추가/업데이트: ${schedule.id}`);
|
||||
} catch (err) {
|
||||
console.error('[Meilisearch] 문서 추가 오류:', err.message);
|
||||
logger.error(`문서 추가 오류: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -196,9 +198,9 @@ export async function deleteSchedule(meilisearch, scheduleId) {
|
|||
try {
|
||||
const index = meilisearch.index(INDEX_NAME);
|
||||
await index.deleteDocument(scheduleId);
|
||||
console.log(`[Meilisearch] 일정 삭제: ${scheduleId}`);
|
||||
logger.info(`일정 삭제: ${scheduleId}`);
|
||||
} catch (err) {
|
||||
console.error('[Meilisearch] 문서 삭제 오류:', err.message);
|
||||
logger.error(`문서 삭제 오류: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -249,11 +251,11 @@ export async function syncAllSchedules(meilisearch, db) {
|
|||
|
||||
// 일괄 추가
|
||||
await index.addDocuments(documents);
|
||||
console.log(`[Meilisearch] ${documents.length}개 일정 동기화 완료`);
|
||||
logger.info(`${documents.length}개 일정 동기화 완료`);
|
||||
|
||||
return documents.length;
|
||||
} catch (err) {
|
||||
console.error('[Meilisearch] 동기화 오류:', err.message);
|
||||
logger.error(`동기화 오류: ${err.message}`);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@
|
|||
import Inko from 'inko';
|
||||
import { extractNouns, initMorpheme, isReady } from './morpheme.js';
|
||||
import { getChosung, isChosungOnly, isChosungMatch } from './chosung.js';
|
||||
import { createLogger } from '../../utils/logger.js';
|
||||
|
||||
const inko = new Inko();
|
||||
const logger = createLogger('Suggestion');
|
||||
|
||||
// 설정
|
||||
const CONFIG = {
|
||||
|
|
@ -42,9 +44,9 @@ export class SuggestionService {
|
|||
async initialize() {
|
||||
try {
|
||||
await initMorpheme();
|
||||
console.log('[Suggestion] 서비스 초기화 완료');
|
||||
logger.info('서비스 초기화 완료');
|
||||
} catch (error) {
|
||||
console.error('[Suggestion] 서비스 초기화 실패:', error.message);
|
||||
logger.error(`서비스 초기화 실패: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +81,7 @@ export class SuggestionService {
|
|||
if (this.isEnglishOnly(normalizedQuery)) {
|
||||
const korean = this.convertEnglishToKorean(normalizedQuery);
|
||||
if (korean) {
|
||||
console.log(`[Suggestion] 한글 변환: "${normalizedQuery}" → "${korean}"`);
|
||||
logger.debug(`한글 변환: "${normalizedQuery}" → "${korean}"`);
|
||||
normalizedQuery = korean;
|
||||
}
|
||||
}
|
||||
|
|
@ -131,9 +133,9 @@ export class SuggestionService {
|
|||
}
|
||||
}
|
||||
|
||||
console.log(`[Suggestion] 저장: "${normalizedQuery}" → 명사: [${nouns.join(', ')}]`);
|
||||
logger.debug(`저장: "${normalizedQuery}" → 명사: [${nouns.join(', ')}]`);
|
||||
} catch (error) {
|
||||
console.error('[Suggestion] 저장 오류:', error.message);
|
||||
logger.error(`저장 오류: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +173,7 @@ export class SuggestionService {
|
|||
return await this.getPrefixSuggestions(searchQuery.trim(), koreanQuery?.trim(), limit);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Suggestion] 조회 오류:', error.message);
|
||||
logger.error(`조회 오류: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -200,7 +202,7 @@ export class SuggestionService {
|
|||
|
||||
return rows.map(r => `${prefix} ${r.word2}`);
|
||||
} catch (error) {
|
||||
console.error('[Suggestion] Bi-gram 조회 오류:', error.message);
|
||||
logger.error(`Bi-gram 조회 오류: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -236,7 +238,7 @@ export class SuggestionService {
|
|||
|
||||
return rows.map(r => r.query);
|
||||
} catch (error) {
|
||||
console.error('[Suggestion] Prefix 조회 오류:', error.message);
|
||||
logger.error(`Prefix 조회 오류: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -258,7 +260,7 @@ export class SuggestionService {
|
|||
|
||||
return rows.map(r => r.word);
|
||||
} catch (error) {
|
||||
console.error('[Suggestion] 초성 검색 오류:', error.message);
|
||||
logger.error(`초성 검색 오류: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
@ -293,7 +295,7 @@ export class SuggestionService {
|
|||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('[Suggestion] 인기 검색어 조회 오류:', error.message);
|
||||
logger.error(`인기 검색어 조회 오류: ${error.message}`);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
import { readFileSync } from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
import { createLogger } from '../../utils/logger.js';
|
||||
|
||||
const logger = createLogger('Morpheme');
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
|
@ -47,7 +50,7 @@ export async function initMorpheme() {
|
|||
|
||||
initPromise = (async () => {
|
||||
try {
|
||||
console.log('[Morpheme] kiwi-nlp 초기화 시작...');
|
||||
logger.info('kiwi-nlp 초기화 시작...');
|
||||
|
||||
// kiwi-nlp 동적 import (ESM)
|
||||
const { KiwiBuilder } = await import('kiwi-nlp');
|
||||
|
|
@ -69,7 +72,7 @@ export async function initMorpheme() {
|
|||
try {
|
||||
modelFiles[filename] = new Uint8Array(readFileSync(filepath));
|
||||
} catch (err) {
|
||||
console.warn(`[Morpheme] 모델 파일 로드 실패: ${filename}`);
|
||||
logger.warn(`모델 파일 로드 실패: ${filename}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,18 +81,18 @@ export async function initMorpheme() {
|
|||
try {
|
||||
modelFiles[USER_DICT] = new Uint8Array(readFileSync(userDictPath));
|
||||
userDicts = [USER_DICT];
|
||||
console.log('[Morpheme] 사용자 사전 로드 완료');
|
||||
logger.info('사용자 사전 로드 완료');
|
||||
} catch (err) {
|
||||
console.warn('[Morpheme] 사용자 사전 없음, 기본 사전만 사용');
|
||||
logger.warn('사용자 사전 없음, 기본 사전만 사용');
|
||||
}
|
||||
|
||||
// Kiwi 인스턴스 생성
|
||||
kiwi = await builder.build({ modelFiles, userDicts });
|
||||
|
||||
isInitialized = true;
|
||||
console.log('[Morpheme] kiwi-nlp 초기화 완료');
|
||||
logger.info('kiwi-nlp 초기화 완료');
|
||||
} catch (error) {
|
||||
console.error('[Morpheme] 초기화 실패:', error.message);
|
||||
logger.error(`초기화 실패: ${error.message}`);
|
||||
// 초기화 실패해도 서비스는 계속 동작 (fallback 사용)
|
||||
}
|
||||
})();
|
||||
|
|
@ -114,7 +117,7 @@ export async function extractNouns(text) {
|
|||
|
||||
// kiwi가 초기화되지 않았으면 fallback
|
||||
if (!kiwi) {
|
||||
console.warn('[Morpheme] kiwi 미초기화, fallback 사용');
|
||||
logger.warn('kiwi 미초기화, fallback 사용');
|
||||
return fallbackExtract(text);
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +144,7 @@ export async function extractNouns(text) {
|
|||
|
||||
return nouns.length > 0 ? nouns : fallbackExtract(text);
|
||||
} catch (error) {
|
||||
console.error('[Morpheme] 형태소 분석 오류:', error.message);
|
||||
logger.error(`형태소 분석 오류: ${error.message}`);
|
||||
return fallbackExtract(text);
|
||||
}
|
||||
}
|
||||
|
|
@ -167,12 +170,12 @@ export function isReady() {
|
|||
* 형태소 분석기 리로드 (사전 변경 시 호출)
|
||||
*/
|
||||
export async function reloadMorpheme() {
|
||||
console.log('[Morpheme] 리로드 시작...');
|
||||
logger.info('리로드 시작...');
|
||||
isInitialized = false;
|
||||
kiwi = null;
|
||||
initPromise = null;
|
||||
await initMorpheme();
|
||||
console.log('[Morpheme] 리로드 완료');
|
||||
logger.info('리로드 완료');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
43
backend/src/utils/logger.js
Normal file
43
backend/src/utils/logger.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* 로거 유틸리티
|
||||
* 서비스 레이어에서 사용할 수 있는 간단한 로깅 유틸리티
|
||||
*/
|
||||
|
||||
const PREFIX = {
|
||||
info: '[INFO]',
|
||||
warn: '[WARN]',
|
||||
error: '[ERROR]',
|
||||
debug: '[DEBUG]',
|
||||
};
|
||||
|
||||
function formatMessage(level, context, message) {
|
||||
const timestamp = new Date().toISOString();
|
||||
return `${timestamp} ${PREFIX[level]} [${context}] ${message}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 로거 생성
|
||||
* @param {string} context - 로깅 컨텍스트 (예: 'Meilisearch', 'Suggestions')
|
||||
* @returns {object} 로거 객체
|
||||
*/
|
||||
export function createLogger(context) {
|
||||
return {
|
||||
info: (message, ...args) => {
|
||||
console.log(formatMessage('info', context, message), ...args);
|
||||
},
|
||||
warn: (message, ...args) => {
|
||||
console.warn(formatMessage('warn', context, message), ...args);
|
||||
},
|
||||
error: (message, ...args) => {
|
||||
console.error(formatMessage('error', context, message), ...args);
|
||||
},
|
||||
debug: (message, ...args) => {
|
||||
if (process.env.DEBUG) {
|
||||
console.debug(formatMessage('debug', context, message), ...args);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// 기본 로거 (컨텍스트 없음)
|
||||
export default createLogger('App');
|
||||
|
|
@ -86,18 +86,19 @@
|
|||
|
||||
---
|
||||
|
||||
### 10단계: 로거 통일
|
||||
- [ ] `src/utils/logger.js` 생성
|
||||
- [ ] 모든 `console.error/log` → logger 사용
|
||||
### 10단계: 로거 통일 ✅ 완료
|
||||
- [x] `src/utils/logger.js` 생성
|
||||
- [x] 모든 `console.error/log` → logger 또는 fastify.log 사용
|
||||
|
||||
**대상 파일 (30개 이상):**
|
||||
- `services/image.js:61`
|
||||
- `services/meilisearch/index.js:112, 188, 201, 256`
|
||||
- `services/suggestions/index.js:47, 136, 174, 203, 239, 261, 296`
|
||||
- `services/suggestions/morpheme.js:92, 144`
|
||||
- `routes/albums/photos.js:199`
|
||||
- `routes/schedules/index.js:264`
|
||||
- `routes/schedules/suggestions.js:18, 190`
|
||||
**수정된 파일:**
|
||||
- `src/utils/logger.js` - 로거 유틸리티 생성 (createLogger)
|
||||
- `src/services/image.js` - logger 사용
|
||||
- `src/services/meilisearch/index.js` - logger 사용
|
||||
- `src/services/suggestions/index.js` - logger 사용
|
||||
- `src/services/suggestions/morpheme.js` - logger 사용
|
||||
- `src/routes/albums/photos.js` - fastify.log 사용
|
||||
- `src/routes/schedules/index.js` - fastify.log 사용
|
||||
- `src/routes/schedules/suggestions.js` - fastify.log 사용
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -120,7 +121,7 @@
|
|||
| 7단계 | 순차→병렬 쿼리 | ✅ 완료 |
|
||||
| 8단계 | meilisearch 카테고리 ID | ✅ 완료 |
|
||||
| 9단계 | 응답 형식 통일 | ✅ 완료 |
|
||||
| 10단계 | 로거 통일 | 대기 |
|
||||
| 10단계 | 로거 통일 | ✅ 완료 |
|
||||
| 11단계 | 대형 핸들러 분리 | 대기 |
|
||||
|
||||
---
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue