fix. 임시 데이터 맞춰 수정

This commit is contained in:
clkim
2025-09-09 22:18:10 +09:00
parent fcd4dc799b
commit 56a1a50312
18 changed files with 492 additions and 259 deletions

View File

@@ -8,6 +8,10 @@
</NuxtLayout>
</template>
<style>
@import "#layers/assets/css/theme.css";
</style>
<script setup lang="ts">
import { useNuxtApp } from "nuxt/app";
import { useGameDataStore } from "#layers/stores/useGameDataStore";
@@ -32,9 +36,10 @@ if (gameDataFromServer) {
setGameData(gameDataFromServer);
}
const meta = gameDataFromServer?.meta_tag || null;
const meta = gameDataFromServer?.meta_tag ?? null;
const theme = gameDataFromServer?.design_theme === 1 ? "dark" : "light";
if (meta) {
if (gameDataFromServer && meta) {
metaData.value = meta;
useSeoMeta({
title: meta.page_title,
@@ -48,9 +53,11 @@ if (meta) {
twitterDescription: meta.x_desc,
});
useHead({
// htmlAttrs: {
// lang: locale.value
// },
htmlAttrs: {
"data-game": gameDataFromServer.game_name || "",
"data-theme": theme || "",
lang: gameDataFromServer.default_lang_code,
},
//meta: [...(updatedMetaTags.value || [])],
//link: [...(links.value || [])]
});

View File

@@ -0,0 +1,49 @@
/* 라이트 테마 기본 색상 */
:root {
--foreground: #ffffff;
--foreground-10: #ffffff;
--foreground-reversal: #1f1f1f;
--foreground-reversal-10: rgba(0, 0, 0, 0.1);
--foreground-reversal-30: #ebebeb; /* gray-80 */
--foreground-reversal-40: rgba(0, 0, 0, 0.4);
--foreground-reversal-70: #666666; /* gray-700 */
--primary: #3b82f6;
--secondary: #64748b;
--surface: #f8fafc;
--textSecondary: #475569;
--accent: #f59e0b;
--border: #e2e8f0;
}
/* 다크 테마 색상 */
[data-theme="dark"] {
--foreground: #191919;
--foreground-10: #292929;
--foreground-reversal: #ebebeb;
--foreground-reversal-10: rgba(255, 255, 255, 0.1);
--foreground-reversal-30: #404040; /* gray-750 */
--foreground-reversal-40: rgba(255, 255, 255, 0.4);
--foreground-reversal-70: #b2b2b2; /* gray-300 */
--primary: #60a5fa;
--secondary: #94a3b8;
--surface: #1e293b;
--textSecondary: #cbd5e1;
--accent: #fbbf24;
--border: #475569;
}
/* Gradient 텍스트 스타일 */
.text-gradient-pink {
background: linear-gradient(270deg, #e872ff 0%, #ff357e 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
color: transparent;
}

View File

@@ -0,0 +1,15 @@
<script setup lang="ts">
const props = defineProps({
to: {
type: String,
default: null,
required: true,
},
});
const localePath = useLocalePath();
</script>
<template>
<NuxtLink :to="localePath(props.to)"><slot></slot></NuxtLink>
</template>

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
interface Props {
size?: number | string;
color?: string;
className?: string;
}
withDefaults(defineProps<Props>(), {
size: 12,
color: "#7F7F7F",
className: "",
});
</script>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
:width="size"
:height="size"
viewBox="0 0 12 12"
:fill="color"
>
<path
d="M5.29499 7.715L2.39999 4.875C2.07499 4.555 2.29999 4 2.75999 4L9.23499 4C9.69499 4 9.91999 4.555 9.59499 4.875L6.69999 7.715C6.30999 8.095 5.68999 8.095 5.29999 7.715H5.29499Z"
fill="#7F7F7F"
/>
</svg>
</template>

View File

@@ -0,0 +1,32 @@
<script setup lang="ts">
interface Props {
size?: number | string;
color?: string;
className?: string;
}
withDefaults(defineProps<Props>(), {
size: 16,
color: "#B2B2B2",
className: "",
});
</script>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
:width="size"
:height="size"
viewBox="0 0 16 16"
:fill="color"
>
<path
d="M3.63636 3.33333C3.469 3.33333 3.33333 3.469 3.33333 3.63636L3.33333 12.3636C3.33333 12.531 3.469 12.6667 3.63636 12.6667H12.3636C12.531 12.6667 12.6667 12.531 12.6667 12.3636V9.93939C12.6667 9.5712 12.9651 9.27273 13.3333 9.27273C13.7015 9.27273 14 9.5712 14 9.93939V12.3636C14 13.2674 13.2674 14 12.3636 14H3.63636C2.73262 14 2 13.2674 2 12.3636L2 3.63636C2 2.73263 2.73262 2 3.63636 2L6.06061 2C6.4288 2 6.72727 2.29848 6.72727 2.66667C6.72727 3.03486 6.4288 3.33333 6.06061 3.33333H3.63636Z"
fill="#B2B2B2"
/>
<path
d="M12.6667 4.27614V6.54545C12.6667 6.91364 12.9651 7.21212 13.3333 7.21212C13.7015 7.21212 14 6.91364 14 6.54545V2.66667C14 2.29848 13.7015 2 13.3333 2L9.45455 2C9.08636 2 8.78788 2.29848 8.78788 2.66667C8.78788 3.03486 9.08636 3.33333 9.45455 3.33333L11.7239 3.33333L7.28616 7.77103C7.02581 8.03138 7.02581 8.45349 7.28616 8.71384C7.54651 8.97419 7.96862 8.97419 8.22897 8.71384L12.6667 4.27614Z"
fill="#B2B2B2"
/>
</svg>
</template>

View File

@@ -0,0 +1,29 @@
<script setup lang="ts">
interface Props {
size?: number | string;
color?: string;
className?: string;
}
withDefaults(defineProps<Props>(), {
size: 12,
color: "#FD3886",
className: "",
});
</script>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
:width="size"
:height="size"
viewBox="0 0 12 12"
fill="none"
:class="className"
>
<path
d="M6.37364 3.24966C6.22658 2.91678 5.77344 2.91678 5.62638 3.24966L4.95508 4.76916L3.36352 4.96618C3.01484 5.00934 2.87482 5.4593 3.1326 5.7082L4.30928 6.84431L3.99693 8.48558C3.9285 8.84514 4.2951 9.12323 4.60148 8.94417L6.00001 8.12684L7.39853 8.94417C7.70491 9.12323 8.07151 8.84514 8.00308 8.48558L7.69074 6.84431L8.8674 5.70819C9.12518 5.4593 8.98515 5.00934 8.63648 4.96618L7.04492 4.76916L6.37364 3.24966Z"
:fill="color"
/>
</svg>
</template>

View File

@@ -1,5 +1,5 @@
<template>
<div id="header-stove"></div>
<div id="header-stove" class="relative z-[5]"></div>
</template>
<script setup lang="ts">

View File

@@ -1,14 +1,20 @@
<script setup lang="ts">
import type { GameDataValue, GameDataGnb } from "#layers/types/api/gameData";
const gameDataStore = useGameDataStore();
const gameData = computed(() => gameDataStore.gameData);
const gameData = gameDataStore.gameData as GameDataValue;
const gnbList = gameData?.gnb?.menus as GameDataGnb["menus"];
</script>
<template>
<header class="bg-black text-white relative z-50">
<header
class="bg-theme-foreground text-theme-foreground-reversal relative z-50"
>
<LayoutStoveGnb />
<div class="px-[40px] h-16 flex items-center">
<div data-name="header-game" class="px-[40px] h-16 flex items-center">
<!-- 로고 -->
<div class="mr-[40px]">
<div data-name="header-logo" class="mr-[40px]">
<img
:src="gameData?.gnb?.bi_path"
:alt="gameData?.game_name"
@@ -17,126 +23,64 @@ const gameData = computed(() => gameDataStore.gameData);
</div>
<!-- 메인 네비게이션 -->
<nav class="flex items-center space-x-[32px]">
<!-- 동적 메뉴 (비활성화) -->
<template v-if="false">
<nav data-name="header-nav" class="flex items-center space-x-[32px]">
<template v-if="gnbList">
<div
v-for="menuList in gameData?.gnb?.menus"
:key="menuList.path_code"
v-for="(gnbItem, key) in gnbList"
:key="key"
class="relative group"
>
<a
v-if="menuList.depth === 1"
:href="menuList.url_path"
:target="menuList.link_target"
class="hover:text-yellow-400 transition-colors"
<!-- Link 컴포넌트 사용 -->
<MoleculesLink
:to="gnbItem.url_path"
:target="gnbItem.link_target"
class="relative flex items-center h-[64px]"
>
{{ menuList.menu_name }}
</a>
{{ gnbItem.menu_name }}
<AtomsIconsArrowDown v-if="gnbItem.children" class="ml-1" />
<span
class="absolute bottom-0 left-0 w-full h-2 border-b-2 border-transparent transition-border-color group-hover:border-theme-foreground-reversal group-active:border-theme-foreground-reversal-10"
></span>
</MoleculesLink>
<div
v-if="gnbItem.children"
class="absolute top-full left-[-28px] min-w-[190px] pt-[4px]"
>
<ul
class="bg-theme-foreground-10 rounded-[20px] shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50 p-3"
>
<li v-for="child in gnbItem.children" :key="child.menu_name">
<!-- Link 컴포넌트 사용 -->
<MoleculesLink
:to="child.url_path"
:target="child.link_target"
class="flex items-center px-4 py-[9px] rounded-[12px] transition-background hover:bg-theme-foreground-reversal-40 active:bg-theme-foreground-reversal-70"
>
{{ child.menu_name }}
<AtomsIconsLinkOut
v-if="child.link_target === '_blank'"
class="ml-1"
/>
</MoleculesLink>
</li>
</ul>
</div>
</div>
</template>
<!-- 고객지원 (2단계 드롭다운) -->
<div class="relative group">
<button
class="flex items-center space-x-1 hover:text-yellow-400 transition-colors"
>
<span>고객지원</span>
<svg
class="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
></path>
</svg>
</button>
<!-- 2단계 드롭다운 메뉴 -->
<div
class="absolute top-full left-0 mt-2 w-48 bg-gray-800 rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"
>
<div class="py-2">
<a
href="#"
class="block px-4 py-2 hover:bg-gray-700 hover:text-yellow-400 transition-colors"
>다운로드</a
>
<a
href="#"
class="block px-4 py-2 hover:bg-gray-700 hover:text-yellow-400 transition-colors bg-gray-700 text-yellow-400"
>쿠폰 등록</a
>
<a
href="#"
class="flex items-center justify-between px-4 py-2 hover:bg-gray-700 hover:text-yellow-400 transition-colors"
>
<span>고객센터</span>
<svg
class="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
></path>
</svg>
</a>
<a
href="#"
class="flex items-center justify-between px-4 py-2 hover:bg-gray-700 hover:text-yellow-400 transition-colors"
>
<span>확률 정보</span>
<svg
class="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
></path>
</svg>
</a>
<a
href="#"
class="block px-4 py-2 hover:bg-gray-700 hover:text-yellow-400 transition-colors"
>보안 강화 캠페인</a
>
</div>
</div>
</div>
<!-- 기부 -->
<a href="#" class="hover:text-yellow-400 transition-colors">기부</a>
<!-- 구분선 -->
<div class="w-px h-6 bg-gray-600"></div>
<div class="w-px h-4 bg-theme-foreground-reversal-30"></div>
<!-- 이벤트 -->
<a
href="#"
class="flex items-center space-x-1 text-pink-400 hover:text-pink-300 transition-colors"
>
<span class="text-pink-400"></span>
<a href="#" class="flex items-center space-x-[3px] text-gradient-pink">
<AtomsIconsStar />
<span>이벤트</span>
<span class="text-pink-400"></span>
<AtomsIconsStar />
</a>
</nav>
<!-- 게임 시작 버튼 (2단계 드롭다운) -->
<div class="relative group ml-auto">
<!-- 오른쪽 영역 -->
<div data-name="header-right" class="relative group ml-auto">
<button
class="bg-amber-600 hover:bg-amber-700 text-white px-6 py-3 rounded-lg font-medium transition-colors"
>
@@ -145,12 +89,12 @@ const gameData = computed(() => gameDataStore.gameData);
<!-- 2단계 드롭다운 메뉴 -->
<div
class="absolute top-full right-0 mt-2 w-64 bg-gray-800 rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50"
class="absolute top-full right-0 mt-2 w-64 bg-gray-800 rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50 p-3"
>
<div class="py-2">
<div class="px-4 py-2">
<a
href="#"
class="flex items-center space-x-3 px-4 py-3 hover:bg-gray-700 transition-colors"
class="flex items-center space-x-3 hover:bg-gray-700 transition-colors"
>
<div
class="w-8 h-8 bg-orange-500 rounded flex items-center justify-center"
@@ -159,9 +103,11 @@ const gameData = computed(() => gameDataStore.gameData);
</div>
<span>PC 버전 다운로드</span>
</a>
</div>
<div class="px-4 py-2">
<a
href="#"
class="flex items-center space-x-3 px-4 py-3 hover:bg-gray-700 transition-colors"
class="flex items-center space-x-3 hover:bg-gray-700 transition-colors"
>
<svg class="w-8 h-8" viewBox="0 0 24 24" fill="currentColor">
<path
@@ -170,9 +116,11 @@ const gameData = computed(() => gameDataStore.gameData);
</svg>
<span>Google Store</span>
</a>
</div>
<div class="px-4 py-2">
<a
href="#"
class="flex items-center space-x-3 px-4 py-3 hover:bg-gray-700 transition-colors"
class="flex items-center space-x-3 hover:bg-gray-700 transition-colors"
>
<svg class="w-8 h-8" viewBox="0 0 24 24" fill="currentColor">
<path

View File

@@ -0,0 +1,27 @@
<script setup lang="ts">
interface Props {
to: string;
target?: string;
class?: string;
}
const props = withDefaults(defineProps<Props>(), {
target: "",
class: "",
});
</script>
<template>
<a
v-if="props.target === '_blank'"
v-bind="$attrs"
:href="props.to"
:target="props.target"
:class="props.class"
>
<slot />
</a>
<AtomsLocaleLink v-else v-bind="$attrs" :to="props.to" :class="props.class">
<slot />
</AtomsLocaleLink>
</template>

View File

@@ -1,8 +1,5 @@
<script setup lang="ts">
import {
getResponsiveResolvedSrc,
getResponsiveClass,
} from "#layers/utils/dataUtil";
import { getResponsiveSrc, getResponsiveClass } from "#layers/utils/dataUtil";
import type { PageDataComponent } from "#layers/types/api/pageData";
const props = defineProps<{ componentData: PageDataComponent }>();
@@ -10,10 +7,15 @@ const props = defineProps<{ componentData: PageDataComponent }>();
const assetType = computed(() => {
return props.componentData?.component_type;
});
const assetPath = computed(() => {
// [TODO] 이미지 그룹 여러개 처리
// [TODO] comm 처리
return props.componentData?.resources[0]["groups"][0]?.img_path?.comm;
const resources = computed(() => {
return props.componentData?.resources;
});
const imageSrc = getResponsiveSrc({
resources: resources.value,
type: "bg",
test: "test2",
});
</script>
@@ -21,17 +23,17 @@ const assetPath = computed(() => {
<div class="absolute inset-0 w-full h-full">
<!-- 이미지 타입-->
<div
v-if="assetType === 'IMG'"
class="absolute inset-0 w-full h-full bg-cover bg-center bg-no-repeat"
v-if="assetType === 'IMG' && imageSrc"
class="w-full h-full bg-cover bg-center bg-no-repeat"
:class="getResponsiveClass()"
:style="getResponsiveResolvedSrc(assetPath, 'bg')"
:style="imageSrc"
></div>
<!-- 비디오 타입 -->
<video
<!-- <video
v-else-if="assetType === 'VIDEO'"
class="absolute inset-0 w-full h-full object-cover"
:poster="assetPoster"
:poster="getResponsiveSrc({ resources: resources, type: 'img' })"
autoplay
muted
loop
@@ -39,7 +41,7 @@ const assetPath = computed(() => {
>
<source :src="assetPath" type="video/mp4" />
<source :src="assetPath" type="video/webm" />
</video>
</video> -->
<div
class="absolute inset-0"
style="

View File

@@ -1,38 +1,33 @@
<script setup lang="ts">
import { getResponsiveResolvedSrc } from "#layers/utils/dataUtil";
import { getResponsiveSrc, getDisplayText } from "#layers/utils/dataUtil";
import type { PageDataComponent } from "#layers/types/api/pageData";
const props = defineProps<{ componentData: PageDataComponent }>();
const displayText = computed(() => {
return (
props.componentData?.resources[0]?.["groups"]?.[0]?.display?.ko?.text || ""
);
const resources = computed(() => {
return props.componentData?.resources;
});
const assetPath = computed(() => {
// [TODO] 이미지 그룹 여러개 처리
// [TODO] 언어 처리
return props.componentData?.resources[0]?.["groups"]?.[0]?.img_path?.ko;
const displayText = getDisplayText({
resources: resources.value,
});
const imageSrcs = computed(() => {
if (!assetPath.value?.path_mo || !assetPath.value?.path_pc) {
return { mobileSrc: "", pcSrc: "" };
}
return getResponsiveResolvedSrc(assetPath.value);
const imageSrc = getResponsiveSrc({
resources: resources.value,
type: "img",
});
</script>
<template>
<div>
<img
:src="imageSrcs.mobileSrc"
:src="imageSrc?.mobileSrc"
:alt="displayText"
class="w-full sm:hidden"
/>
<!-- PC 이미지 (sm 이상) -->
<img
:src="imageSrcs.pcSrc"
:src="imageSrc?.pcSrc"
:alt="displayText"
class="w-full hidden sm:block"
/>

View File

@@ -1,38 +1,33 @@
<script setup lang="ts">
import { getResponsiveResolvedSrc } from "#layers/utils/dataUtil";
import { getResponsiveSrc, getDisplayText } from "#layers/utils/dataUtil";
import type { PageDataComponent } from "#layers/types/api/pageData";
const props = defineProps<{ componentData: PageDataComponent }>();
const displayText = computed(() => {
return (
props.componentData?.resources[0]?.["groups"]?.[0]?.display?.ko?.text || ""
);
const resources = computed(() => {
return props.componentData?.resources;
});
const assetPath = computed(() => {
// [TODO] 이미지 그룹 여러개 처리
// [TODO] 언어 처리
return props.componentData?.resources[0]?.["groups"]?.[0]?.img_path?.ko;
const displayText = getDisplayText({
resources: resources.value,
});
const imageSrcs = computed(() => {
if (!assetPath.value?.path_mo || !assetPath.value?.path_pc) {
return { mobileSrc: "", pcSrc: "" };
}
return getResponsiveResolvedSrc(assetPath.value);
const imageSrc = getResponsiveSrc({
resources: resources.value,
type: "img",
});
</script>
<template>
<h2>
<img
:src="imageSrcs.mobileSrc"
:src="imageSrc?.mobileSrc"
:alt="displayText"
class="w-full sm:hidden"
/>
<!-- PC 이미지 (sm 이상) -->
<img
:src="imageSrcs.pcSrc"
:src="imageSrc?.pcSrc"
:alt="displayText"
class="w-full hidden sm:block"
/>

View File

@@ -1,26 +1,26 @@
<script setup lang="ts">
import {
getResponsiveResolvedSrc,
getResponsiveClass,
} from "#layers/utils/dataUtil";
import { getResponsiveSrc, getResponsiveClass } from "#layers/utils/dataUtil";
import type { PageDataComponent } from "#layers/types/api/pageData";
const props = defineProps<{ componentData: PageDataComponent }>();
const assetPath = computed(() => {
// [TODO] 이미지 그룹 여러개 처리
// [TODO] comm 처리
return props.componentData?.resources[0]["groups"][0]?.img_path?.comm;
const resources = computed(() => {
return props.componentData?.resources;
});
console.log("assetPath:", props.componentData);
const imageSrc = getResponsiveSrc({
resources: resources.value,
type: "bg",
test: "test",
});
</script>
<template>
<button
class="bg-cover bg-center bg-no-repeat w-[66px] h-[66px] lg:w-[100px] lg:h-[100px]"
:class="getResponsiveClass()"
:style="getResponsiveResolvedSrc(assetPath, 'bg')"
:style="imageSrc"
>
<span class="sr-only">videoPlay</span>
</button>

View File

@@ -1,52 +0,0 @@
import { commonFetch } from "#layers/utils/apiUtil";
import { getHeader } from "h3";
import type {
GameDataResponse,
GameDataValue,
} from "#layers/types/api/gameData";
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig();
const baseDomain = (config.public.baseDomain || ".onstove.com") as string;
const stoveApiBaseUrl = config.public.stoveApiUrl;
const apiUrl = `${stoveApiBaseUrl}/pub-comm/v1.0/template/game`;
let gameAlias = "";
try {
// 미들웨어에서 설정한 gameAlias가 있다면 우선 사용
if (event.context.gameAlias) {
gameAlias = event.context.gameAlias;
} else {
const host = getHeader(event, "host") || "";
const isGameAliasExtractable = host.includes(baseDomain);
if (isGameAliasExtractable) {
const subdomain = host.split(".")[0];
if (subdomain && subdomain !== "www") {
gameAlias = subdomain;
}
}
}
} catch (error) {
console.log("gameAlias extraction error: ", error);
}
try {
const queryParams: Record<string, string> = {
game_alias: gameAlias,
lang_code: "ko",
};
const response = (await commonFetch("GET", apiUrl, {
query: queryParams,
})) as GameDataResponse | null;
if (response?.code === 0 && "value" in response) {
event.context.gameData = response.value;
return response.value as GameDataValue;
}
} catch (error) {
console.error(error);
return {};
}
});

View File

@@ -18,31 +18,30 @@ export interface GameDataValue {
game_id: string;
game_code: number;
s3_folder_name: string;
default_lang_code: string;
game_name: string;
ga_code: string;
favicon_path: string;
design_theme: number;
key_color_codes: string[];
lang_codes: string[];
lang_codes: string; // JSON 문자열로 변경
key_color_codes: string; // JSON 문자열로 변경
use_game_font: boolean;
footer_dev_ci_img_yn: boolean;
game_font: GameDataFont;
global: GameDataGlobal;
footer_dev_ci_img_path: string;
game_font: string; // JSON 문자열로 변경
globals: GameDataGlobal[]; // 배열로 변경
gnb: GameDataGnb;
quick_menus: GameDataQuickMenu[];
stove_gnb: GameDataStoveGnb;
comm_img: GameDataCommImg;
meta_tag: GameDataMetaTag;
youtube: GameDataYoutube;
sns: GameDataSns;
market: GameDataMarket;
footer: GameDataFooter;
intro: GameDataIntro;
inspection: Record<string, any>; // 동적 객체
stove_gnb: string; // JSON 문자열로 변경
meta_tag: string; // JSON 문자열로 변경
sns: string; // JSON 문자열로 변경
footer: string; // JSON 문자열로 변경
}
// Global 설정 타입
export interface GameDataGlobal {
system_font: GameDataFont;
lang: GameDataLang;
system_font: string; // JSON 문자열로 변경
lang: string; // JSON 문자열로 변경
}
// 폰트 타입
@@ -61,12 +60,18 @@ export interface GameDataLang {
// GNB 설정 타입
export interface GameDataGnb {
game_gnb_ver: string;
display_start_dt: number;
display_start_dt: string; // ISO 문자열로 변경
theme_type: string;
bi_path: string;
lang_codes: string[];
buttons: any[];
menus: GameDataMenu[];
lang_codes: string; // JSON 문자열로 변경
buttons: GameDataButton[];
menus: Record<string, GameDataMenu>; // 동적 객체로 변경
}
// 버튼 타입
export interface GameDataButton {
depth_type: number;
button: string; // JSON 문자열로 변경
}
// 메뉴 타입
@@ -78,7 +83,16 @@ export interface GameDataMenu {
click_action_type: number;
url_path: string;
link_target: string;
tracking: GameDataTracking;
children: Record<string, GameDataMenu>; // 중첩 메뉴를 위한 children 속성 추가
tracking: string; // JSON 문자열로 변경
}
// 인트로 타입
export interface GameDataIntro {
seq: number;
display_start_dt: string;
display_end_dt: string;
page_url: string;
}
// 트래킹 타입
@@ -108,6 +122,15 @@ export interface GameDataStoveGnb {
stove_install_button_visible: string;
}
// 파비콘 경로 타입
export interface GameDataFaviconPath {
"16_16": string;
"32_32": string;
"72_72": string;
"180_180": string;
"192_192": string;
}
// 공통 이미지 타입
export interface GameDataCommImg {
groups: GameDataCommImgGroup[];
@@ -226,5 +249,69 @@ export interface GameDataApiResult {
error: string | null;
}
// JSON 문자열 파싱을 위한 유틸리티 타입들
export interface ParsedKeyColorCodes {
extra: string;
primary: string;
secondary: string;
"text-primary": string;
"text-secondary": string;
}
export interface ParsedGameFont {
"font-family": string;
}
export interface ParsedGlobal {
system_font: ParsedGameFont;
lang: GameDataLang;
}
export interface ParsedButton {
google_play: {
label: Record<string, string>;
url: string;
tracking: GameDataTracking;
};
app_store: {
label: Record<string, string>;
url: string;
tracking: GameDataTracking;
};
}
export interface ParsedStoveGnb {
skin_type: string;
stove_install_button_visible: string;
}
export interface ParsedSns {
kakao: GameDataSnsItem;
twitter: GameDataSnsItem;
discord: GameDataSnsItem;
youtube: GameDataSnsItem;
instagram: GameDataSnsItem;
facebook: GameDataSnsItem;
tiktok: GameDataSnsItem;
}
export interface ParsedFooter {
use_game_rating: boolean;
game_rating_info: GameDataGameRatingInfo;
use_dev_ci_url: boolean;
dev_ci_url: string;
fund_display_yn: boolean;
fund_display_url: string;
}
// 파비콘 경로 파싱 타입
export interface ParsedFaviconPath {
"16_16": string;
"32_32": string;
"72_72": string;
"180_180": string;
"192_192": string;
}
// 기존 gameData 타입과의 호환성을 위한 별칭
export type gameData = GameDataValue;

View File

@@ -17,24 +17,78 @@ export const getResolvedSrc = (path: string): string => {
return `${rootPath}${path}`;
};
// [TODO] data 타입 정의
export const getDisplayText = ({
resources,
groupsDepth = 0,
}: {
resources: any;
groupsDepth?: number;
}): string => {
const group = resources[0]?.groups?.[groupsDepth];
return group?.display?.text ?? "";
};
// [TODO] data 타입 정의
export const getAssetPath = ({
resources,
groupsDepth = 0,
test,
}: {
resources: any;
groupsDepth?: number;
test?: string;
}): ResponsiveImagePath | null => {
// const group = resources[0]?.groups?.[groupsDepth].resource_data;
// const imgPath = group?.resource_data?.img_path;
const group = resources[groupsDepth];
if (test) {
if (test === "test") {
const tests = {
path_mo: group?.resource_data?.img_path.comm,
};
return tests ?? null;
}
if (test === "test2") {
return group?.resource_data?.img_path.comm ?? null;
}
}
const imgPath = group?.resource_data?.img_path.ko;
return imgPath ?? null;
};
// 반응형 클래스 리턴하는 함수
export const getResponsiveClass = () => {
return ["bg-[image:var(--mobile-bg)]", "sm:bg-[image:var(--pc-bg)]"];
};
// 반응형 이미지 리턴하는 함수
export const getResponsiveResolvedSrc = (
path: ResponsiveImagePath,
type: ImageType = "img"
): ResponsiveImageResult => {
// 필수 경로가 없으면 null 반환
if (!path?.path_mo || !path?.path_pc) {
export const getResponsiveSrc = ({
resources,
type = "img",
groupsDepth = 0,
test,
}: {
resources: any;
type?: ImageType;
groupsDepth?: number;
test?: string;
}): ResponsiveImageResult | ResponsiveImagePath => {
const path = getAssetPath({ resources, groupsDepth, test });
if (!path?.path_mo) {
return null;
}
// 이미지 경로 해석
const resolvedImages = {
pc: getResolvedSrc(path.path_pc),
pc: getResolvedSrc(path.path_pc || path.path_mo),
mobile: getResolvedSrc(path.path_mo),
};

View File

@@ -11,6 +11,23 @@ export default {
lg: "1440px", // Large PC: 1440px+
},
spacing: {},
colors: {
"theme-foreground": "var(--foreground)",
"theme-foreground-10": "var(--foreground-10)",
"theme-foreground-reversal": "var(--foreground-reversal)",
"theme-foreground-reversal-10": "var(--foreground-reversal-10)",
"theme-foreground-reversal-30": "var(--foreground-reversal-30)",
"theme-foreground-reversal-40": "var(--foreground-reversal-40)",
"theme-foreground-reversal-70": "var(--foreground-reversal-70)",
// "theme-primary": "var(--light-primary)",
// "theme-secondary": "var(--light-secondary)",
// "theme-surface": "var(--light-surface)",
// "theme-text-secondary": "var(--light-textSecondary)",
// "theme-accent": "var(--light-accent)",
// "theme-border": "var(--light-border)",
},
},
},
} satisfies Config;

View File

@@ -2,7 +2,7 @@
"extends": "./.nuxt/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"types": ["nuxt", "node"],
"types": ["nuxt", "node", "@nuxtjs/i18n"],
"moduleResolution": "bundler",
"paths": {
"@/*": ["."],