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:
parent
85f03cb2d8
commit
bdd2dfcd84
3 changed files with 15 additions and 11 deletions
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 캐시 키 생성 헬퍼
|
// 캐시 키 생성 헬퍼
|
||||||
|
|
|
||||||
|
|
@ -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*
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue