From c8cd7f780ee3602e8b8584ae52fec2fae30ec8f1 Mon Sep 17 00:00:00 2001 From: clkim Date: Fri, 16 Jan 2026 14:52:14 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat.=20[SWV-807]=20=EA=B2=8C=EC=9E=84=20?= =?UTF-8?q?=ED=8F=B0=ED=8A=B8=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/app.vue | 138 ++++++++++++++------- layers/components/blocks/VisualContent.vue | 20 ++- layers/stores/useGameDataStore.ts | 2 + layers/types/api/gameData.ts | 4 +- layers/types/api/pageData.ts | 2 + 5 files changed, 113 insertions(+), 53 deletions(-) diff --git a/app/app.vue b/app/app.vue index ed8813b..a0578f1 100644 --- a/app/app.vue +++ b/app/app.vue @@ -3,87 +3,129 @@ import { useNuxtApp } from 'nuxt/app' import type { GameDataMetaTag, GameDataValue, + GameDataKeyColors, GameDataImg, } from '#layers/types/api/gameData' const nuxtApp = useNuxtApp() const { locale } = useI18n() - const gameDataStore = useGameDataStore() const modalStore = useModalStore() const scrollStore = useScrollStore() const { setGameData } = gameDataStore -const { gameName, gaCode } = storeToRefs(gameDataStore) const { confirm, alert } = modalStore +const { gameName, gaCode } = storeToRefs(gameDataStore) const { scrollGnbPosition } = storeToRefs(scrollStore) -// 통합 메타데이터 설정 -const setupAllMetaData = (data: GameDataValue) => { - const meta = data.meta_tag_json ?? ({} as GameDataMetaTag) - const faviconPath = data.favicon_json ?? ({} as GameDataImg) - const theme = data.design_theme === 1 ? 'light' : 'dark' +// favicon 링크 생성 헬퍼 +const createStyleLinks = (faviconJson: GameDataImg, fontPath: string = '') => { + const links = [] + const iconUrl = faviconJson[0] + const appleTouchIconUrl = faviconJson[1] + const pngIconUrl = faviconJson[2] - // 파비콘 링크 생성 - const faviconLinks = [ - { + if (iconUrl) { + links.push({ rel: 'icon', type: 'image/x-icon', - href: formatPathHost(faviconPath[0]), - }, - { + href: formatPathHost(iconUrl), + }) + } + if (appleTouchIconUrl) { + links.push({ rel: 'apple-touch-icon', - href: formatPathHost(faviconPath[1]), - }, - { + href: formatPathHost(appleTouchIconUrl), + }) + } + if (pngIconUrl) { + links.push({ rel: 'icon', type: 'image/png', - href: formatPathHost(faviconPath[2]), + href: formatPathHost(pngIconUrl), + }) + } + if (fontPath) { + links.push({ + rel: 'stylesheet', + href: formatPathHost(fontPath), + }) + } + + return links +} + +// 메타 태그 생성 헬퍼 +const createMetaTags = (metaTag: Partial = {}) => { + const metaList = [ + { name: 'description', content: metaTag.page_desc }, + { property: 'og:title', content: metaTag.og_title }, + { property: 'og:description', content: metaTag.og_desc }, + { + property: 'og:image', + content: formatPathHost(metaTag.og_image), + }, + { name: 'twitter:title', content: metaTag.x_title }, + { name: 'twitter:description', content: metaTag.x_desc }, + { + name: 'twitter:image', + content: formatPathHost(metaTag.x_image), }, ] - // 색상 CSS 변수 생성 - const cssColorVariables = Object.entries(data.key_color_json ?? {}) + // content가 유효한 메타 태그만 필터링 + return metaList.filter( + meta => meta.content && String(meta.content).trim() !== '' + ) +} + +// CSS 변수 생성 헬퍼 +const createCssVariable = (keyColorJson: GameDataKeyColors) => { + const colorVariables = Object.entries(keyColorJson) + .filter(([key, value]) => key && value != null) .map(([key, value]) => `--${key}: ${value};`) .join('\n ') - const cssContent = ` - :root { - ${cssColorVariables} - } - ` + return `:root {${colorVariables}}` +} - useHead({ - title: meta?.page_title ?? '', - meta: [ - { name: 'description', content: meta.page_desc }, - { property: 'og:title', content: meta.og_title }, - { property: 'og:description', content: meta.og_desc }, - { property: 'og:image', content: formatPathHost(meta.og_image) }, - { name: 'twitter:title', content: meta.x_title }, - { name: 'twitter:description', content: meta.x_desc }, - { name: 'twitter:image', content: formatPathHost(meta.x_image) }, - ], - htmlAttrs: { - 'data-game': data.game_name || '', - 'data-theme': theme, - lang: locale.value ?? data.default_lang_code, - }, - link: faviconLinks, - style: [ - { - innerHTML: cssContent, - id: 'game-css-variables', +// 게임 헤드 설정 +const setupGameHead = (data: GameDataValue) => { + try { + const metaTag: Partial = data.meta_tag_json ?? {} + const designTheme = data.design_theme === 1 ? 'light' : 'dark' + const styleLinks = createStyleLinks( + data.favicon_json + // data?.game_font?.font_path + ) + + useHead({ + title: metaTag.page_title ?? '', + meta: createMetaTags(metaTag), + htmlAttrs: { + 'data-game': data.game_name ?? '', + 'data-theme': designTheme, + lang: locale.value ?? data.default_lang_code ?? 'ko', }, - ], - }) + link: styleLinks, + style: [ + { + innerHTML: createCssVariable(data.key_color_json), + id: 'game-css-variables', + }, + ], + }) + } catch (error) { + // eslint-disable-next-line no-console + console.error('[setupGameHead] Failed to setup game head:', error) + } } if (import.meta.server) { const gameData = nuxtApp.ssrContext?.event?.context?.gameData if (gameData) { setGameData(gameData) - setupAllMetaData(gameData) + setupGameHead(gameData) } } diff --git a/layers/components/blocks/VisualContent.vue b/layers/components/blocks/VisualContent.vue index a63d871..42dcc44 100644 --- a/layers/components/blocks/VisualContent.vue +++ b/layers/components/blocks/VisualContent.vue @@ -12,16 +12,32 @@ const props = withDefaults(defineProps(), { objectFit: 'contain', }) +const gameDataStore = useGameDataStore() +const { fontFamily } = storeToRefs(gameDataStore) + const imagePaths = computed(() => getResourceSrc(props.resourcesData)) const displayText = computed(() => props.resourcesData?.display?.text) const displayColor = computed(() => getColorCodeFromData(props.resourcesData?.display, 'none') ) -// HTML 콘텐츠 정리 (줄바꿈 처리) +// HTML 콘텐츠 줄바꿈 처리 const sanitizedContent = computed(() => { return displayText.value?.replace(/\n/g, '
') || '' }) + +// 텍스트 스타일 +const textStyle = computed(() => { + const style: Record = { + color: displayColor.value, + } + + if (props.resourcesData?.display?.use_game_font === 1) { + style.fontFamily = fontFamily.value + } + + return style +}) diff --git a/layers/stores/useGameDataStore.ts b/layers/stores/useGameDataStore.ts index d63c5fa..e2c7a84 100644 --- a/layers/stores/useGameDataStore.ts +++ b/layers/stores/useGameDataStore.ts @@ -17,6 +17,7 @@ export const useGameDataStore = defineStore('gameData', () => { urlJson: null as GameDataValue['url_json'] | null, gnb: null as GameDataValue['gnb'] | null, eventBanner: null as GameDataValue['event_banner'] | null, + fontFamily: null as GameDataValue['game_font']['font_family'] | null, }) const state = reactive(getInitialState()) @@ -37,6 +38,7 @@ export const useGameDataStore = defineStore('gameData', () => { state.urlJson = data?.url_json state.gnb = data?.gnb state.eventBanner = data?.event_banner + state.fontFamily = data?.game_font?.font_family } const clearGameData = () => { diff --git a/layers/types/api/gameData.ts b/layers/types/api/gameData.ts index 5f9d1f5..93ba96e 100644 --- a/layers/types/api/gameData.ts +++ b/layers/types/api/gameData.ts @@ -29,7 +29,6 @@ export interface GameDataValue { design_theme: number lang_codes: string[] key_color_json: GameDataKeyColors - use_game_font: boolean comm_sns_bg_color_json: { display: ColorObject } @@ -73,8 +72,7 @@ export interface GameDataKeyColors { // 게임 폰트 타입 export interface GameDataGameFont { font_family: string - font_weight: string - font_style: string + font_path: string } // 파비콘 경로 타입 diff --git a/layers/types/api/pageData.ts b/layers/types/api/pageData.ts index 77c7145..65fc9c9 100644 --- a/layers/types/api/pageData.ts +++ b/layers/types/api/pageData.ts @@ -87,11 +87,13 @@ export interface PageDataResourceGroupBtnInfo extends ColorObject { disabled: boolean txt_btn_name: string detail: Record + use_game_font: 0 | 1 } // 리소스 그룹 타입 export interface PageDataResourceGroupDisplay extends ColorObject { text: string + use_game_font: 0 | 1 } export interface PageDataResourceGroup { From 40dc7f0e409841a51946ac4f00aecbdc67be0a9e Mon Sep 17 00:00:00 2001 From: clkim Date: Fri, 16 Jan 2026 14:55:02 +0900 Subject: [PATCH 2/2] =?UTF-8?q?fix.=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/app.vue | 9 +- app/pages/inspection/index.vue | 294 ++++-------------- layers/assets/css/components/_button.css | 8 +- .../atoms/Button/{Variant.vue => Modal.vue} | 12 +- layers/components/atoms/Button/index.vue | 37 ++- layers/components/atoms/Input.vue | 4 +- layers/components/atoms/Select.vue | 2 +- .../components/atoms/icons/PlayRoundFill.vue | 6 +- layers/components/blocks/Button/Launcher.vue | 82 ++--- .../Arrows.vue => Button/SlideArrows.vue} | 0 layers/components/blocks/DatePicker.vue | 2 +- layers/components/blocks/Pagination.vue | 2 +- layers/components/blocks/StoveGnb.vue | 19 +- layers/components/blocks/modal/Alert.vue | 4 +- layers/components/blocks/modal/Confirm.vue | 8 +- layers/components/layouts/Header.vue | 21 +- layers/components/widgets/ButtonList.vue | 8 +- layers/components/widgets/modal/Client.vue | 4 +- layers/components/widgets/modal/Preregist.vue | 2 +- .../components/widgets/slide/CenterFocus.vue | 5 +- .../widgets/slide/CenterHighlight.vue | 5 +- layers/components/widgets/slide/Default.vue | 5 +- layers/components/widgets/slide/Fade.vue | 2 +- layers/components/widgets/slide/Thumbnail.vue | 5 +- layers/middleware/init.route.global.ts | 2 +- layers/middleware/pageData.global.ts | 2 +- layers/server/middleware/gameData.ts | 6 +- layers/stores/useGameDataStore.ts | 8 + layers/templates/FxCoupon01/index.vue | 18 +- layers/templates/FxDownload01/index.vue | 8 +- layers/templates/FxSecure01/index.vue | 4 +- layers/templates/FxVideo01/index.vue | 8 +- layers/templates/GrContents01/index.vue | 1 + layers/types/components/button.ts | 4 +- 34 files changed, 238 insertions(+), 369 deletions(-) rename layers/components/atoms/Button/{Variant.vue => Modal.vue} (78%) rename layers/components/blocks/{slide/Arrows.vue => Button/SlideArrows.vue} (100%) diff --git a/app/app.vue b/app/app.vue index a0578f1..18f86a5 100644 --- a/app/app.vue +++ b/app/app.vue @@ -80,7 +80,7 @@ const createMetaTags = (metaTag: Partial = {}) => { } // CSS 변수 생성 헬퍼 -const createCssVariable = (keyColorJson: GameDataKeyColors) => { +const createStyleCss = (keyColorJson: GameDataKeyColors) => { const colorVariables = Object.entries(keyColorJson) .filter(([key, value]) => key && value != null) .map(([key, value]) => `--${key}: ${value};`) @@ -95,9 +95,10 @@ const setupGameHead = (data: GameDataValue) => { const metaTag: Partial = data.meta_tag_json ?? {} const designTheme = data.design_theme === 1 ? 'light' : 'dark' const styleLinks = createStyleLinks( - data.favicon_json - // data?.game_font?.font_path + data.favicon_json, + data?.game_font?.font_path ) + const styleCss = createStyleCss(data.key_color_json) useHead({ title: metaTag.page_title ?? '', @@ -110,7 +111,7 @@ const setupGameHead = (data: GameDataValue) => { link: styleLinks, style: [ { - innerHTML: createCssVariable(data.key_color_json), + innerHTML: styleCss, id: 'game-css-variables', }, ], diff --git a/app/pages/inspection/index.vue b/app/pages/inspection/index.vue index 4c55e25..86d570b 100644 --- a/app/pages/inspection/index.vue +++ b/app/pages/inspection/index.vue @@ -56,80 +56,24 @@ >
- - - - {{ tm('Inspection_Community_Btn') }} + {{ tm('Inspection_Community_Btn') }} - - -
- - {{ tm('Txt_Game_Start') }} - - - - - - - - {{ tm('Txt_Game_Start') }} - - - - - -
+ + + {{ tm('Txt_Game_Start') }} +
@@ -137,7 +81,6 @@

{{ @@ -147,18 +90,23 @@ }}

- - {{ getButtonText(btn.platform) }} - + + {{ getButtonText(market) }} + +
@@ -170,6 +118,8 @@ diff --git a/layers/components/blocks/slide/Arrows.vue b/layers/components/blocks/Button/SlideArrows.vue similarity index 100% rename from layers/components/blocks/slide/Arrows.vue rename to layers/components/blocks/Button/SlideArrows.vue diff --git a/layers/components/blocks/DatePicker.vue b/layers/components/blocks/DatePicker.vue index d68dc50..28de42d 100644 --- a/layers/components/blocks/DatePicker.vue +++ b/layers/components/blocks/DatePicker.vue @@ -365,7 +365,7 @@ onMounted(() => { >