From 8f98bdbd3a7149db1af65f56000d7f807d1fc0b4 Mon Sep 17 00:00:00 2001 From: clkim Date: Mon, 30 Mar 2026 13:05:57 +0900 Subject: [PATCH] =?UTF-8?q?refactor.=20LRU=20=EC=BA=90=EC=8B=9C=EB=A5=BC?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=97=AC=20API=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=BA=90=EC=8B=B1=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- layers/middleware/pageData.global.ts | 24 ++++++++++++++++---- layers/server/middleware/gameData.ts | 34 ++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/layers/middleware/pageData.global.ts b/layers/middleware/pageData.global.ts index da8e1e6..3d6e067 100644 --- a/layers/middleware/pageData.global.ts +++ b/layers/middleware/pageData.global.ts @@ -1,3 +1,4 @@ +import { LRUCache } from 'lru-cache' import { usePageDataStore } from '#layers/stores/usePageDataStore' import { useLoadingStore } from '#layers/stores/useLoadingStore' import { commonFetch } from '#layers/utils/apiUtil' @@ -5,6 +6,12 @@ import { getGameDomain, getPathAfterLanguage } from '#layers/utils/urlUtil' import { DEFAULT_LOCALE_CODE } from '@/i18n.config' import type { PageDataResponse } from '#layers/types/api/pageData' +/** 페이지 데이터 API 응답 LRU */ +const pageDataResponseCache = new LRUCache({ + max: 100, + ttl: 1000 * 60, +}) + export default defineNuxtRouteMiddleware(async (to, _from) => { const runtimeConfig = useRuntimeConfig() @@ -64,9 +71,18 @@ export default defineNuxtRouteMiddleware(async (to, _from) => { } } - pageDataResponse = (await commonFetch('GET', apiUrl, { - query: queryParams, - })) as PageDataResponse | null + const cacheKey = `${apiUrl}:${JSON.stringify(queryParams)}` + const cached = pageDataResponseCache.get(cacheKey) + if (cached) { + pageDataResponse = cached + } else { + pageDataResponse = (await commonFetch('GET', apiUrl, { + query: queryParams, + })) as PageDataResponse | null + if (pageDataResponse?.code === 0 && 'value' in pageDataResponse) { + pageDataResponseCache.set(cacheKey, pageDataResponse) + } + } console.log('🚀 ~ pageData.global response:', pageDataResponse) } catch (error) { @@ -119,7 +135,7 @@ export default defineNuxtRouteMiddleware(async (to, _from) => { }) ) } - return navigateTo(`/${currentLangCode}/home`) + return navigateTo(`/${currentLangCode}/home`, { external: true }) } // [TODO] diff --git a/layers/server/middleware/gameData.ts b/layers/server/middleware/gameData.ts index 4b79fd0..10cd27e 100644 --- a/layers/server/middleware/gameData.ts +++ b/layers/server/middleware/gameData.ts @@ -61,11 +61,16 @@ function setCacheHeaders( event.node.res.setHeader('Cache-Control', cacheControl) } -const cache = new LRUCache({ +const inspectionDataCache = new LRUCache({ max: 100, // 캐시에 저장할 최대 항목 수 ttl: 1000 * 30, // 30초 동안 캐시 유지 }) +const gameDataResponseCache = new LRUCache({ + max: 100, + ttl: 1000 * 60, // 60초 +}) + /** * Locale Middleware 역할 함수 * URL의 언어 코드를 최종 언어로 변경하거나 추가 @@ -152,14 +157,23 @@ export default defineEventHandler(async event => { game_domain: gameDomain || '', lang_code: getPathLocale(event?.node.req.url), } - const response = await $fetch(gameApiUrl, { - query: queryParams, - }) + const gameDataCacheKey = `${gameApiUrl}:${JSON.stringify(queryParams)}` + const cachedGameData = gameDataResponseCache.get(gameDataCacheKey) + if (cachedGameData) { + gameDataResponse = cachedGameData + } else { + const response = await $fetch(gameApiUrl, { + query: queryParams, + }) + gameDataResponse = response + if (gameDataResponse?.code === 0 && 'value' in gameDataResponse) { + gameDataResponseCache.set(gameDataCacheKey, gameDataResponse) + } + } - gameDataResponse = response - gameDataLangCodes = response?.value?.lang_codes || null - gameDataDefaultLangCode = response?.value?.default_lang_code || null - gameDataIntro = response?.value?.intro?.page_url || '' + gameDataLangCodes = gameDataResponse?.value?.lang_codes || null + gameDataDefaultLangCode = gameDataResponse?.value?.default_lang_code || null + gameDataIntro = gameDataResponse?.value?.intro?.page_url || '' event.context.gameDomain = gameDomain } catch (error) { // eslint-disable-next-line no-console @@ -201,7 +215,7 @@ export default defineEventHandler(async event => { let inspectionData // 3. 캐시된 데이터가 없거나 만료되었을 때만 API 호출 - const cachedData = cache.get(cacheKey) + const cachedData = inspectionDataCache.get(cacheKey) if (cachedData) { inspectionData = cachedData } else { @@ -214,7 +228,7 @@ export default defineEventHandler(async event => { }) inspectionData = response?.value?.inspection as WebInspectionData if (inspectionData) { - cache.set(cacheKey, inspectionData) // 캐시에 저장 + inspectionDataCache.set(cacheKey, inspectionData) // 캐시에 저장 } }