diff --git a/i18n/locales/de.ts b/i18n/locales/de.ts index e7f3c28..ae011fa 100644 --- a/i18n/locales/de.ts +++ b/i18n/locales/de.ts @@ -1,29 +1,27 @@ +import fallback from './fallback/de' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['de'] || {} + return localeData + } + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/en.ts b/i18n/locales/en.ts index e7f3c28..c575f26 100644 --- a/i18n/locales/en.ts +++ b/i18n/locales/en.ts @@ -1,29 +1,27 @@ +import fallback from './fallback/en' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['en'] || {} + return localeData + } + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/es.ts b/i18n/locales/es.ts index e7f3c28..af54510 100644 --- a/i18n/locales/es.ts +++ b/i18n/locales/es.ts @@ -1,29 +1,27 @@ +import fallback from './fallback/es' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['es'] || {} + return localeData + } + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/fr.ts b/i18n/locales/fr.ts index e7f3c28..1d9e195 100644 --- a/i18n/locales/fr.ts +++ b/i18n/locales/fr.ts @@ -1,29 +1,27 @@ +import fallback from './fallback/fr' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['fr'] || {} + return localeData + } + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/ja.ts b/i18n/locales/ja.ts index e7f3c28..5f22c6b 100644 --- a/i18n/locales/ja.ts +++ b/i18n/locales/ja.ts @@ -1,29 +1,27 @@ +import fallback from './fallback/ja' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['ja'] || {} + return localeData + } + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/ko.ts b/i18n/locales/ko.ts index 9f08531..c73dcde 100644 --- a/i18n/locales/ko.ts +++ b/i18n/locales/ko.ts @@ -1,17 +1,24 @@ +import fallback from './fallback/ko' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const result = (await commonFetch('GET', `${translationApi}`)) as any - if(import.meta.client) { - console.log("🚀 ~ result:", result[locale]) + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, + }) + + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['ko'] || {} + return localeData } - return result[locale] + + return {} } catch (e) { console.error('[Exception] ko.defineI18nLocale: ', e) return fallback diff --git a/i18n/locales/pt.ts b/i18n/locales/pt.ts index e7f3c28..0d969ce 100644 --- a/i18n/locales/pt.ts +++ b/i18n/locales/pt.ts @@ -1,29 +1,27 @@ +import fallback from './fallback/pt' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['pt'] || {} + return localeData + } + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/th.ts b/i18n/locales/th.ts index e7f3c28..fa815ee 100644 --- a/i18n/locales/th.ts +++ b/i18n/locales/th.ts @@ -1,29 +1,27 @@ +import fallback from './fallback/th' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['th'] || {} + return localeData + } + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/zh-cn.ts b/i18n/locales/zh-cn.ts index 1f3ef75..67ff6a9 100644 --- a/i18n/locales/zh-cn.ts +++ b/i18n/locales/zh-cn.ts @@ -1,34 +1,27 @@ +import fallback from './fallback/zh-cn' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const { data } = await useFetch(translationApi, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, }) - - if(locale === 'zh-cn') { - locale = 'zh-CN' + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['zh-cn'] || {} + return localeData } - - // API 데이터에서 locale에 맞는 데이터를 추출 - const apiData = data.value?.[locale] || {} // locale에 맞는 데이터가 없으면 빈 객체 반환 - - // API 데이터와 common.json 데이터를 병합 (common.json이 우선순위) - const finalResult = { ...apiData } - - return finalResult - } catch (error) { - console.error('Error fetching translation data:', error) - // 에러 발생 시 common.json 데이터라도 반환 - return commonData[locale] || {} + + return {} + } catch (e) { + console.error('[Exception] ko.defineI18nLocale: ', e) + return fallback } }) + diff --git a/i18n/locales/zh-tw.ts b/i18n/locales/zh-tw.ts index 9f08531..1a9c011 100644 --- a/i18n/locales/zh-tw.ts +++ b/i18n/locales/zh-tw.ts @@ -1,17 +1,24 @@ +import fallback from './fallback/zh-tw' + export default defineI18nLocale(async (locale: string) => { - //https://static-pubcomm.gate8.com/dev/test/multilingual/test_common_template.json?20251021185116 - const config = useRuntimeConfig() - const rootPath = config.public.staticUrl - const runType = config.public.runType - - const translationApi = `${rootPath}/${runType}/test/multilingual/test_common_template.json` - + const runtimeConfig = useRuntimeConfig() + const dataResourcesUrl = runtimeConfig.public.dataResourcesUrl as string + const multilingualFileName = 'test_common_template.json' try { - const result = (await commonFetch('GET', `${translationApi}`)) as any - if(import.meta.client) { - console.log("🚀 ~ result:", result[locale]) + const resultGetMultilingual = await useGetMultilingual({ + baseApiUrl: dataResourcesUrl, + fileName: multilingualFileName, + }) + + // multilingual 객체에서 현재 locale에 해당하는 데이터 추출 + const multilingualData = resultGetMultilingual?.value?.multilingual + if (multilingualData && typeof multilingualData === 'object') { + // locale이 'ko'이므로 'ko' 키의 데이터를 반환 + const localeData = multilingualData[locale] || multilingualData['zh-tw'] || {} + return localeData } - return result[locale] + + return {} } catch (e) { console.error('[Exception] ko.defineI18nLocale: ', e) return fallback diff --git a/layers/components/blocks/StoveGnbNew.vue b/layers/components/blocks/StoveGnbNew.vue index 60b31e6..db7d873 100644 --- a/layers/components/blocks/StoveGnbNew.vue +++ b/layers/components/blocks/StoveGnbNew.vue @@ -8,11 +8,12 @@ onMounted(() => { const langCodes = gameData?.lang_codes const defaultLangCode = gameData?.default_lang_code const stoveGnbData = gameData?.stove_gnb_json + const designTheme = gameData?.design_theme const currentDomain = window.location.protocol + '//' + window.location.hostname if (typeof window !== 'undefined' && (window as any).StoveGnb) { - mountedInstance = (window as any).StoveGnb.mount('#stove-wrap', { + const stoveGnbOptions = { logArea: currentDomain, useLanguageCodeFromPath: true, serviceTitle: { @@ -38,14 +39,18 @@ onMounted(() => { }, mode: { theme: { - default: - stoveGnbData?.skin_type === 'gnb-dark-mini' ? 'dark' : 'light', - support: ['dark', 'light'], + support: ['light', 'dark'], + default: designTheme === 1 ? 'light' : 'dark', + // support: designTheme === 1 ? ['light'] : ['dark'], }, mini: true, + layout: 'wide', fixed: false, - }, - }) + } + } + mountedInstance = (window as any).StoveGnb.mount('#stove-wrap', stoveGnbOptions) + + console.log("🚀 ~ onMounted ~ stoveGnbOptions:", stoveGnbOptions) } }) diff --git a/layers/components/layouts/Footer.vue b/layers/components/layouts/Footer.vue index 936d456..93c2df1 100644 --- a/layers/components/layouts/Footer.vue +++ b/layers/components/layouts/Footer.vue @@ -1,20 +1,20 @@ @@ -141,20 +138,20 @@ diff --git a/layers/composables/useApiData.ts b/layers/composables/useApiData.ts deleted file mode 100644 index cc5277c..0000000 --- a/layers/composables/useApiData.ts +++ /dev/null @@ -1,22 +0,0 @@ - -interface ReqApiData { - baseApiUrl: string - url: string - } - - export const useApiData = async (req: ReqApiData): Promise => { - const dataUrl = `${req.baseApiUrl}/${req.url}` // 정상 URL 경로 - try { - const fetch = await $fetch(dataUrl, { - method: 'GET', - headers: { - 'Content-Type': 'application/json;charset=UTF-8' - } - }) - return fetch - } catch (error) { - console.log('error', error) - return [] - } - } - \ No newline at end of file diff --git a/layers/composables/useGetGameDataExternal.ts b/layers/composables/useGetGameDataExternal.ts index 8f3cd5a..9d79789 100644 --- a/layers/composables/useGetGameDataExternal.ts +++ b/layers/composables/useGetGameDataExternal.ts @@ -15,8 +15,8 @@ export const useGetGameDataExternal = () => { console.log('🚀 ~ getGameDataExternal ~ req:', req) // const config = useRuntimeConfig() const config = useRuntimeConfig() - const stoveApiUrl = `${config.public.stoveApiUrl}` - const apiUrl = `${stoveApiUrl}/pub-comm/v1.0/template/game?game_domain=${req.gameDomain}&lang_code=${req.langCode}` + const stoveApiBaseUrl = config.public.stoveApiUrl + const apiUrl = `${stoveApiBaseUrl}/pub-comm/v1.0/template/game?game_domain=${req.gameDomain}&lang_code=${req.langCode}` try { const response = (await commonFetch('GET', apiUrl)) as GameDataResponse diff --git a/layers/composables/useTemplateRegistry.ts b/layers/composables/useTemplateRegistry.ts index ed91448..39d787b 100644 --- a/layers/composables/useTemplateRegistry.ts +++ b/layers/composables/useTemplateRegistry.ts @@ -11,6 +11,7 @@ import GrBoard01 from '#layers/templates/GrBoard01/index.vue' import GrContents01 from '#layers/templates/GrContents01/index.vue' import FxVideo01 from '#layers/templates/FxVideo01/index.vue' import FxDownload01 from '#layers/templates/FxDownload01/index.vue' +import FxSecure01 from '#layers/templates/FxSecure01/index.vue' import FxPreregist01 from '#layers/templates/FxPreregist01/index.vue' const templateRegistry = { @@ -27,6 +28,7 @@ const templateRegistry = { GR_CONTENTS_01: { component: GrContents01 }, FX_VIDEO_01: { component: FxVideo01 }, FX_DOWNLOAD_01: { component: FxDownload01 }, + FX_SECURE_01: { component: FxSecure01 }, FX_PREREGIST_01: { component: FxPreregist01 }, } as const diff --git a/layers/composables/useTokenValidation.ts b/layers/composables/useTokenValidation.ts index ba07dad..c0db1b8 100644 --- a/layers/composables/useTokenValidation.ts +++ b/layers/composables/useTokenValidation.ts @@ -26,6 +26,7 @@ export const useTokenValidation = () => { const showLoginModal = (alertKey: string) => { modalStore.handleOpenConfirm({ contentText: tm(alertKey), + isShowDimmed: true, confirmButtonText: tm('Text_StoveLogin'), confirmButtonEvent: () => { csrGoStoveLogin() diff --git a/layers/middleware/inspection.ts b/layers/middleware/inspection.ts index 21e6936..36ccf8d 100644 --- a/layers/middleware/inspection.ts +++ b/layers/middleware/inspection.ts @@ -7,8 +7,8 @@ export default defineNuxtRouteMiddleware(async (to) => { const config = useRuntimeConfig() // const baseDomain = `${config.public.baseDomain}` - const stoveApiUrl = `${config.public.stoveApiUrl}` - const stoveGameId = `${gameData.value.game_id}` + const stoveApiBaseUrl = config.public.stoveApiUrl + const stoveGameId = gameData.value.game_id // const stoveMaintenanceApiUrl = `${config.public.stoveMaintenanceApiUrl}` // const localeCookie = useCookie('LOCALE', { @@ -20,7 +20,7 @@ export default defineNuxtRouteMiddleware(async (to) => { // 웹 점검 ----- const { isWebInspection, getInspectionDataExternal } = useGetInspectionDataExternal() - await getInspectionDataExternal({ baseApiUrl: stoveApiUrl, gameId: stoveGameId }) + await getInspectionDataExternal({ baseApiUrl: stoveApiBaseUrl, gameId: stoveGameId }) // 게임 점검 ----- diff --git a/layers/middleware/pageData.global.ts b/layers/middleware/pageData.global.ts index 3c4cb17..bcb6979 100644 --- a/layers/middleware/pageData.global.ts +++ b/layers/middleware/pageData.global.ts @@ -3,17 +3,25 @@ import { usePageDataStore } from '#layers/stores/usePageDataStore' import { useGetGameDomain } from '#layers/composables/useGetGameDomain' import { usePathResolver } from '#layers/composables/usePathResolver' import type { PageDataResponse } from '#layers/types/api/pageData' +import type { + GameDataValue, +} from '#layers/types/api/gameData' export default defineNuxtRouteMiddleware(async (to, _from) => { + if (!import.meta.client) return + const config = useRuntimeConfig() + const stoveApiBaseUrl = config.public.stoveApiUrl + const apiUrl = `${stoveApiBaseUrl}/pub-comm/v2.0/template/page` + const store = usePageDataStore() const gameDomain = useGetGameDomain() const { getPathAfterLanguage } = usePathResolver() const headers = useRequestHeaders() - const langCode = ssrGetFinalLocale(to.path, headers) + const gameDataStore = useGameDataStore() + const gameData = gameDataStore.gameData as GameDataValue - const stoveApiBaseUrl = config.public.stoveApiUrl - const apiUrl = `${stoveApiBaseUrl}/pub-comm/v2.0/template/page` + const langCode = ssrGetFinalLocale(to.path, headers, gameData?.lang_codes, gameData?.default_lang_code) try { if (to.path.includes('inspection')) { diff --git a/layers/server/middleware/gameData.ts b/layers/server/middleware/gameData.ts index 87a96be..47d2399 100644 --- a/layers/server/middleware/gameData.ts +++ b/layers/server/middleware/gameData.ts @@ -3,7 +3,6 @@ import { getHeader, getRequestHost, defineEventHandler, - getRequestURL, } from 'h3' import { ssrGetFinalLocale } from '../../utils/localeUtil' import type { GameDataResponse } from '../../types/api/gameData' @@ -123,10 +122,7 @@ function fnLocaleMiddleware(event: any, finalLocale: string) { export default defineEventHandler(async event => { - const config = useRuntimeConfig() // const runType = `${config.public.runType}` - const iBaseApiUrl = `${config.public.stoveApiUrlServer}` - const baseDomain = `${config.public.baseDomain}` // console.log("🚀 ~ baseDomain:", config.public.baseDomain) // const url = getRequestURL(event) @@ -147,6 +143,45 @@ export default defineEventHandler(async event => { // } // } + const config = useRuntimeConfig() + const iBaseApiUrl = `${config.public.stoveApiUrlServer}` + const baseDomain = `${config.public.baseDomain}` + const stoveApiUrlBaseServer = config.public.stoveApiUrlServer + const apiUrl = `${stoveApiUrlBaseServer}/pub-comm/v1.0/template/game` + + const initGameData: GameDataResponse | null = null + let initLangCodes: string[] | null = null + let finalLocale + let cleanHost + let initDefaultLocale + + const host = + (getHeader(event, 'host') || getRequestHost(event)).toString() || '' + const isGameDomainExtractable = host.includes(baseDomain) + + if (isGameDomainExtractable) { + cleanHost = host.split(':')[0] + event.context.gameDomain = cleanHost + } + + try { + + const queryParams: Record = { + game_domain: cleanHost || '', + lang_code: '', + } + const initResponse = (await $fetch(apiUrl, { + query: queryParams, + })) as GameDataResponse | null + // initGameData = initResponse || null + // console.log("🚀 ~ 00000 initGameData:", initGameData) + initLangCodes = initResponse?.value?.lang_codes || null + initDefaultLocale = initResponse?.value?.default_lang_code || null + console.log("🚀 ~ 000111 initLangCodes:", initLangCodes) + } catch (error) { + console.error('init gameData load error:', error) + } + const fullPath = event.path // 1-1. 정적 파일 패스 @@ -157,8 +192,8 @@ export default defineEventHandler(async event => { // 1-2. /inspection 패스 if (fullPath.includes('/inspection')) { // 리턴 되기 전 언어 쿠키 세팅 - // const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) - // setFinalLocaleCookie(event, finalLocale, baseDomain) + finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers, initLangCodes, initDefaultLocale) + setFinalLocaleCookie(event, finalLocale, baseDomain) return } @@ -178,30 +213,16 @@ export default defineEventHandler(async event => { const cacheKey = 'inspection' // console.log("🚀 11111 ~ cacheKey:", cacheKey) - const host = - (getHeader(event, 'host') || getRequestHost(event)).toString() || '' - const isGameDomainExtractable = host.includes(baseDomain) - - if (isGameDomainExtractable) { - const cleanHost = host.split(':')[0] - event.context.gameDomain = cleanHost - } // gameData를 직접 가져와서 context에 저장 (API 호출 없이) try { - const config = useRuntimeConfig() - const stoveApiUrlServer = config.public.stoveApiUrlServer - const apiUrl = `${stoveApiUrlServer}/pub-comm/v1.0/template/game` - // console.log("🚀 ~ apiUrl:", apiUrl) - // 2. 언어 코드 추출 - // const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) - - const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) + finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers, initLangCodes, initDefaultLocale) + console.log("🚀 222 finalLocale:", finalLocale) const queryParams: Record = { - game_domain: event.context.gameDomain || '', + game_domain: cleanHost || '', lang_code: finalLocale, } const response = (await $fetch(apiUrl, { @@ -209,10 +230,9 @@ export default defineEventHandler(async event => { })) as GameDataResponse | null // 언어패스 쿠키 굽기 - 장기방안에서는 굽지않음 - // const langCoverages = response?.value?.lang_codes || [] - // if(langCoverages.includes(finalLocale)) { - // setFinalLocaleCookie(event, finalLocale, baseDomain) - // } + if(initLangCodes?.includes(finalLocale)) { + setFinalLocaleCookie(event, finalLocale, baseDomain) + } diff --git a/layers/templates/FxSecure01/index.vue b/layers/templates/FxSecure01/index.vue new file mode 100644 index 0000000..9e6d6fd --- /dev/null +++ b/layers/templates/FxSecure01/index.vue @@ -0,0 +1,272 @@ + + + \ No newline at end of file diff --git a/layers/utils/localeUtil.ts b/layers/utils/localeUtil.ts index b51ee32..33117ff 100644 --- a/layers/utils/localeUtil.ts +++ b/layers/utils/localeUtil.ts @@ -38,6 +38,11 @@ export const csrGetFinalLocale = (path = '', coveragesLocales: string[]) => { let finalLocale = DEFAULT_LOCALE_CODE // 기본값 설정 + // coveragesLocales가 빈 배열이거나 유효하지 않은 경우 기본 언어 반환 + if (!coveragesLocales || !Array.isArray(coveragesLocales) || coveragesLocales.length === 0) { + return finalLocale + } + // 1. URL 패스에 포함된 언어 if (path && path !== '' && path.split('/').length > 1) { // 쿼리스트링 제거한 순수 path 검사 @@ -80,9 +85,14 @@ export const csrGetFinalLocale = (path = '', coveragesLocales: string[]) => { * @param {string} path - 현재 URL 경로 * @param {any} headers - 요청 헤더 */ -export const ssrGetFinalLocale = (path = '', headers: any) => { - let finalLocale = DEFAULT_LOCALE_CODE // 기본값 설정 +export const ssrGetFinalLocale = (path = '', headers: any, coveragesLocales: string[], defaultLocale: string) => { + let finalLocale = defaultLocale || DEFAULT_LOCALE_CODE // 기본값 설정 try { + // coveragesLocales가 빈 배열이거나 유효하지 않은 경우 기본 언어 반환 + if (!coveragesLocales || !Array.isArray(coveragesLocales) || coveragesLocales.length === 0) { + return finalLocale + } + // 1. URL path에 포함된 언어 정보 if (path && path !== '' && path.split('/').length > 1) { // 쿼리스트링 제거한 순수 path 검사 @@ -91,7 +101,7 @@ export const ssrGetFinalLocale = (path = '', headers: any) => { } const pathLocalee = `${path.split('/')[1]}`.toLowerCase() // URL path에 포함된 언어 정보가 지원하는 언어인지 체크 - if (pathLocalee && pathLocalee !== '') { + if (pathLocalee && pathLocalee !== '' && coveragesLocales.includes(pathLocalee)) { finalLocale = pathLocalee return finalLocale } @@ -101,7 +111,7 @@ export const ssrGetFinalLocale = (path = '', headers: any) => { const cookieHeader = headers.cookie || '' const cookies = parseCookies(cookieHeader) const cookieLanguage = cookies.LOCALE ? `${cookies.LOCALE}`.toLowerCase() : '' - if (cookieLanguage && cookieLanguage !== '') { + if (cookieLanguage && cookieLanguage !== '' && coveragesLocales.includes(cookieLanguage)) { finalLocale = cookieLanguage return finalLocale } @@ -116,21 +126,21 @@ export const ssrGetFinalLocale = (path = '', headers: any) => { const preferredLocale = getPreferredLanguage(acceptLanguage) if (preferredLocale) { // 선호 언어의 기본 코드와 일치하는 지원 로케일 찾기 - // const matchedLocale = coveragesLocales.find((locale: string) => - // preferredLocale.toLowerCase().startsWith(locale.toLowerCase()) - // ) - // if (matchedLocale) { - // finalLocale = matchedLocale.toLowerCase() - // return finalLocale - // } + const matchedLocale = coveragesLocales.find((locale: string) => + preferredLocale.toLowerCase().startsWith(locale.toLowerCase()) + ) + if (matchedLocale) { + finalLocale = matchedLocale.toLowerCase() + return finalLocale + } } } } // 3. 서비스 기본 언어 - finalLocale = DEFAULT_LOCALE_CODE - } catch (e) { - finalLocale = DEFAULT_LOCALE_CODE + finalLocale = defaultLocale + } catch { + finalLocale = defaultLocale } return finalLocale } diff --git a/public/images/common/img_OTP.png b/public/images/common/img_OTP.png new file mode 100644 index 0000000..22ef70b Binary files /dev/null and b/public/images/common/img_OTP.png differ diff --git a/public/images/common/logo_smilegate.png b/public/images/common/logo_smilegate.png new file mode 100644 index 0000000..2a408a4 Binary files /dev/null and b/public/images/common/logo_smilegate.png differ diff --git a/temp/middleware.ts b/temp/middleware.ts deleted file mode 100644 index f8ca820..0000000 --- a/temp/middleware.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { LRUCache } from 'lru-cache' -// import { DEFAULT_LOCALE_COVERAGES } from '@/i18n.config' -import { getTrueClientIp } from '#layers/utils/apiUtil' -import { ssrGetFinalLocale } from '#layers/utils/localeUtil' -import type { ResGetInspectionData, WebInspectionData } from '#layers/types/InspectionType' -import { isStaticFile } from '#layers/utils/commonUtil' - - -console.log("🚀 ~ setCacheHeaders ~ event.node.res.setHeader:") -/** - * 캐시 제어 헤더를 설정하는 공통 함수 - * - * @param event - 이벤트 객체 - * @param cacheMode - 캐시 모드 설정 ('no-cache', 'short', 'medium', 'default') - * @param customMaxAge - 커스텀 max-age 값 (초 단위) - */ -function setCacheHeaders( - event: { node: { res: { setHeader: (name: string, value: string) => void } } }, - cacheMode: 'no-cache' | 'short' | 'medium' | 'default', - customMaxAge?: number -): void { - // 원래 setHeader 함수 참조 저장 - const originalSetHeader = event.node.res.setHeader - - // Cache-Control 헤더 설정값 결정 - let cacheControl: string - switch (cacheMode) { - case 'no-cache': - cacheControl = 'no-cache, no-store, must-revalidate' - // no-cache 모드일 때는 추가 헤더도 설정 - event.node.res.setHeader('Pragma', 'no-cache') - event.node.res.setHeader('Expires', '0') - break - case 'short': - cacheControl = `public, max-age=${customMaxAge || 10}` - break - case 'medium': - cacheControl = `public, max-age=${customMaxAge || 15}` - break - case 'default': - default: - cacheControl = `public, max-age=${customMaxAge || 60}` - break - } - - // Cache-Control 헤더를 강제로 설정하기 위해 setHeader 메소드 오버라이드 - event.node.res.setHeader = function (name: string, value: string) { - if (name.toLowerCase() === 'cache-control') { - return originalSetHeader.call(this, name, cacheControl) - } - return originalSetHeader.call(this, name, value) - } - - // 바로 캐시 제어 헤더 적용 -} - -const cache = new LRUCache({ - max: 100, // 캐시에 저장할 최대 항목 수 - ttl: 1000 * 30 // 30초 동안 캐시 유지 -}) - - -/** - * 최종 언어 쿠키 세팅 - * - * @param event - 이벤트 객체 - * @param finalLocale - 최종 언어 - * @param baseDomain - 기본 도메인 - */ -function setFinalLocaleCookie(event: any, finalLocale: string, baseDomain: string) { - setCookie(event, 'LOCALE', finalLocale.toUpperCase(), { - domain: baseDomain, - path: '/', - maxAge: 60 * 60 * 24 * 365 // 1년 (초 단위) - }) -} - -/** - * Locale Middleware 역할 함수 - * - * @param event - 이벤트 객체 - * @param finalLocale - 최종 언어 - */ -function fnLocaleMiddleware(event: any, finalLocale: string) { - const path = event?.node.req.url || '' - let arrPath = [] - let queryString = '' - - if (path.includes('?')) { - // 쿼리스트링 포함 시 순수 경로만 추출 - arrPath = path.split('?')[0].split('/') - queryString = path.split('?')[1] - } else { - arrPath = path.split('/') - queryString = '' - } - - // 최종 언어 세팅된 경로 생성 - const pathLocale = arrPath.length > 1 ? arrPath[1] : '' - - // URL에서 현재 언어와 최종 언어가 다르면 리다이렉트 - if (pathLocale !== finalLocale) { - let newLocalePath = '' - if (pathLocale === '') { - newLocalePath = `/${finalLocale}` - } else { - arrPath[1] = finalLocale - newLocalePath = arrPath.join('/') - } - - if (queryString !== '') { - newLocalePath += `?${queryString}` - } - - event.node.res.statusCode = 302 - event.node.res.setHeader('Location', newLocalePath) - event.node.res.end() - } -} - -export default defineEventHandler(async (event) => { - const config = useRuntimeConfig() - const runType = `${config.public.runType}` - const iBaseApiUrl = `${config.public.stoveApiUrlServer}` - const gameId = `${event.context.gameData?.game_id}` - const baseDomain = `${config.public.baseDomain}` - - if (['local', 'local-gate8', 'dev'].includes(runType)) { - // Sandbox 이상 환경에서만 동작 및 확인 가능 (local, dev는 통과 처리) - try { - // 언어 코드 추출 - const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) - setFinalLocaleCookie(event, finalLocale, baseDomain) - - // ------------------------------------------------------------------------------- - // [Locale Middleware] - // ------------------------------------------------------------------------------- - fnLocaleMiddleware(event, finalLocale) - } catch (e) { - console.error('[Exception] /server/middleware/middleware-global: ', e) - } - } else { - // ------------------------------------------------------------------------------- - // [Inspection Middleware] - // ------------------------------------------------------------------------------- - const fullPath = event.path - - // 1-1. 정적 파일 패스 - if (isStaticFile(event.path)) { - return - } - - // 1-2. /inspection 패스 - if (fullPath.includes('/inspection')) { - // 리턴 되기 전 언어 쿠키 세팅 - const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) - setFinalLocaleCookie(event, finalLocale, baseDomain) - return - } - - // 1-3. 특정 경로 패스 (API, 리소스) - if ( - fullPath.startsWith('/api/') || - fullPath.startsWith('/_nuxt/') || - fullPath.includes('/assets/') || - fullPath.includes('favicon') - ) { - return - } - - // 캐시 키 생성 - const cacheKey = 'inspection' - - try { - // 2. 언어 코드 추출 - const finalLocale = ssrGetFinalLocale(event?.node.req.url, event.node.req.headers) - setFinalLocaleCookie(event, finalLocale, baseDomain) - - // 초기화 - let inspectionData - - // 3. 캐시된 데이터가 없거나 만료되었을 때만 API 호출 - if (cache.has(cacheKey)) { - inspectionData = cache.get(cacheKey) as WebInspectionData - } else { - const apiUrl = `${iBaseApiUrl}/pub-comm/v3.0/inspection/${gameId}` - // 직접 $fetch 사용 (composable 사용하지 않음) - const response = await $fetch(apiUrl, { - method: 'GET', - headers: { - 'Content-Type': 'application/json' - } - }) - inspectionData = response?.value?.inspection as WebInspectionData - console.log("🚀 00000 inspectionData:", inspectionData) - cache.set(cacheKey, inspectionData) // 캐시에 저장 - } - - // 4. 현재 시간과 점검 기간 비교 - const currentTime = Date.now() - const tsStartDate = inspectionData?.ts_start_date || 0 - const tsEndDate = inspectionData?.ts_end_date || 0 - const timeUntilInspectionSeconds = Math.floor((tsStartDate - currentTime) / 1000) - - // 5. 점검 상태별 캐시 설정 - if (inspectionData?.inspection_status === 1 && currentTime >= tsStartDate && currentTime <= tsEndDate) { - /** - * 점검 중인 경우 - * - 점검 상태가 1이고 현재 시간이 점검 시작과 종료 사이에 있는지 확인 - * - 점검 URL 경로가 아닐 경우 no-cache 설정 - * - 화이트 리스트 체크 - */ - // 점검 url path 가 아닐 경우, no-cache 설정 - const inspectionPath = `/${finalLocale}/inspection` - if (fullPath !== inspectionPath) { - setCacheHeaders(event, 'no-cache') - } - - // 점검 중일 때 IP 필터링 활성화 여부 확인 - if (inspectionData?.ip_filter_use_yn === 'Y') { - const clientIP = getTrueClientIp(event.node.req as any) - - // 허용된 IP 목록 확인 - if (!inspectionData?.ip_filter_list?.includes(clientIP)) { - // 허용되지 않은 IP인 경우 점검 페이지로 이동 - event.node.res.statusCode = 302 - event.node.res.setHeader('Location', inspectionPath) - event.node.res.end() - } else { - // 화이트 리스트인 경우 - // ------------------------------------------------------------------------------- - // [Locale Middleware] - // ------------------------------------------------------------------------------- - fnLocaleMiddleware(event, finalLocale) - } - } else { - event.node.res.statusCode = 302 - event.node.res.setHeader('Location', inspectionPath) - event.node.res.end() - } - } else { - /** - * 점검이 아닌 경우 - * - 홈 경로는 no-cache - * - 점검 예정 시간에 따른 캐시 설정 - * - 점검 5분 전: 짧은 캐시 (10초) - * - 점검 30분 전: 중간 캐시 (15초) - * - 점검 30분 이후: 기본 캐시 (60초) - */ - // 홈 경로: 캐시 없음 - const isHomePath = [ - '', - '/' - //, ...Object.values(DEFAULT_LOCALE_COVERAGES).flatMap((locale) => [`/${locale}`, `/${locale}/`]) - ].includes(fullPath) - - if (isHomePath) { - setCacheHeaders(event, 'no-cache') - } else { - // 점검 예정 시간에 따른 캐시 설정 - - if (tsStartDate > 0 && timeUntilInspectionSeconds > 0) { - if (timeUntilInspectionSeconds < 300) { - // 점검 5분 전: 짧은 캐시 (10초) - setCacheHeaders(event, 'short', 10) - } else if (timeUntilInspectionSeconds < 1800) { - // 점검 30분 전: 중간 캐시 (15초) - setCacheHeaders(event, 'medium', 15) - } else { - // 점검 30분 이후: 기본 캐시 (60초) - setCacheHeaders(event, 'default') - } - } - } - // ------------------------------------------------------------------------------- - // [Locale Middleware] - // ------------------------------------------------------------------------------- - fnLocaleMiddleware(event, finalLocale) - } - - // 정상 접속 허용 - } catch (e) { - console.error('[Exception] /server/middleware/middleware-02-global: ', e) - } - } -})