feat: 성능 최적화 (Phase 4)

- cache.js: Redis KEYS → SCAN으로 변경 (블로킹 방지)
- suggestions.js: 동기식 파일 I/O → 비동기 변경
  - readFileSync → readFile (fs/promises)
  - writeFileSync → writeFile (fs/promises)
- improvements.md: Phase 4 완료로 업데이트

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
caadiq 2026-01-23 22:04:27 +09:00
parent 85f03cb2d8
commit bdd2dfcd84
3 changed files with 15 additions and 11 deletions

View file

@ -1,7 +1,7 @@
/** /**
* 추천 검색어 API 라우트 * 추천 검색어 API 라우트
*/ */
import { readFileSync, writeFileSync } from 'fs'; import { readFile, writeFile } from 'fs/promises';
import { SuggestionService } from '../../services/suggestions/index.js'; import { SuggestionService } from '../../services/suggestions/index.js';
import { reloadMorpheme, getUserDictPath } from '../../services/suggestions/morpheme.js'; import { reloadMorpheme, getUserDictPath } from '../../services/suggestions/morpheme.js';
import { badRequest, serverError } from '../../utils/error.js'; import { badRequest, serverError } from '../../utils/error.js';
@ -139,7 +139,7 @@ export default async function suggestionsRoutes(fastify) {
}, async (request, reply) => { }, async (request, reply) => {
try { try {
const dictPath = getUserDictPath(); const dictPath = getUserDictPath();
const content = readFileSync(dictPath, 'utf-8'); const content = await readFile(dictPath, 'utf-8');
return { content }; return { content };
} catch (error) { } catch (error) {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
@ -180,7 +180,7 @@ export default async function suggestionsRoutes(fastify) {
try { try {
const dictPath = getUserDictPath(); const dictPath = getUserDictPath();
writeFileSync(dictPath, content, 'utf-8'); await writeFile(dictPath, content, 'utf-8');
// 형태소 분석기 리로드 // 형태소 분석기 리로드
await reloadMorpheme(); await reloadMorpheme();

View file

@ -41,15 +41,19 @@ export async function invalidate(redis, keys) {
} }
/** /**
* 패턴으로 캐시 무효화 * 패턴으로 캐시 무효화 (SCAN 사용으로 블로킹 방지)
* @param {object} redis - Redis 클라이언트 * @param {object} redis - Redis 클라이언트
* @param {string} pattern - 패턴 (: 'schedule:*') * @param {string} pattern - 패턴 (: 'schedule:*')
*/ */
export async function invalidatePattern(redis, pattern) { export async function invalidatePattern(redis, pattern) {
const keys = await redis.keys(pattern); let cursor = '0';
do {
const [nextCursor, keys] = await redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
cursor = nextCursor;
if (keys.length > 0) { if (keys.length > 0) {
await redis.del(...keys); await redis.del(...keys);
} }
} while (cursor !== '0');
} }
// 캐시 키 생성 헬퍼 // 캐시 키 생성 헬퍼

View file

@ -546,8 +546,8 @@ await writeFile(dictPath, content, 'utf-8');
| 이슈 | 우선순위 | 상태 | | 이슈 | 우선순위 | 상태 |
|------|---------|------| |------|---------|------|
| Redis KEYS → SCAN | Low | ⬜ 미해결 | | Redis KEYS → SCAN | Low | ✅ 해결됨 |
| 동기식 파일 I/O | Low | ⬜ 미해결 | | 동기식 파일 I/O | Low | ✅ 해결됨 |
--- ---
@ -560,4 +560,4 @@ await writeFile(dictPath, content, 'utf-8');
--- ---
*마지막 업데이트: 2025-01* *마지막 업데이트: 2026-01-23*