refactor. 게임 데이터 로딩 및 언어 전환 로직 개선

- LanguageSwitcher.vue에서 setLocale 및 gameData 로딩 로직 통합
- useGameDataLoader.ts 추가로 게임 데이터 로딩 기능 구현
- init.route.global.ts에서 초기화 로직 개선 및 중복 호출 방지
- useGameDataStore.ts에 currentLangCode 상태 추가 및 설정 함수 구현

Made-with: Cursor
This commit is contained in:
clkim
2026-03-20 18:07:04 +09:00
parent 2c444a06f3
commit 53aee6963b
4 changed files with 76 additions and 19 deletions

View File

@@ -193,7 +193,7 @@ const getLanguageName = (localeCode: string) => {
const { locale, setLocale } = useI18n()
const switchLocalePath = useSwitchLocalePath()
const pageDataStore = usePageDataStore()
const { loadGameData } = useGameDataLoader()
const selectedLocale = ref(locale.value)
const isChanging = ref(false)
@@ -233,25 +233,16 @@ const switchLanguage = async () => {
})
localeCookie.value = selectedLocale.value.toLowerCase()
window.location.href = path
// 언어 변경 및 라우팅
// await setLocale(selectedLocale.value as any)
// 전체 페이지에 페이드 아웃 효과 적용
// document.body.style.transition = 'opacity 0.1s ease-out'
// document.body.style.opacity = '0'
// // 페이드 아웃 완료 후 페이지 이동
// await new Promise(resolve => setTimeout(resolve, 100))
// 서버 미드웨어를 통해 gameData 갱신을 위해 페이지 새로고침
// 이렇게 하면 서버 미드웨어가 새로운 언어로 gameData를 다시 가져옴
// i18n locale 변경 (SPA)
await setLocale(selectedLocale.value as any)
// gameData가 언어별로 달라지는 영역(Header/GNB 등) 즉시 갱신
await loadGameData(selectedLocale.value)
// Nuxt SPA 라우팅으로 페이지 이동
await navigateTo(path)
}
} catch {
// 오류 발생 시 이전 언어로 복원
selectedLocale.value = locale.value
// 페이드 효과 복원
document.body.style.opacity = '1'
} finally {
isChanging.value = false
}

View File

@@ -0,0 +1,31 @@
import type { GameDataResponse } from '#layers/types/api/gameData'
import { getGameDomain } from '#layers/utils/urlUtil'
export const useGameDataLoader = () => {
const runtimeConfig = useRuntimeConfig()
const gameDataStore = useGameDataStore()
const loadGameData = async (langCode: string) => {
const normalizedLangCode = `${langCode}`.toLowerCase()
const stoveApiBaseUrl = runtimeConfig.public.stoveApiUrl
const gameDomain = getGameDomain()
const response = await $fetch<GameDataResponse>(
`${stoveApiBaseUrl}/pub-comm/v1.0/template/game`,
{
query: {
game_domain: gameDomain || '',
lang_code: normalizedLangCode,
},
}
)
if (response?.code === 0 && 'value' in response) {
gameDataStore.setGameData(response.value)
gameDataStore.setCurrentLangCode(normalizedLangCode)
}
}
return { loadGameData }
}

View File

@@ -2,7 +2,9 @@ import { DEFAULT_LOCALE_CODE, ISO_LANGUAGE_CODES } from '@/i18n.config'
import { csrGetFinalLocale } from '#layers/utils/localeUtil'
import { parseUrl, isExternalUrl } from '#layers/utils/urlUtil'
export default defineNuxtRouteMiddleware(to => {
let pending: Promise<void> | null = null
export default defineNuxtRouteMiddleware(async to => {
// 서버에서는 실행하지 않음 (서버 미들웨어에서 이미 처리)
if (import.meta.server) return
@@ -12,7 +14,8 @@ export default defineNuxtRouteMiddleware(to => {
}
const gameDataStore = useGameDataStore()
const { langCodes, defaultLangCode, intro } = storeToRefs(gameDataStore)
const { langCodes, defaultLangCode, intro, currentLangCode, gameData } =
storeToRefs(gameDataStore)
// 게임 데이터 스토어가 초기화되지 않았으면 대기
if (!langCodes.value || !defaultLangCode.value || !intro.value) {
@@ -39,9 +42,35 @@ export default defineNuxtRouteMiddleware(to => {
// 현재 경로와 최종 경로가 같으면 리다이렉트 불필요
if (fullPath.split('?')[0] === finalPath.split('?')[0]) {
// gameData가 없으면(초기 진입 등) 여기서 강제 호출하지 않음
// - 초기 gameData는 서버 미들웨어에서 주입되는 구조
if (!gameData.value) return
// 초기 hydration에서 currentLangCode가 비어있으면 현재 로케일로 동기화만
if (!currentLangCode.value) {
gameDataStore.setCurrentLangCode(finalLocale)
return
}
// 로케일이 같으면 불필요한 재호출 방지
if (
`${currentLangCode.value}`.toLowerCase() ===
`${finalLocale}`.toLowerCase()
) {
return
}
// 동시에 여러 번 호출되는 것 방지 (라우트 훅/미들웨어 중복 등)
if (pending) return pending
const { loadGameData } = useGameDataLoader()
pending = loadGameData(finalLocale).finally(() => {
pending = null
})
return pending
}
// 외부 URL인지 확인
const isExternal = isExternalUrl(finalPath)

View File

@@ -9,6 +9,7 @@ export const useGameDataStore = defineStore('gameData', () => {
stoveGnbJson: null as GameDataValue['stove_gnb_json'] | null,
langCodes: null as GameDataValue['lang_codes'] | null,
defaultLangCode: null as GameDataValue['default_lang_code'] | null,
currentLangCode: null as string | null,
gaCode: null as GameDataValue['ga_code'] | null,
platformType: null as GameDataValue['platform_type'] | null,
osType: null as GameDataValue['os_type'] | null,
@@ -55,6 +56,10 @@ export const useGameDataStore = defineStore('gameData', () => {
state.eventBanner = data?.event_banner
}
const setCurrentLangCode = (langCode: string | null) => {
state.currentLangCode = langCode ? `${langCode}`.toLowerCase() : null
}
const clearGameData = () => {
Object.assign(state, getInitialState())
}
@@ -62,6 +67,7 @@ export const useGameDataStore = defineStore('gameData', () => {
return {
...toRefs(state),
setGameData,
setCurrentLangCode,
clearGameData,
}
})