Merge commit 'ea5f31acffc6466a035d1b2cf41d0ec93d7e8512' into feature/20260130_cl_SWV-811

This commit is contained in:
clkim
2026-01-14 10:29:45 +09:00
18 changed files with 131 additions and 124 deletions

View File

@@ -3,8 +3,8 @@ import { useNuxtApp } from 'nuxt/app'
import type {
GameDataMetaTag,
GameDataValue,
GameDataFavicon,
GameDataKeyColors,
GameDataImg,
} from '#layers/types/api/gameData'
const nuxtApp = useNuxtApp()
@@ -14,12 +14,12 @@ const modalStore = useModalStore()
const scrollStore = useScrollStore()
const { setGameData } = gameDataStore
const { gameData } = storeToRefs(gameDataStore)
const { gameName, gaCode } = storeToRefs(gameDataStore)
const { confirm, alert } = modalStore
const { scrollGnbPosition } = storeToRefs(scrollStore)
// favicon 링크 생성 헬퍼
const createStyleLinks = (faviconJson: GameDataFavicon) => {
const createStyleLinks = (faviconJson: GameDataImg, fontPath: string = '') => {
const links = []
const iconUrl = faviconJson[0]
const appleTouchIconUrl = faviconJson[1]
@@ -45,6 +45,12 @@ const createStyleLinks = (faviconJson: GameDataFavicon) => {
href: formatPathHost(pngIconUrl),
})
}
if (fontPath) {
links.push({
rel: 'stylesheet',
href: formatPathHost(fontPath),
})
}
return links
}
@@ -74,10 +80,7 @@ const createMetaTags = (metaTag: Partial<GameDataMetaTag> = {}) => {
}
// CSS 변수 생성 헬퍼
const createCssVariables = (
keyColorJson: GameDataKeyColors
// commImgJson: GameDataCommImg
) => {
const createCssVariable = (keyColorJson: GameDataKeyColors) => {
const colorVariables = Object.entries(keyColorJson)
.filter(([key, value]) => key && value != null)
.map(([key, value]) => `--${key}: ${value};`)
@@ -92,7 +95,7 @@ const setupGameHead = (data: GameDataValue) => {
const metaTag: Partial<GameDataMetaTag> = data.meta_tag_json ?? {}
const designTheme = data.design_theme === 1 ? 'light' : 'dark'
const styleLinks = createStyleLinks(data.favicon_json)
const cssVariables = createCssVariables(data.key_color_json)
const cssVariables = createCssVariable(data.key_color_json)
useHead({
title: metaTag.page_title ?? '',
@@ -147,7 +150,7 @@ onMounted(() => {
)
const { gtag, initialize } = useGtag()
initialize(gameData.value?.ga_code)
initialize(gaCode.value)
gtag('event', 'screen_view', {
app_name: 'My App',
screen_name: 'Home',
@@ -170,7 +173,7 @@ onBeforeUnmount(() => {
</script>
<template>
<h1 class="sr-only">{{ gameData?.game_name }}</h1>
<h1 class="sr-only">{{ gameName }}</h1>
<NuxtPage />
<!-- 공통 모달 컴포넌트 -->

View File

@@ -96,8 +96,7 @@ const errorTitle = ref('')
const errorMsg = ref('')
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const gameName = computed(() => gameData.value?.game_name)
const { gameName } = storeToRefs(gameDataStore)
//error clear 함수 생성
const localePath = useLocalePath()

View File

@@ -8,7 +8,7 @@ const gameDataStore = useGameDataStore()
const modalStore = useModalStore()
const { sendLog } = useAnalytics()
const { gameData } = storeToRefs(gameDataStore)
const { gameData, snsJson } = storeToRefs(gameDataStore)
const { handleOpenToast } = modalStore
const analytics = {
@@ -22,9 +22,6 @@ const snsBackgroundColor = computed(() => {
const colorCode = getColorCodeFromData(colorData, 'none')
return colorCode
})
const snsList = computed(() => {
return gameData.value?.sns_json
})
const handleControlForce = (state: boolean) => {
showSnsList.value = state
@@ -45,7 +42,7 @@ const handleCopy = async () => {
</script>
<template>
<div v-if="Object.keys(snsList).length > 0" class="sns-container">
<div v-if="Object.keys(snsJson).length > 0" class="sns-container">
<transition name="fade">
<AtomsButtonCircle
v-show="!showSnsList"
@@ -63,7 +60,7 @@ const handleCopy = async () => {
class="sns-list"
:style="{ backgroundColor: snsBackgroundColor }"
>
<template v-for="(item, key) in snsList" :key="key">
<template v-for="(item, key) in snsJson" :key="key">
<a
v-if="item.use_yn === 1 && item.url"
:href="item.url"

View File

@@ -20,7 +20,7 @@ const scrollStore = useScrollStore()
const breakpoints = useResponsiveBreakpoints()
const { sendLog } = useAnalytics()
const { gameData } = storeToRefs(gameDataStore)
const { gameName, imgJson, gnb, eventBanner } = storeToRefs(gameDataStore)
const { pageLayoutType } = storeToRefs(pageDataStore)
const { isPassedStoveGnb } = storeToRefs(scrollStore)
@@ -35,18 +35,18 @@ const navWidth = ref(0)
const officialItemWidths = ref<number[]>([])
const overflowCount = ref<number>(0)
const gnbData = computed(() => gameData.value?.gnb)
const biSmallPath = computed(() => imgJson.value?.bi_small)
const gnbMenusCount = computed(() => {
const menus = gnbData.value?.menus
const menus = gnb.value?.menus
if (!menus || typeof menus !== 'object') return 0
return Object.keys(menus).length
})
const currentPath = computed(() => formatPathWithoutLocale(route.path))
const start1depthData = computed(
() => gnbData.value?.buttons[0]?.button_json as GameDataResourceGroup
() => gnb.value?.buttons[0]?.button_json as GameDataResourceGroup
)
const start2depthData = computed(
() => gnbData.value?.buttons[1]?.button_json as GameDataResourceGroupSet
() => gnb.value?.buttons[1]?.button_json as GameDataResourceGroupSet
)
// 자식 중 활성 링크 존재 여부 확인
@@ -60,7 +60,7 @@ const hasActiveChild = (children?: GameDataMenuChildren) => {
// navAreaRef의 넓이를 구하는 함수
const calculateNavWidth = () => {
if (!import.meta.client) return
if (!navAreaRef.value || !gnbData.value) return 0
if (!navAreaRef.value || !gnb.value) return 0
const navAreaWidth = navAreaRef.value.offsetWidth
navWidth.value = navAreaWidth + MORE_WIDTH
@@ -179,20 +179,17 @@ onMounted(() => {
</script>
<template>
<header :class="['header', { 'empty-game': !gnbData }]">
<header :class="['header', { 'empty-game': !gnb }]">
<BlocksStoveGnb class="h-[48px]" />
<div
v-if="gnbData"
:class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]"
>
<div v-if="gnb" :class="['game-wrap', { 'is-fixed': isPassedStoveGnb }]">
<AtomsLocaleLink
to="/home"
class="mx-auto md:hidden"
@click="handleSendLog('BI')"
>
<img
:src="formatPathHost(gnbData?.bi_path)"
:alt="gameData?.game_name"
:src="formatPathHost(biSmallPath)"
:alt="gameName"
class="h-[30px]"
/>
</AtomsLocaleLink>
@@ -211,8 +208,8 @@ onMounted(() => {
@click="[handleMenuClose(), handleSendLog('BI')]"
>
<img
:src="formatPathHost(gnbData?.bi_path)"
:alt="gameData?.game_name"
:src="formatPathHost(biSmallPath)"
:alt="gameName"
class="h-[30px]"
/>
</AtomsLocaleLink>
@@ -221,7 +218,7 @@ onMounted(() => {
<template v-if="gnbMenusCount > 0">
<div class="official custom-theme-scrollbar">
<div
v-for="(gnbItem, key) in gnbData?.menus"
v-for="(gnbItem, key) in gnb?.menus"
:key="key"
class="nav-item group"
:class="{
@@ -291,7 +288,7 @@ onMounted(() => {
<div class="more-list">
<div class="list-inner">
<div
v-for="(gnbItem, key) in gnbData?.menus"
v-for="(gnbItem, key) in gnb?.menus"
:key="key"
:class="{
'is-hidden':
@@ -349,15 +346,11 @@ onMounted(() => {
</div>
</template>
<ClientOnly>
<div v-if="gameData?.event_banner" class="event">
<div v-if="eventBanner" class="event">
<div class="nav-item">
<AtomsLocaleLink
:to="gameData?.event_banner?.page_url"
:target="
gameData?.event_banner?.link_type === 1
? '_self'
: '_blank'
"
:to="eventBanner?.page_url"
:target="eventBanner?.link_type === 1 ? '_self' : '_blank'"
:class="[
'nav-1depth',
{ 'router-link-active': pageLayoutType === 'promotion' },

View File

@@ -26,12 +26,12 @@ const {
setPreregist,
} = usePreregist()
const { gameData } = storeToRefs(gameDataStore)
const { gameId, gameName } = storeToRefs(gameDataStore)
const { sendLog } = useAnalytics()
// Constants
const stoveCs = runtimeConfig.public.stoveCs
const customerServiceUrl = `${stoveCs}/${gameData.value?.game_id}`
const customerServiceUrl = `${stoveCs}/${gameId.value}`
/**
* 번역 함수 (Props로 전달받은 tm 또는 key 반환)
@@ -57,8 +57,7 @@ const errorMessages = computed<Record<number, string>>(() => ({
const tmWithGameName = (key: string): string => {
const text = tm(key)
if (typeof text === 'string' && text.includes('%게임명%')) {
const gameName = gameData.value?.game_name ?? ''
return text.replace(/%게임명%/g, gameName)
return text.replace(/%게임명%/g, gameName.value)
}
return text
}
@@ -375,7 +374,7 @@ defineExpose({
<h3
class="text-xl font-bold leading-[30px] tracking-[-0.6px] text-[#ebebeb] md:text-2xl md:leading-[34px] md:tracking-[-0.72px]"
>
{{ gameData?.game_name }}
{{ gameName }}
</h3>
<p
class="text-[13px] font-normal leading-[22px] tracking-[-0.325px] text-[#ebebeb] md:text-[15px] md:leading-6 md:tracking-[-0.45px]"

View File

@@ -100,12 +100,12 @@ const sendSA = (
if (isEmptyAnalytics(analytics)) return
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const { gameCode } = storeToRefs(gameDataStore)
try {
const device = useDevice()
const gameNo = gameData.value?.game_code
const gameNo = gameCode.value
const deviceType = device.isDesktop ? 'pcweb' : 'mobileweb'
const country = `${csrGetCountry()}`
const memberNo = `${csrGetStoveMemberNo()}`

View File

@@ -9,13 +9,13 @@ export const useCheckGameStart = () => {
const { handleTokenValidation } = useTokenValidation()
const { tm, locale } = useI18n()
const { gameData } = storeToRefs(gameDataStore)
const { gameId } = storeToRefs(gameDataStore)
const stoveCs = runtimeConfig.public.stoveCs
const isProcessing = ref(false) // 연속 클릭 방지
const isShowCheckLauncher = ref(false) // 런처 실행 로딩 표시
const isShowDownloadLauncher = ref(false) // 런처 다운로드 표시
const customerServiceUrl = `${stoveCs}/${gameData.value?.game_id}`
const customerServiceUrl = `${stoveCs}/${gameId.value}`
let launcherTimeoutId: ReturnType<typeof setTimeout> | null = null
// 에러 처리
@@ -116,11 +116,8 @@ export const useCheckGameStart = () => {
const runLauncher = async () => {
if (!import.meta.client) return
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const accessTokenSub = csrGetAccessToken()
const stoveGameId = gameData.value?.game_id || ''
const stoveGameId = gameId.value || ''
const nationCookie = useCookie('NNTO').value
const localeCode = locale.value.toUpperCase() || 'KO'

View File

@@ -19,11 +19,11 @@ export const useTokenValidation = () => {
const gameDataStore = useGameDataStore()
const { tm } = useI18n()
const { gameData } = storeToRefs(gameDataStore)
const { gameId } = storeToRefs(gameDataStore)
const apiBaseUrl = runtimeConfig.public.stoveApiUrl
const stoveCs = runtimeConfig.public.stoveCs
const customerServiceUrl = `${stoveCs}/${gameData.value?.game_id}`
const customerServiceUrl = `${stoveCs}/${gameId.value}`
const isTokenValid = ref(false)
// 로그인 모달 표시

View File

@@ -9,22 +9,22 @@ export default defineNuxtRouteMiddleware(to => {
if (to.path.includes('inspection')) return
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const { langCodes, intro } = storeToRefs(gameDataStore)
// app.vue에서 설정한 스토어 값이 없으면 대기
if (!gameData.value) return
if (!langCodes.value || !intro.value) return
// -------------------------------------------------------------------------------
// [Home Redirect]
// -------------------------------------------------------------------------------
const gamePath = getPathAfterLanguage(to.path)
const langCode = csrGetFinalLocale(to.path, gameData.value.lang_codes)
const langCode = csrGetFinalLocale(to.path, langCodes.value)
const isRootPath = gamePath === '' || gamePath === '/'
if (isRootPath) {
// gameData.intro.page_url이 있으면 해당 URL로 리다이렉트, 없으면 /home으로
const introPageUrl = gameData.value?.intro?.page_url
// intro.page_url이 있으면 해당 URL로 리다이렉트, 없으면 /home으로
const introPageUrl = intro.value?.page_url
const redirectPath = getIntroRedirectPath(
introPageUrl,
langCode,

View File

@@ -9,17 +9,17 @@ export default defineNuxtRouteMiddleware(async to => {
try {
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const { gameId, langCodes } = storeToRefs(gameDataStore)
// app.vue에서 설정한 스토어 값이 없으면 대기
if (!gameData.value) return
if (!gameId.value || !langCodes.value) return
const stoveApiBaseUrl = `${runtimeConfig.public.stoveApiUrl}`
// const baseDomain = `${runtimeConfig.public.baseDomain}`
// const stoveMaintenanceApiUrl = `${runtimeConfig.public.stoveMaintenanceApiUrl}`
const stoveGameId = gameData.value.game_id
const stoveGameId = gameId.value
const finalLocale = csrGetFinalLocale(to.path, gameData.value.lang_codes)
const finalLocale = csrGetFinalLocale(to.path, langCodes.value)
// 웹 점검 -----
const { isWebInspection, getInspectionDataExternal } =

View File

@@ -20,14 +20,13 @@ export default defineNuxtRouteMiddleware(async (to, _from) => {
const pageDataStore = usePageDataStore()
const loadingStore = useLoadingStore()
const { gameData } = storeToRefs(gameDataStore)
const { langCodes } = storeToRefs(gameDataStore)
const stoveApiBaseUrl = runtimeConfig.public.stoveApiUrl
const accessToken = csrGetAccessToken()
const gameDomain = getGameDomain()
const gamePath = getPathAfterLanguage(to.path)
const langCode =
csrGetFinalLocale(to.path, gameData.value?.lang_codes) || 'ko'
const langCode = csrGetFinalLocale(to.path, langCodes.value) || 'ko'
let pageDataResponse: PageDataResponse | null = null

View File

@@ -10,10 +10,9 @@ export default defineNuxtPlugin(async nuxtApp => {
const commonTranslations = 'STOVE_PUBTEMPLATE_common_translations.json'
const gameDataStore = useGameDataStore()
const { gameData } = storeToRefs(gameDataStore)
const langCodes = gameData.value?.lang_codes
const { langCodes } = storeToRefs(gameDataStore)
if (!langCodes || langCodes.length === 0) {
if (!langCodes || langCodes.value.length === 0) {
return
}
@@ -31,7 +30,7 @@ export default defineNuxtPlugin(async nuxtApp => {
'zh-cn': 'zh-CN',
}
langCodes.forEach(langCode => {
langCodes.value.forEach(langCode => {
// 로케일 코드 변환 (필요한 경우)
const normalizedLangCode = localeMap[langCode] || langCode

View File

@@ -310,7 +310,7 @@ export default defineEventHandler(async event => {
const isRootPath = gamePath === '' || gamePath === '/'
if (isRootPath) {
// gameData.intro.page_url이 있으면 해당 URL로 리다이렉트, 없으면 /home으로
// intro.page_url이 있으면 해당 URL로 리다이렉트, 없으면 /home으로
const introPageUrl = gameDataValue?.intro?.page_url
const currentFullUrl = event.node.req.url || fullPath
const redirectPath = getIntroRedirectPath(

View File

@@ -1,26 +1,50 @@
import type { GameDataValue } from '#layers/types/api/gameData'
export const useGameDataStore = defineStore('gameData', () => {
const gameData = ref<GameDataValue | null>(null)
const langCodes = ref<string[] | null>(null)
const defaultLangCode = ref<string | null>(null)
const getInitialState = () => ({
gameData: null as GameDataValue | null,
gameId: null as GameDataValue['game_id'] | null,
gameCode: null as GameDataValue['game_code'] | null,
gameName: null as GameDataValue['game_name'] | null,
langCodes: null as GameDataValue['lang_codes'] | null,
defaultLangCode: null as GameDataValue['default_lang_code'] | null,
gaCode: null as GameDataValue['ga_code'] | null,
platformType: null as GameDataValue['platform_type'] | null,
osType: null as GameDataValue['os_type'] | null,
intro: null as GameDataValue['intro'] | null,
imgJson: null as GameDataValue['img_json'] | null,
snsJson: null as GameDataValue['sns_json'] | null,
urlJson: null as GameDataValue['url_json'] | null,
gnb: null as GameDataValue['gnb'] | null,
eventBanner: null as GameDataValue['event_banner'] | null,
})
const state = reactive(getInitialState())
const setGameData = (data: GameDataValue) => {
gameData.value = data
langCodes.value = data.lang_codes
defaultLangCode.value = data.default_lang_code
state.gameData = data
state.gameId = data?.game_id
state.gameCode = data?.game_code
state.gameName = data?.game_name
state.langCodes = data?.lang_codes
state.defaultLangCode = data?.default_lang_code
state.gaCode = data?.ga_code
state.platformType = data?.platform_type
state.osType = data?.os_type
state.intro = data?.intro
state.imgJson = data?.img_json
state.snsJson = data?.sns_json
state.urlJson = data?.url_json
state.gnb = data?.gnb
state.eventBanner = data?.event_banner
}
const clearGameData = () => {
gameData.value = null
langCodes.value = null
defaultLangCode.value = null
Object.assign(state, getInitialState())
}
return {
gameData,
langCodes,
defaultLangCode,
...toRefs(state),
setGameData,
clearGameData,
}

View File

@@ -1,32 +1,29 @@
import type { PageDataValue } from '#layers/types/api/pageData'
export const usePageDataStore = defineStore('pageData', () => {
const pageData = ref<PageDataValue | null>(null)
const pageLayoutType = ref<'default' | 'promotion' | null>(null)
const pageName = ref<string | null>(null)
const pageNameEn = ref<string | null>(null)
// 초기 상태를 함수로 정의
const getInitialState = () => ({
pageData: null as PageDataValue | null,
pageLayoutType: null as 'default' | 'promotion' | null,
pageName: null as PageDataValue['page_name'] | null,
pageNameEn: null as PageDataValue['page_name_en'] | null,
})
const state = reactive(getInitialState())
const setPageData = (response: PageDataValue) => {
clearPageData()
pageData.value = response
pageLayoutType.value = getLayoutType(pageData.value)
pageName.value = pageData.value?.page_name
pageNameEn.value = pageData.value?.page_name_en
state.pageData = response
state.pageLayoutType = getLayoutType(state.pageData)
state.pageName = state.pageData?.page_name
state.pageNameEn = state.pageData?.page_name_en
}
const clearPageData = () => {
pageData.value = null
pageLayoutType.value = null
pageName.value = null
pageNameEn.value = null
Object.assign(state, getInitialState())
}
return {
pageData,
pageLayoutType,
pageName,
pageNameEn,
...toRefs(state),
setPageData,
clearPageData,
}

View File

@@ -68,7 +68,7 @@ const { pageNo, pageSize, updatePagination, getPageBlock } = useCouponPaging()
const gameDataStore = useGameDataStore()
const modalStore = useModalStore()
const couponStore = useCouponStore()
const { gameData } = storeToRefs(gameDataStore)
const { gameId, gameCode } = storeToRefs(gameDataStore)
const { handleOpenAlert, handleOpenConfirm } = modalStore
const { couponNo, isSelectCharacter, selectCharacter } =
storeToRefs(couponStore)
@@ -148,8 +148,8 @@ const validationCheckBefore = async () => {
* await checkGameMaintenance({
* baseApiUrl: stoveMaintenanceApiUrl,
* category: 'GAME',
* service_id1: gameData.value.game_id,
* gameId: gameData.value.game_id,
* service_id1: gameId.value,
* gameId: gameId.value,
* lang: locale.value,
* })
* if (isGameMaintenance.value) {
@@ -176,8 +176,8 @@ const validationCheckBefore = async () => {
* await getCharacterList({
* baseApiUrl: stoveApiUrl,
* accessToken: csrGetAccessToken(),
* game_id: gameData.value.game_id,
* gameId: gameData.value.game_id,
* game_id: gameId.value,
* gameId: gameId.value,
* })
* if (
* !characterList.value.some(
@@ -195,7 +195,7 @@ const validationCheckBefore = async () => {
*/
await getInspectionDataExternal({
baseApiUrl: stoveApiUrl,
gameId: gameData.value.game_id,
gameId: gameId.value,
})
if (isWebInspection.value) {
return COUPON_RESULT.WEB_INSPECTION
@@ -216,8 +216,8 @@ const validationCheckBefore = async () => {
await getGuid({
baseApiUrl: stoveApiUrl,
accessToken: csrGetAccessToken(),
game_id: gameData.value.game_id,
gameId: gameData.value.game_id,
game_id: gameId.value,
gameId: gameId.value,
})
if (!hasGuid.value) {
return COUPON_RESULT.EMPTY_GUID
@@ -311,7 +311,7 @@ const handleCouponRegister = async () => {
const res = await postCouponUse({
accessToken: csrGetAccessToken(),
user_token_type: 'web',
game_code: gameData.value.game_code.toString(),
game_code: gameCode.value.toString(),
coupon_no: couponNo.value,
client_ipaddr: clientIp.value,
world_no: selectCharacter.value.world_id,
@@ -368,7 +368,7 @@ const handlePeriodSearch = async () => {
const req: ReqCouponList = {
accessToken: accessToken,
user_token_type: 'web',
game_code: gameData.value.game_code.toString(),
game_code: gameCode.value.toString(),
start_date: getTime(startDate.value),
end_date: getTime(endDate.value),
use_state_code: searchStatus.value,
@@ -402,7 +402,7 @@ const getCouponBoxUrl = () => {
url = getStoveCouponUrl('desktop')
}
return `${url}?game_id=${gameData.value.game_id}`
return `${url}?game_id=${gameId.value}`
}
/**
@@ -439,7 +439,7 @@ const goToCouponBox = () => {
url = getStoveCouponUrl('desktop')
}
csrGoExternalLink(`${url}?game_id=${gameData.value.game_id}`)
csrGoExternalLink(`${url}?game_id=${gameId.value}`)
}
/**

View File

@@ -18,9 +18,10 @@ const pageDataStore = usePageDataStore()
const { getCwmsArticle } = useCwmsArticle()
const { locale } = useI18n()
const { gameData } = storeToRefs(gameDataStore)
const { urlJson } = storeToRefs(gameDataStore)
const { pageData } = storeToRefs(pageDataStore)
const communityUrl = computed(() => urlJson.value?.community)
const boardId = computed(
() => getComponentGroup(props.components, 'boardId')?.display?.text
)
@@ -101,11 +102,10 @@ const splideOptions = computed(() => {
})
const getArticleUrl = (articleId: string) => {
const communityUrl = gameData.value?.url_json?.community
if (!communityUrl || !articleId || !boardId.value) {
if (!communityUrl.value || !articleId || !boardId.value) {
return ''
}
return `${communityUrl}/view/${articleId}`
return `${communityUrl.value}/view/${articleId}`
}
</script>

View File

@@ -42,11 +42,12 @@ export interface GameDataValue {
intro: GameDataIntro
inspection: Record<string, any>
stove_gnb_json: GameDataStoveGnb
favicon_json: GameDataFavicon
favicon_json: GameDataImg
meta_tag_json: GameDataMetaTag
sns_json: GameDataSns
url_json: Record<string, { url: string }>
footer_json: string // JSON 문자열로 변경
img_json: GameDataImg
market_json: Record<string, { url: string }>
event_banner: GameDataEventBanner
os_type: OsType
@@ -76,8 +77,8 @@ export interface GameDataGameFont {
}
// 파비콘 경로 타입
export interface GameDataFavicon {
[index: number]: string
export interface GameDataImg {
[key: string]: string
}
// 메타 태그 타입
@@ -159,7 +160,6 @@ export interface GameDataMenu {
// GNB 설정 타입
export interface GameDataGnb {
game_gnb_ver: string
bi_path: string
lang_codes: string // JSON 문자열로 변경
buttons: GameDataButton[]
menus: GameDataMenuChildren