-
๋น์ ์ ์ํ ๋ค์ ํ ๋ชฐ์
์ฅ์น
-
๋จ์ํ ํ์ด๋จธ๊ฐ ์๋๋๋ค. ๋ฌด๋ฆฌํ์ง ์๊ณ ์ค๋ ์ง์ํ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํฉ๋๋ค.
+
{landing.features.title}
+
{landing.features.description}
- {[
- { icon: "โณ", title: "๊ตฌ์กฐํ๋ ์ธ์
ํ์ด๋จธ", desc: "๋ถ๋ด ์์ด ์์ํ ์ ์๋ ์งง์ ๋ชฐ์
๊ณผ ํ์คํ ํด์. ๋น์ ๋ง์ ์์
๋ฆฌ๋ฌ์ ๋ถ๋๋ฝ๊ฒ ์ค์ ํ๊ณ ๊ด๋ฆฌํ์ธ์." },
- { icon: "๐ฑ", title: "๋ค์ ํ ์ฐ๋์ ์ฝ์ํน", desc: "ํ๋ฉด ๋๋จธ ๋๊ตฐ๊ฐ์ ํจ๊ปํ๋ ๋ฐ๋ ๋๋ธ๋ง ํจ๊ณผ. ๊ฐ์๊ฐ ์๋, ์กฐ์ฉํ์ง๋ง ๊ฐ๋ ฅํ ๋๊ธฐ๋ฅผ ์๋ก ๋๋์ด๋ณด์ธ์." },
- { icon: "๐๏ธ", title: "๋๋ง์ ์ฌ๋ฏธ์ ๊ณต๊ฐ", desc: "๋น ์ค๋ ๋ค๋ฝ๋ฐฉ, ํ์ด ๋๋ ์นดํ. ๋ฐฑ์์์๊ณผ ํจ๊ป ๋ด๊ฐ ๊ฐ์ฅ ํธ์ํจ์ ๋๋ผ๋ ๊ฐ์ ๊ณต๊ฐ์ ๊พธ๋ฏธ๊ณ ๋จธ๋ฌด๋ฅด์ธ์." }
- ].map((feature, idx) => (
+ {landing.features.items.map((feature, idx) => (
{feature.icon}
{feature.title}
- {feature.desc}
+ {feature.description}
))}
@@ -104,65 +103,64 @@ export default function MarketingPage() {
-
๋์๊ฒ ๋ง๋ ๊ณต๊ฐ ์ ํํ๊ธฐ
-
๊ฐ์ธ์ ๊ฐ๋ฒผ์ด ์ง์ค๋ถํฐ ํ๋ฆฌ๋์์ ์๋ฒฝํ ์ํฌ์คํ์ด์ค๊น์ง.
+
{landing.pricing.title}
+
{landing.pricing.description}
{/* Starter Plan */}
-
Starter
-
๊ฐ๋ฒผ์ด ์ง์ค์ด ํ์ํ ๋ถ
+
{landing.pricing.plans.starter.name}
+
{landing.pricing.plans.starter.subtitle}
- ๋ฌด๋ฃ
+ {landing.pricing.plans.starter.price}
- - โ ๊ธฐ๋ณธ ๊ฐ์ ๊ณต๊ฐ ํ
๋ง
- - โ 1:1 ํํธ๋ ๋งค์นญ (์ฃผ 3ํ)
- - โ ์คํ ์ฝ์ํน ๋ฃธ ์
์ฅ
+ {landing.pricing.plans.starter.features.map((feature) => (
+ - โ{feature}
+ ))}
-
+
+ {landing.pricing.plans.starter.cta}
+
{/* Pro Plan */}
- ์ถ์ฒ
+ {landing.pricing.plans.pro.badge}
-
Pro
-
๋ฐฉํด ์๋ ์๋ฒฝํ ๋ชฐ์
ํ๊ฒฝ
+
{landing.pricing.plans.pro.name}
+
{landing.pricing.plans.pro.subtitle}
- โฉ6,900
- /์
+ {landing.pricing.plans.pro.price}
+ {landing.pricing.plans.pro.priceSuffix}
- - โ ํ๋ฆฌ๋ฏธ์ ํ
๋ง ๋ฌด์ ํ
- - โ 1:1 ๋งค์นญ ๋ฌด์ ํ
- - โ ๊ณ ๊ธ ์ง์ค ํต๊ณ ๋ฐ ๋ฆฌํฌํธ
- - โ ๊ณต๊ฐ ์ปค์คํ
์์ดํ
์ ๊ณต
+ {landing.pricing.plans.pro.features.map((feature) => (
+ - โ{feature}
+ ))}
-
+
+ {landing.pricing.plans.pro.cta}
+
{/* Teams Plan */}
-
Teams
-
๋ฆฌ๋ชจํธ ์ํฌ ๊ธฐ์
๋ฐ ํ
+
{landing.pricing.plans.teams.name}
+
{landing.pricing.plans.teams.subtitle}
- โฉ12,000
- /์ธยท์
+ {landing.pricing.plans.teams.price}
+ {landing.pricing.plans.teams.priceSuffix}
- - โ Pro ํ๋์ ๋ชจ๋ ๊ธฐ๋ฅ
- - โ ํ๋ผ์ด๋น ํ ์คํ์ด์ค
- - โ ํ ์ ์ฒด ์์ฐ์ฑ ๋์๋ณด๋
+ {landing.pricing.plans.teams.features.map((feature) => (
+ - โ{feature}
+ ))}
@@ -176,31 +174,31 @@ export default function MarketingPage() {
-
๐ชด VibeRoom
+
๐ชด {copy.appName}
- ํ๋ฆฌ๋์์ ์จ์ ํ ์ง์ค์ด ํ์ํ ๋ถ๋ค์ ์ํ ๋ฐ๋ปํ๊ณ ๊ตฌ์กฐํ๋ ์จ๋ผ์ธ ์ฝ์ํน ์คํ์ด์ค์
๋๋ค.
+ {landing.footer.description}
-
์ ํ
+
{landing.footer.productTitle}
-
ํ์ฌ
+
{landing.footer.companyTitle}
- © 2026 VibeRoom. All rights reserved.
+ {landing.footer.copyright}
diff --git a/src/features/auth/components/AuthRedirectButton.tsx b/src/features/auth/components/AuthRedirectButton.tsx
new file mode 100644
index 0000000..274859d
--- /dev/null
+++ b/src/features/auth/components/AuthRedirectButton.tsx
@@ -0,0 +1,47 @@
+"use client";
+
+import type { ReactNode } from "react";
+import Cookies from "js-cookie";
+import { useRouter } from "next/navigation";
+import { TOKEN_COOKIE_KEY } from "@/features/auth/model/constants";
+import { Button, type ButtonSize, type ButtonVariant } from "@/shared/ui/Button";
+import { useAuthStore } from "@/store/useAuthStore";
+
+interface AuthRedirectButtonProps {
+ children: ReactNode;
+ className?: string;
+ size?: ButtonSize;
+ variant?: ButtonVariant;
+ authenticatedHref?: string;
+ unauthenticatedHref?: string;
+}
+
+export function AuthRedirectButton({
+ children,
+ className,
+ size = "md",
+ variant = "primary",
+ authenticatedHref = "/space",
+ unauthenticatedHref = "/login",
+}: AuthRedirectButtonProps) {
+ const router = useRouter();
+ const accessToken = useAuthStore((state) => state.accessToken);
+ const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
+
+ const handleClick = () => {
+ const hasAccessToken = isAuthenticated || Boolean(accessToken) || Boolean(Cookies.get(TOKEN_COOKIE_KEY));
+ router.push(hasAccessToken ? authenticatedHref : unauthenticatedHref);
+ };
+
+ return (
+
+ );
+}
diff --git a/src/features/auth/model/constants.ts b/src/features/auth/model/constants.ts
new file mode 100644
index 0000000..78eee73
--- /dev/null
+++ b/src/features/auth/model/constants.ts
@@ -0,0 +1,2 @@
+export const TOKEN_COOKIE_KEY = "vr_access_token";
+export const REFRESH_TOKEN_COOKIE_KEY = "vr_refresh_token";
diff --git a/src/shared/i18n/index.ts b/src/shared/i18n/index.ts
new file mode 100644
index 0000000..f88fe5a
--- /dev/null
+++ b/src/shared/i18n/index.ts
@@ -0,0 +1 @@
+export { ko as copy } from './ko';
diff --git a/src/shared/i18n/ko.ts b/src/shared/i18n/ko.ts
new file mode 100644
index 0000000..8c6dd03
--- /dev/null
+++ b/src/shared/i18n/ko.ts
@@ -0,0 +1,716 @@
+export const ko = {
+ appName: 'VibeRoom',
+ metadata: {
+ title: 'VibeRoom - ๋น์ ๋ง์ ํธ์ํ ๋ชฐ์
๊ณต๊ฐ',
+ description:
+ 'ํ๋ฆฌ๋์์ ์จ์ ํ ์ง์ค์ด ํ์ํ ๋ถ๋ค์ ์ํ ๋ฐ๋ปํ๊ณ ๊ตฌ์กฐํ๋ ์จ๋ผ์ธ ์ฝ์ํน ์คํ์ด์ค. ์์
ํ์ด๋จธ, ์ธ์
๊ด๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ๋์จํ ์ฐ๋๋ฅผ ํตํด ๋น์ ์ ๋ฆฌ๋ฌ์ ์ฐพ์๋ณด์ธ์.',
+ },
+ common: {
+ close: '๋ซ๊ธฐ',
+ cancel: '์ทจ์',
+ save: '์ ์ฅ',
+ delete: '์ญ์ ',
+ complete: '์๋ฃ',
+ select: '์ ํ',
+ hub: 'ํ๋ธ๋ก',
+ loading: '๋ถ๋ฌ์ค๋ ์ค์ด์์.',
+ default: '๊ธฐ๋ณธ',
+ defaultBackground: '๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ',
+ admin: '๊ด๋ฆฌ์',
+ requestFailed: (status: number) => `์์ฒญ ์คํจ: ${status}`,
+ apiRequestFailed: (status: number) => `API ์์ฒญ ์คํจ: ${status}`,
+ },
+ landing: {
+ nav: {
+ features: '๊ธฐ๋ฅ ์๊ฐ',
+ pricing: '์๊ธ์ ',
+ login: '๋ก๊ทธ์ธ',
+ startFree: '๋ฌด๋ฃ๋ก ์์ํ๊ธฐ',
+ },
+ hero: {
+ titleLead: 'ํจ๊ปํ๋ ์กฐ์ฉํ ๋ชฐ์
,',
+ titleAccent: 'VibeRoom',
+ description:
+ '์ง์คํ๊ธฐ ์ด๋ ค์ด ์๊ฐ, ๋น์ ์ ๋ค๊ทธ์น์ง ์๋ ํธ์ํ ๊ณต๊ฐ์ผ๋ก ๋ค์ด์ค์ธ์. ๊ตฌ์กฐํ๋ ์ฝ์ํน ์ธ์
๊ณผ ๋์จํ ์ฐ๋๊ฐ ๋น์ ์ ํ์ด์ค๋ฅผ ๋์ฐพ์ ์ค๋๋ค.',
+ primaryCta: '๋๋ง์ ๊ณต๊ฐ ๋ง๋ค๊ธฐ',
+ secondaryCta: '๋ ์์๋ณด๊ธฐ',
+ timerPreview: '45:00 ๋จ์',
+ },
+ features: {
+ title: '๋น์ ์ ์ํ ๋ค์ ํ ๋ชฐ์
์ฅ์น',
+ description: '๋จ์ํ ํ์ด๋จธ๊ฐ ์๋๋๋ค. ๋ฌด๋ฆฌํ์ง ์๊ณ ์ค๋ ์ง์ํ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํฉ๋๋ค.',
+ items: [
+ {
+ icon: 'โณ',
+ title: '๊ตฌ์กฐํ๋ ์ธ์
ํ์ด๋จธ',
+ description:
+ '๋ถ๋ด ์์ด ์์ํ ์ ์๋ ์งง์ ๋ชฐ์
๊ณผ ํ์คํ ํด์. ๋น์ ๋ง์ ์์
๋ฆฌ๋ฌ์ ๋ถ๋๋ฝ๊ฒ ์ค์ ํ๊ณ ๊ด๋ฆฌํ์ธ์.',
+ },
+ {
+ icon: '๐ฑ',
+ title: '๋ค์ ํ ์ฐ๋์ ์ฝ์ํน',
+ description:
+ 'ํ๋ฉด ๋๋จธ ๋๊ตฐ๊ฐ์ ํจ๊ปํ๋ ๋ฐ๋ ๋๋ธ๋ง ํจ๊ณผ. ๊ฐ์๊ฐ ์๋, ์กฐ์ฉํ์ง๋ง ๊ฐ๋ ฅํ ๋๊ธฐ๋ฅผ ์๋ก ๋๋์ด๋ณด์ธ์.',
+ },
+ {
+ icon: '๐๏ธ',
+ title: '๋๋ง์ ์ฌ๋ฏธ์ ๊ณต๊ฐ',
+ description:
+ '๋น ์ค๋ ๋ค๋ฝ๋ฐฉ, ํ์ด ๋๋ ์นดํ. ๋ฐฑ์์์๊ณผ ํจ๊ป ๋ด๊ฐ ๊ฐ์ฅ ํธ์ํจ์ ๋๋ผ๋ ๊ฐ์ ๊ณต๊ฐ์ ๊พธ๋ฏธ๊ณ ๋จธ๋ฌด๋ฅด์ธ์.',
+ },
+ ],
+ },
+ pricing: {
+ title: '๋์๊ฒ ๋ง๋ ๊ณต๊ฐ ์ ํํ๊ธฐ',
+ description: '๊ฐ์ธ์ ๊ฐ๋ฒผ์ด ์ง์ค๋ถํฐ ํ๋ฆฌ๋์์ ์๋ฒฝํ ์ํฌ์คํ์ด์ค๊น์ง.',
+ plans: {
+ starter: {
+ name: 'Starter',
+ subtitle: '๊ฐ๋ฒผ์ด ์ง์ค์ด ํ์ํ ๋ถ',
+ price: '๋ฌด๋ฃ',
+ cta: '๋ฌด๋ฃ๋ก ์์ํ๊ธฐ',
+ features: ['๊ธฐ๋ณธ ๊ฐ์ ๊ณต๊ฐ ํ
๋ง', '1:1 ํํธ๋ ๋งค์นญ (์ฃผ 3ํ)', '์คํ ์ฝ์ํน ๋ฃธ ์
์ฅ'],
+ },
+ pro: {
+ badge: '์ถ์ฒ',
+ name: 'Pro',
+ subtitle: '๋ฐฉํด ์๋ ์๋ฒฝํ ๋ชฐ์
ํ๊ฒฝ',
+ price: 'โฉ6,900',
+ priceSuffix: '/์',
+ cta: 'Pro ์์ํ๊ธฐ',
+ features: [
+ 'ํ๋ฆฌ๋ฏธ์ ํ
๋ง ๋ฌด์ ํ',
+ '1:1 ๋งค์นญ ๋ฌด์ ํ',
+ '๊ณ ๊ธ ์ง์ค ํต๊ณ ๋ฐ ๋ฆฌํฌํธ',
+ '๊ณต๊ฐ ์ปค์คํ
์์ดํ
์ ๊ณต',
+ ],
+ },
+ teams: {
+ name: 'Teams',
+ subtitle: '๋ฆฌ๋ชจํธ ์ํฌ ๊ธฐ์
๋ฐ ํ',
+ price: 'โฉ12,000',
+ priceSuffix: '/์ธยท์',
+ cta: '๋์
๋ฌธ์ํ๊ธฐ',
+ features: ['Pro ํ๋์ ๋ชจ๋ ๊ธฐ๋ฅ', 'ํ๋ผ์ด๋น ํ ์คํ์ด์ค', 'ํ ์ ์ฒด ์์ฐ์ฑ ๋์๋ณด๋'],
+ },
+ },
+ },
+ footer: {
+ description:
+ 'ํ๋ฆฌ๋์์ ์จ์ ํ ์ง์ค์ด ํ์ํ ๋ถ๋ค์ ์ํ ๋ฐ๋ปํ๊ณ ๊ตฌ์กฐํ๋ ์จ๋ผ์ธ ์ฝ์ํน ์คํ์ด์ค์
๋๋ค.',
+ productTitle: '์ ํ',
+ companyTitle: 'ํ์ฌ',
+ links: {
+ features: '๊ธฐ๋ฅ ์๊ฐ',
+ pricing: '์๊ธ์ ',
+ webLogin: '์น์ฑ ๋ก๊ทธ์ธ',
+ about: '์๊ฐ',
+ privacy: '๊ฐ์ธ์ ๋ณด์ฒ๋ฆฌ๋ฐฉ์นจ',
+ terms: '์ด์ฉ์ฝ๊ด',
+ },
+ copyright: 'ยฉ 2026 VibeRoom. All rights reserved.',
+ },
+ },
+ login: {
+ title: '๋ค์ ์ค์
จ๊ตฐ์!',
+ descriptionFirstLine: '๋น๋ฐ๋ฒํธ๋ฅผ ์ธ์ธ ํ์ ์์ด,',
+ descriptionSecondLine: '์ฌ์ฉ ์ค์ธ ๊ณ์ ์ผ๋ก 3์ด ๋ง์ ์์ํ์ธ์.',
+ agreementPrefix: '๋ก๊ทธ์ธํจ์ผ๋ก์จ VibeRoom์',
+ agreementAnd: '๋ฐ',
+ terms: '์ด์ฉ์ฝ๊ด',
+ privacy: '๊ฐ์ธ์ ๋ณด์ฒ๋ฆฌ๋ฐฉ์นจ',
+ agreementSuffix: '์ ๋์ํ๊ฒ ๋ฉ๋๋ค.',
+ },
+ auth: {
+ social: {
+ connecting: '์ฐ๊ฒฐ ์ค...',
+ continueWithGoogle: 'Google๋ก ๊ณ์ํ๊ธฐ',
+ continueWithApple: 'Apple๋ก ๊ณ์ํ๊ธฐ',
+ continueWithFacebook: 'Facebook์ผ๋ก ๊ณ์ํ๊ธฐ',
+ },
+ errors: {
+ loginFailed: '๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค. ๋ค์ ์๋ํด ์ฃผ์ธ์.',
+ googleFailed: '๊ตฌ๊ธ ๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค. ํ์
์ฐจ๋จ ์ฌ๋ถ๋ฅผ ํ์ธํด ์ฃผ์ธ์.',
+ appleFailed: '์ ํ ๋ก๊ทธ์ธ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.',
+ appleInitFailed: '์ ํ ๋ก๊ทธ์ธ ์ด๊ธฐํ ์คํจ',
+ facebookFailed: 'ํ์ด์ค๋ถ ๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค.',
+ },
+ },
+ admin: {
+ defaultLoginId: 'qwer1234',
+ defaultPassword: 'qwer1234!',
+ consoleLabel: 'Admin Console',
+ mediaAdminLabel: 'Media Admin',
+ localCredentialsHint: 'Local admin credentials',
+ signInEyebrow: 'Sign In',
+ loginTitle: '๊ด๋ฆฌ์ ๋ก๊ทธ์ธ',
+ loginDescription: '๊ด๋ฆฌ์ ๊ถํ์ด ํ์ธ๋๋ฉด ์ข์ธก ์ฌ์ด๋๋ฐ ๊ธฐ๋ฐ ๋์๋ณด๋๊ฐ ์ด๋ฆฝ๋๋ค.',
+ loginIdLabel: '์์ด๋',
+ loginPasswordLabel: '๋น๋ฐ๋ฒํธ',
+ openDashboard: '๋์๋ณด๋ ์ด๊ธฐ',
+ loggingIn: '๋ก๊ทธ์ธ ์ค...',
+ logout: '๋ก๊ทธ์์',
+ mediaOperations: 'Media Operations',
+ access: 'Access',
+ accessTitle: 'Admin Only',
+ accessDescription: '๋ก๊ทธ์ธ ํ ์
๋ก๋ ํ ํฐ์ผ๋ก scene ์ด๋ฏธ์ง์ sound ์ค๋์ค๋ฅผ ๋ฐ๋ก R2์ ๋ฐ์ํฉ๋๋ค.',
+ manifestReady: 'Manifest Ready',
+ sessionSection: 'Session',
+ sessionAdminLabel: 'Admin',
+ sessionRoleLabel: 'Role',
+ roleAccessSuffix: ' Access',
+ searchValues: {
+ scene: 'scene assets',
+ sound: 'sound assets',
+ },
+ navItems: [
+ {
+ id: 'scene',
+ title: '์ด๋ฏธ์ง ๋ฑ๋ก',
+ subtitle: 'Scene background assets',
+ section: 'MEDIA',
+ },
+ {
+ id: 'sound',
+ title: '์ค๋์ค ๋ฑ๋ก',
+ subtitle: 'Loop and preview assets',
+ section: 'MEDIA',
+ },
+ ],
+ views: {
+ scene: {
+ eyebrow: 'Image Registration',
+ title: 'Scene ์ด๋ฏธ์ง ๋ฑ๋ก',
+ description:
+ 'Space์ ๋ค์ด์จ ์ฌ์ฉ์๊ฐ ์ ํํ๋ ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง ์ธํธ๋ฅผ ๋ฑ๋กํฉ๋๋ค. cardFile๊ณผ stageFile์ ์ต์ด ์์ฑ ์ ํ์์
๋๋ค.',
+ statTitle: 'Image Pipeline',
+ statValue: 'Scene Assets',
+ statHint: 'R2 / Manifest Sync',
+ workspaceTitle: '์ด๋ฏธ์ง ๋ฑ๋ก ์ํฌ์คํ์ด์ค',
+ workspaceDescription: '์ผ์ชฝ ๋ฉ๋ด๋ ์ ์ง๋๊ณ , ์ด ์ค์ ์์ญ๋ง `์ด๋ฏธ์ง ๋ฑ๋ก` ์์
์ผ๋ก ์ ํ๋ฉ๋๋ค.',
+ sceneIdLabel: 'Scene ID',
+ sceneIdPlaceholder: 'rain-window',
+ placeholderGradientLabel: 'Placeholder Gradient',
+ placeholderGradientPlaceholder: 'linear-gradient(160deg, #1e293b 0%, #0f172a 100%)',
+ cardImageLabel: 'Card Image',
+ stageImageLabel: 'Stage Image',
+ mobileStageLabel: 'Mobile Stage',
+ hdStageLabel: 'HD Stage',
+ blurDataUrlLabel: 'Blur Data URL',
+ blurDataUrlPlaceholder: 'data:image/jpeg;base64,...',
+ notesTitle: '์
๋ก๋ ๋ฉ๋ชจ',
+ notes: [
+ '์ต์ด ์์ฑ ์ `cardFile`, `stageFile`์ ํ์์
๋๋ค.',
+ '๋ชจ๋ฐ์ผ๊ณผ HD ํ์ผ์ ์ ํ๊ฐ์ด๋ฉฐ ๋น ์ง๋ฉด ๊ธฐ์กด ๊ฐ์ ์ ์งํฉ๋๋ค.',
+ '์
๋ก๋ ์ฆ์ R2 ๊ฒฝ๋ก์ manifest ์๋ต ๊ฐ์ด ๊ฐฑ์ ๋ฉ๋๋ค.',
+ ],
+ submit: '์ด๋ฏธ์ง ๋ฑ๋ก',
+ pending: '์ด๋ฏธ์ง ์
๋ก๋ ์ค...',
+ },
+ sound: {
+ eyebrow: 'Audio Registration',
+ title: 'Sound ์ค๋์ค ๋ฑ๋ก',
+ description:
+ 'Loop, preview, fallback ์ค๋์ค๋ฅผ ๋ฑ๋กํฉ๋๋ค. loopFile์ ์ต์ด ์์ฑ ์ ํ์์ด๋ฉฐ ๊ธฐ๋ณธ ๋ณผ๋ฅจ๊ณผ duration ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ์ ์ฅํฉ๋๋ค.',
+ statTitle: 'Audio Pipeline',
+ statValue: 'Sound Assets',
+ statHint: 'Loop / Preview / Fallback',
+ workspaceTitle: '์ค๋์ค ๋ฑ๋ก ์ํฌ์คํ์ด์ค',
+ workspaceDescription: '์ข์ธก ๋ค๋น๊ฒ์ด์
์ ๊ณ ์ ๋๊ณ , ์ด ์ค์ ์์ญ๋ง `์ค๋์ค ๋ฑ๋ก` ์์
์ผ๋ก ๋ฐ๋๋๋ค.',
+ presetIdLabel: 'Preset ID',
+ presetIdPlaceholder: 'rain-focus',
+ durationLabel: 'Duration Sec',
+ durationPlaceholder: '1800',
+ defaultVolumeLabel: 'Default Volume',
+ defaultVolumePlaceholder: '60',
+ loopFileLabel: 'Loop File',
+ previewFileLabel: 'Preview File',
+ fallbackLoopFileLabel: 'Fallback Loop File',
+ notesTitle: '์
๋ก๋ ๋ฉ๋ชจ',
+ notes: [
+ '์ต์ด ์์ฑ ์ `loopFile`์ ํ์์
๋๋ค.',
+ '`preview`, `fallback`์ ์ ํ๊ฐ์ด๋ฉฐ ๊ธฐ์กด ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ์งํ ์ ์์ต๋๋ค.',
+ '`defaultVolume`, `durationSec`๋ manifest ์๋ต์ ํจ๊ป ๋ฐ์๋ฉ๋๋ค.',
+ ],
+ submit: '์ค๋์ค ๋ฑ๋ก',
+ pending: '์ค๋์ค ์
๋ก๋ ์ค...',
+ },
+ },
+ inspector: {
+ recentResponse: '์ต๊ทผ ์๋ต',
+ noUploadSummary: '์์ง ์
๋ก๋ ์์
์ด ์์ต๋๋ค.',
+ noUploadPayload: '์
๋ก๋ ์๋ต์ด ์์ง ์์ต๋๋ค.',
+ sessionToken: '์ธ์
ํ ํฐ',
+ currentRoleTitle: 'Current Role',
+ bearerTokenSession: 'Bearer token enabled session',
+ },
+ messages: {
+ nonAdmin: 'ADMIN ๊ถํ์ด ์๋ ๊ณ์ ์
๋๋ค.',
+ loginFailed: '๋ก๊ทธ์ธ์ ์คํจํ์ต๋๋ค.',
+ loginRequired: '๋จผ์ ๊ด๋ฆฌ์ ๋ก๊ทธ์ธ์ ํด์ฃผ์ธ์.',
+ sceneIdRequired: 'sceneId๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.',
+ presetIdRequired: 'presetId๋ฅผ ์
๋ ฅํด์ฃผ์ธ์.',
+ sceneUploadFailed: 'scene ์
๋ก๋์ ์คํจํ์ต๋๋ค.',
+ soundUploadFailed: 'sound ์
๋ก๋์ ์คํจํ์ต๋๋ค.',
+ sceneUploadDone: (sceneId: string) => `scene "${sceneId}" ์
๋ก๋๊ฐ ์๋ฃ๋์์ต๋๋ค.`,
+ soundUploadDone: (presetId: string) => `sound "${presetId}" ์
๋ก๋๊ฐ ์๋ฃ๋์์ต๋๋ค.`,
+ sceneSummary: (sceneId: string, version: string) =>
+ `${sceneId} ์ด๋ฏธ์ง ์ธํธ๊ฐ ์ต์ ๋ฒ์ ${version}๋ก ๋ฐ์๋์์ต๋๋ค.`,
+ soundSummary: (presetId: string, version: string) =>
+ `${presetId} ์ค๋์ค ์ธํธ๊ฐ ์ต์ ๋ฒ์ ${version}๋ก ๋ฐ์๋์์ต๋๋ค.`,
+ },
+ },
+ settings: {
+ title: 'Settings',
+ focusPreferencesApi: 'Focus Preferences API',
+ loading: '์ ์ฅ๋ ์ค์ ์ ๋ถ๋ฌ์ค๋ ์ค์ด์์.',
+ saving: '๋ณ๊ฒฝ ์ฌํญ์ ์ ์ฅํ๋ ์ค์ด์์.',
+ synced: '๋ณ๊ฒฝ ์ฆ์ ์๋ฒ์ ์ ์ฅํฉ๋๋ค.',
+ reduceMotionTitle: 'Reduce Motion',
+ reduceMotionDescription: '์ ํ ์ ๋๋ฉ์ด์
์ ์ต์ํํฉ๋๋ค. (UI ํ ๊ธ ๋ชฉ์
)',
+ notificationIntensityTitle: '์๋ฆผ ๊ฐ๋',
+ notificationIntensityDescription: '์ง์ค ์์/์ข
๋ฃ ์ ํธ์ ์กด์ฌ๊ฐ์ ์ ํํฉ๋๋ค.',
+ defaultPresetTitle: '๊ธฐ๋ณธ ํ๋ฆฌ์
',
+ defaultPresetDescription: '์
์ฅ ์ ์๋ ์ ํ๋ ์ถ์ฒ ์ธํธ๋ฅผ ๊ณ ๋ฆ
๋๋ค.',
+ notificationIntensityOptions: ['์กฐ์ฉํจ', '๊ธฐ๋ณธ', '๊ฐํจ'],
+ defaultPresetOptions: [
+ { id: 'balanced', label: 'Balanced 25/5 + Rain Focus' },
+ { id: 'deep-work', label: 'Deep Work 50/10 + Deep White' },
+ { id: 'gentle', label: 'Gentle 25/5 + Silent' },
+ ],
+ },
+ stats: {
+ title: 'Stats',
+ apiLabel: 'API',
+ mockLabel: 'Mock',
+ sourceApi: 'API ํต๊ณ ์ฌ์ฉ ์ค',
+ sourceMock: 'API ์คํจ๋ก mock ํต๊ณ ํ์ ์ค',
+ loading: 'ํต๊ณ๋ฅผ ๋ถ๋ฌ์ค๋ ์ค์ด์์.',
+ loadFailed: 'ํต๊ณ๋ฅผ ๋ถ๋ฌ์ค์ง ๋ชปํ์ด์.',
+ synced: 'ํ๋ฉด ์ง์
์ ์ต์ ์์ฝ์ ๋๊ธฐํํฉ๋๋ค.',
+ refresh: '์๋ก๊ณ ์นจ',
+ today: '์ค๋',
+ last7Days: '์ต๊ทผ 7์ผ',
+ chartTitle: '์ง์ค ํ๋ฆ ๊ทธ๋ํ',
+ chartWithTrend: 'trend ์๋ต์ผ๋ก ๊ฐ๋จํ ๋ง๋ ๊ทธ๋ํ๋ฅผ ๋ ๋๋งํฉ๋๋ค.',
+ chartWithoutTrend: 'trend ์๋ต์ด ๋น์ด ์์ด ํ๋ ์ด์คํ๋ ์ํ์
๋๋ค.',
+ todayFocus: '์ค๋ ์ง์ค ์๊ฐ',
+ completedCycles: '์๋ฃํ ์ฌ์ดํด',
+ sessionEntries: '์
์ฅ ํ์',
+ last7DaysFocus: '์ต๊ทผ 7์ผ ์ง์ค ์๊ฐ',
+ bestDay: '์ต๊ณ ๋ชฐ์
์ผ',
+ streak: '์ฐ์ ๋ฌ์ฑ',
+ syncedApi: '๋๊ธฐํ๋จ',
+ temporary: '์์๊ฐ',
+ actualAggregate: '์ค์ง๊ณ',
+ mockAggregate: '๋ชฉ์
',
+ streakActive: '์ ์ง ์ค',
+ streakStart: '์์ ์ ',
+ countUnit: 'ํ',
+ dayUnit: '์ผ',
+ minuteUnit: '๋ถ',
+ barTitle: (date: string, minutes: number) => `${date} ยท ${minutes}๋ถ`,
+ },
+ plan: {
+ proFeatureCards: [
+ {
+ id: 'scene-packs',
+ name: 'Scene Packs',
+ description: 'ํ๋ฆฌ๋ฏธ์ ๊ณต๊ฐ ๋ฌถ์๊ณผ ์ฅ๋ฉด ๋ณ์ฃผ',
+ },
+ {
+ id: 'sound-packs',
+ name: 'Sound Packs',
+ description: 'ํ์ฅ ์ฌ์ด๋ ํ๋ฆฌ์
๋ฌถ์',
+ },
+ {
+ id: 'profiles',
+ name: 'Profiles',
+ description: '๋ด ๊ธฐ๋ณธ ์ธํ
์ ์ฅ/๋ถ๋ฌ์ค๊ธฐ',
+ },
+ ],
+ },
+ session: {
+ todayOneLiner: '์ค๋์ ํ ์ค: ์๋ฒฝ๋ณด๋ค ์์, ํ ์กฐ๊ฐ์ด๋ฉด ์ถฉ๋ถํด์.',
+ goalChips: [
+ { id: 'mail-3', label: '๋ฉ์ผ 3๊ฐ' },
+ { id: 'doc-1p', label: '๋ฌธ์ 1p' },
+ { id: 'code-1-function', label: '์ฝ๋ฉ 1ํจ์' },
+ { id: 'tidy-10m', label: '์ ๋ฆฌ 10๋ถ' },
+ { id: 'reading-15m', label: '๋
์ 15๋ถ' },
+ { id: 'resume-1paragraph', label: '์ด๋ ฅ์ 1๋ฌธ๋จ' },
+ ],
+ checkInPhrases: [
+ { id: 'arrived', text: '์ง๊ธ ๋ค์ด์์ด์' },
+ { id: 'sprint-25', text: '25๋ถ๋ง ๋ฌ๋ฆด๊ฒ์' },
+ { id: 'on-break', text: 'ํด์ ์ค' },
+ { id: 'back-focus', text: '๋ค์ ์ง์ค!' },
+ { id: 'slow-day', text: '์ค๋์ ์ฒ์ฒํ' },
+ ],
+ reactionOptions: [
+ { id: 'thumbs-up', emoji: '๐', label: '์์ํด์' },
+ { id: 'fire', emoji: '๐ฅ', label: '์ง์ค ๋ชจ๋' },
+ { id: 'clap', emoji: '๐', label: '์ํ๊ณ ์์ด์' },
+ { id: 'heart-hands', emoji: '๐ซถ', label: '์ฐ๊ฒฐ๋์ด ์์ด์' },
+ ],
+ soundPresets: [
+ { id: 'deep-white', label: 'Deep White' },
+ { id: 'rain-focus', label: 'Rain Focus' },
+ { id: 'cafe-work', label: 'Cafe Work' },
+ { id: 'ocean-calm', label: 'Ocean Calm' },
+ { id: 'fireplace', label: 'Fireplace' },
+ { id: 'silent', label: 'Silent' },
+ ],
+ timerPresets: [
+ { id: '25-5', label: '25/5', focusMinutes: 25, breakMinutes: 5 },
+ { id: '50-10', label: '50/10', focusMinutes: 50, breakMinutes: 10 },
+ { id: '90-20', label: '90/20', focusMinutes: 90, breakMinutes: 20 },
+ { id: 'custom', label: '์ปค์คํ
' },
+ ],
+ distractionDumpPlaceholder: ['๋์์ธ QA ์์ฒญ ํ์ธ', '์ธ๊ธ๊ณ์ฐ์ ๋ฐํ ๋ฉ๋ชจ', '์คํ ๋ฏธํ
์ง๋ฌธ 1๊ฐ ์ ๋ฆฌ'],
+ todayStats: [
+ { id: 'today-focus', label: '์ค๋ ์ง์ค ์๊ฐ', value: '2h 40m', delta: '+35m' },
+ { id: 'today-cycles', label: '์๋ฃํ ์ฌ์ดํด', value: '5ํ', delta: '+1' },
+ { id: 'today-entry', label: '์
์ฅ ํ์', value: '3ํ', delta: '์ ์ง' },
+ ],
+ weeklyStats: [
+ { id: 'week-focus', label: '์ต๊ทผ 7์ผ ์ง์ค ์๊ฐ', value: '14h 20m', delta: '+2h 10m' },
+ { id: 'week-best-day', label: '์ต๊ณ ๋ชฐ์
์ผ', value: '์์์ผ', delta: '3h 30m' },
+ { id: 'week-consistency', label: '์ฐ์ ๋ฌ์ฑ', value: '4์ผ', delta: '+1์ผ' },
+ ],
+ recentThoughts: [
+ {
+ id: 'thought-1',
+ text: '๋ด์ผ ๋ฏธํ
์ ์ ์ ์์ ์ฒซ ๋ฌธ๋จ๋ง ๋ค์ ๋ค๋ฌ๊ธฐ',
+ sceneName: '๋์๊ด',
+ capturedAt: '๋ฐฉ๊ธ ์ ',
+ },
+ {
+ id: 'thought-2',
+ text: '๊ธฐํ ๋ฌธ์์ ํต์ฌ ํ๋ฆ์ ํ ๋ฌธ์ฅ์ผ๋ก ์ ๋ฆฌํด๋๊ธฐ',
+ sceneName: '๋น ์ค๋ ์ฐฝ๊ฐ',
+ capturedAt: '24๋ถ ์ ',
+ },
+ {
+ id: 'thought-3',
+ text: '์คํ์ ํ์ธํ ์ด์ ๋ฒํธ๋ง ๋ฉ๋ชจํ๊ณ ์ง๊ธ ์์
๋ณต๊ท',
+ sceneName: '์ฒ',
+ capturedAt: '1์๊ฐ ์ ',
+ },
+ {
+ id: 'thought-4',
+ text: '๋ฆฌ๋ทฐ ์ฝ๋ฉํธ๋ ์ค๋ 17์ ์ดํ์ ํ ๋ฒ์ ์ฒ๋ฆฌ',
+ sceneName: '๋ฒฝ๋๋ก',
+ capturedAt: '์ด์ ',
+ },
+ ],
+ justNow: '๋ฐฉ๊ธ ์ ',
+ },
+ scenes: [
+ {
+ id: 'rain-window',
+ name: '๋น ์ค๋ ์ฐฝ๊ฐ',
+ description: '๋น์๋ฆฌ ์๋ก ์คํ ๋ ์กฐ๋ช
์ด ๋ถ๋๋ฝ๊ฒ ๋ฒ์ง๋๋ค.',
+ tags: ['์ ์๊ทน', '๊ฐ์ฑ'],
+ recommendedSound: 'Rain Focus',
+ recommendedTime: '๋ฐค',
+ vibeLabel: '์์ํจ',
+ },
+ {
+ id: 'dawn-cafe',
+ name: '์๋ฒฝ ์นดํ',
+ description: '์ฒซ ์ปคํผ ํฅ์ฒ๋ผ ์์ํ๊ณ ๋ฐ๋ปํ ์ข์.',
+ tags: ['๊ฐ์ฑ', '๋ฅ์ํฌ'],
+ recommendedSound: 'Cafe Murmur',
+ recommendedTime: '์๋ฒฝ',
+ vibeLabel: 'ํฌ๊ทผํจ',
+ },
+ {
+ id: 'quiet-library',
+ name: '๋์๊ด',
+ description: '๋๊ธฐ๋ ์ข
์ด ์๋ฆฌ๋ง ๋ค๋ฆฌ๋ ์ ๋๋ ์ฑ
์.',
+ tags: ['์ ์๊ทน', '๋ฅ์ํฌ'],
+ recommendedSound: 'Deep White',
+ recommendedTime: '์คํ',
+ vibeLabel: '๋ชฐ์
',
+ },
+ {
+ id: 'wave-sound',
+ name: 'ํ๋ ์๋ฆฌ',
+ description: '์์ํ ํด๋ณ ์๋ก ํธํก์ ๊ณ ๋ฅด๋ ๊ณต๊ฐ.',
+ tags: ['์์ง์ ์ ์', '๊ฐ์ฑ'],
+ recommendedSound: 'Ocean Breath',
+ recommendedTime: '๋ฐค',
+ vibeLabel: '์ฐจ๋ถํจ',
+ },
+ {
+ id: 'green-forest',
+ name: '์ฒ',
+ description: '๋ฐ๋์ด ๋๋ญ์์ ์ค์น๋ ์๋ฆฌ๋ก ๋ง์์ ๋ฎ์ถฅ๋๋ค.',
+ tags: ['์ ์๊ทน', '์์ง์ ์ ์'],
+ recommendedSound: 'Forest Hush',
+ recommendedTime: '์ค์ ',
+ vibeLabel: '๋ง์',
+ },
+ {
+ id: 'fireplace',
+ name: '๋ฒฝ๋๋ก',
+ description: '์์ ๋ถ๊ฝ์ด ์ฃผ๋ ๋ฆฌ๋ฌ์ผ๋ก ์ง์ค์ ๋ถ์ก์ต๋๋ค.',
+ tags: ['๊ฐ์ฑ', '์ ์๊ทน'],
+ recommendedSound: 'Fireplace',
+ recommendedTime: '๋ฐค',
+ vibeLabel: '์จ๊ธฐ',
+ },
+ {
+ id: 'city-night',
+ name: '๋์ ์ผ๊ฒฝ',
+ description: '์ ๋ฆฌ์ฐฝ ๋๋จธ ์ผ๊ฒฝ์ด ๋ฉ๋ฆฌ ํ๋ฅด๋ ๊ณ ์ํ ๋ฐค.',
+ tags: ['๋ฅ์ํฌ', '๊ฐ์ฑ'],
+ recommendedSound: 'Night Lo-fi',
+ recommendedTime: '์ฌ์ผ',
+ vibeLabel: '๊ณ ์ํจ',
+ },
+ {
+ id: 'snow-mountain',
+ name: '์ค์ฐ',
+ description: '์ฐจ๋ถํ ๊ณต๊ธฐ์ ์ ๋ช
ํ ์ํ์ ์ด ๋จธ๋ฆฌ๋ฅผ ๋ง๊ฒ ํฉ๋๋ค.',
+ tags: ['์์ง์ ์ ์', '๋ฅ์ํฌ'],
+ recommendedSound: 'Cold Wind',
+ recommendedTime: '์๋ฒฝ',
+ vibeLabel: '์ ๋ช
ํจ',
+ },
+ {
+ id: 'sun-window',
+ name: '์ฐฝ๊ฐ',
+ description: 'ํ์ด์ด ๋ค์ด์ค๋ ๊ฐ๊ฒฐํ ์ฑ
์, ๋ถ๋ด ์๋ ์์.',
+ tags: ['์ ์๊ทน', '๋ฅ์ํฌ'],
+ recommendedSound: 'Soft Daylight',
+ recommendedTime: '์คํ',
+ vibeLabel: '๊ฐ๋ฒผ์',
+ },
+ {
+ id: 'outer-space',
+ name: '์ฐ์ฃผ',
+ description: '๋ณ๋น๋ง ๋จ๊ธด ์ด๋ ์์์ ๊น๊ฒ ์ ์ํฉ๋๋ค.',
+ tags: ['๋ฅ์ํฌ', '๊ฐ์ฑ'],
+ recommendedSound: 'Deep Drone',
+ recommendedTime: '์ฌ์ผ',
+ vibeLabel: '๊น์',
+ },
+ ],
+ modal: {
+ closeAriaLabel: '๋ชจ๋ฌ ๋ซ๊ธฐ',
+ closeButton: '๋ซ๊ธฐ',
+ },
+ media: {
+ manifestLoadFailed: '๋ฏธ๋์ด manifest๋ฅผ ๋ถ๋ฌ์ค์ง ๋ชปํ์ด์.',
+ },
+ preferences: {
+ defaultNotificationIntensity: '๊ธฐ๋ณธ',
+ loadFailed: '์ค์ ์ ๋ถ๋ฌ์ค์ง ๋ชปํ์ด์.',
+ saveFailed: '์ค์ ์ ์ ์ฅํ์ง ๋ชปํ์ด์.',
+ saved: '์ ์ฅ๋จ',
+ saveFailedLabel: '์ ์ฅ ์คํจ',
+ },
+ focusSession: {
+ syncFailed: '์ธ์
์์ง๊ณผ ๋๊ธฐํํ์ง ๋ชปํ์ด์.',
+ startFailed: '์ธ์
์ ์์ํ์ง ๋ชปํ์ด์.',
+ pauseFailed: '์ธ์
์ ์ผ์์ ์งํ์ง ๋ชปํ์ด์.',
+ resumeFailed: '์ธ์
์ ๋ค์ ์์ํ์ง ๋ชปํ์ด์.',
+ restartPhaseFailed: 'ํ์ฌ ํ์ด์ฆ๋ฅผ ๋ค์ ์์ํ์ง ๋ชปํ์ด์.',
+ completeFailed: '์ธ์
์ ์๋ฃ ์ฒ๋ฆฌํ์ง ๋ชปํ์ด์.',
+ abandonFailed: '์ธ์
์ ์ข
๋ฃํ์ง ๋ชปํ์ด์.',
+ },
+ space: {
+ sessionGoal: {
+ label: '์ด๋ฒ 25๋ถ, ๋ฑ ํ ๊ฐ์ง',
+ required: '(ํ์)',
+ placeholder: '์: ๊ณ์ฝ์ 1ํ์ด์ง ์ ๋ฆฌ',
+ hint: 'ํฌ๊ฒ ๋ง๊ณ , ๋ฐ๋ก ๋ค์ ํ ์กฐ๊ฐ.',
+ },
+ setup: {
+ panelAriaLabel: '์ง์ค ์์ ํจ๋',
+ eyebrow: 'Ritual',
+ title: '์ด๋ฒ ํ ์กฐ๊ฐ์ ์ ํ๊ณ ์์ํด์.',
+ description: '๋ชฉํ๋ฅผ ์ ํ ๋ค HUD์ ์์ ๋ฒํผ์ผ๋ก ์ค์ ์ธ์
์ ์์ํด์.',
+ resumeTitle: '์ง๋ ํ ์กฐ๊ฐ ์ด์ด์',
+ startFresh: '์๋ก ์์',
+ resumePrepare: '์ด์ด์ ์ค๋น',
+ sceneLabel: '๋ฐฐ๊ฒฝ',
+ timerLabel: 'ํ์ด๋จธ',
+ soundLabel: '์ฌ์ด๋',
+ readyHint: '๋ชฉํ๋ฅผ ์ ์ผ๋ฉด ์์ํ ์ ์์ด์.',
+ openFocusScreen: '์ง์ค ํ๋ฉด ์ด๊ธฐ',
+ },
+ timerHud: {
+ actions: [
+ { id: 'start', label: '์์', icon: 'โถ' },
+ { id: 'pause', label: '์ผ์์ ์ง', icon: 'โธ' },
+ { id: 'reset', label: '๋ฆฌ์
', icon: 'โบ' },
+ ],
+ readyMode: 'Ready',
+ focusMode: 'Focus',
+ breakMode: 'Break',
+ goalFallback: '์ด๋ฒ ํ ์กฐ๊ฐ์ ์ค์ ํด ์ฃผ์ธ์.',
+ goalPrefix: '์ด๋ฒ ํ ์กฐ๊ฐ ยท ',
+ completeButton: '์๋ฃ',
+ },
+ focusHud: {
+ goalFallback: '์ง์ค์ ์์ํด์.',
+ goalToast: (goal: string) => `์ด๋ฒ ํ ์กฐ๊ฐ ยท ${goal}`,
+ restReminder: '5๋ถ์ด ์ง๋ฌ์ด์. ๋ค์ ํ ์กฐ๊ฐ์ผ๋ก ๋์์์.',
+ },
+ goalComplete: {
+ suggestions: ['๋ฆฌ๋ทฐ ์ฝ๋ฉํธ 2๊ฐ ์ฒ๋ฆฌ', '๋ฌธ์ 1๋ฌธ๋จ ๋ค๋ฌ๊ธฐ', '์ด์ 1๊ฐ ์ ๋ฆฌ', '๋ฉ์ผ 2๊ฐ ํ์ '],
+ placeholderFallback: '๋ค์ ํ ์กฐ๊ฐ์ ์ ์ด๋ณด์ธ์',
+ placeholderExample: (goal: string) => `์: ${goal}`,
+ title: '์ข์์. ๋ค์ ํ ์กฐ๊ฐ์?',
+ description: '๋๋ฌด ํฌ๊ฒ ์ก์ง ๋ง๊ณ , ๋ฐ๋ก ๋ค์ ํ ์กฐ๊ฐ๋ง.',
+ closeAriaLabel: '๋ซ๊ธฐ',
+ restButton: '์ ๊น ์ฌ๊ธฐ',
+ confirmButton: '๋ฐ๋ก ๋ค์ ์กฐ๊ฐ ์์',
+ },
+ controlCenter: {
+ sectionTitles: {
+ background: 'Background',
+ time: 'Time',
+ sound: 'Sound',
+ packs: 'Packs',
+ },
+ packsDescription: 'ํ์ฅ/๊ฐ์ธํ',
+ recommendation: (soundLabel: string, timerLabel: string) => `์ถ์ฒ: ${soundLabel} ยท ${timerLabel}`,
+ recommendationHint: '์ถ์ฒ ์กฐํฉ์ ์ฐธ๊ณ ์ ๋ณด๋ก๋ง ์ ๊ณต๋ผ์.',
+ autoHideTitle: '์ปจํธ๋กค ์๋ ์จ๊น',
+ autoHideDescription: '์
๋ ฅ์ด ์์ผ๋ฉด ์ ์ ํ ํจ๋์ ๋ซ์์.',
+ autoHideAriaLabel: '์ปจํธ๋กค ์๋ ์จ๊น',
+ sideSheetSubtitle: '๋ฐฐ๊ฒฝ ยท ํ์ด๋จธ ยท ์ฌ์ด๋๋ฅผ ๊ทธ ์๋ฆฌ์์ ๋ฐ๊ฟ์.',
+ quickControlsTitle: 'Quick Controls',
+ },
+ toolsDock: {
+ notesButton: 'Notes',
+ popoverCloseAria: 'ํ์ค๋ฒ ๋ซ๊ธฐ',
+ planPro: 'PRO',
+ planNormal: 'Normal',
+ inboxSaved: '์ธ๋ฐ์ค์ ์ ์ฅ๋จ',
+ undo: '์คํ์ทจ์',
+ inboxSaveUndone: '์ ์ฅ ์ทจ์๋จ',
+ deleted: '์ญ์ ๋จ',
+ deleteUndone: '์ญ์ ๋ฅผ ์ทจ์ํ์ด์.',
+ emptyToClear: '๋น์ธ ํญ๋ชฉ์ด ์์ด์.',
+ clearedAll: '๋ชจ๋ ๋น์์ง',
+ restored: '๋ณต์ํ์ด์.',
+ normalPlanInfo: 'NORMAL ํ๋ ์ฌ์ฉ ์ค ยท ์ ๊ธ ํญ๋ชฉ์์๋ง ์
๊ทธ๋ ์ด๋ํ ์ ์์ด์.',
+ proFeatureLocked: (source: string) => `${source}์(๋) PRO ๊ธฐ๋ฅ์ด์์.`,
+ proFeaturePending: (label: string) => `${label} ์ค๋น ์ค(๋๋ฏธ)`,
+ purchaseMock: '๊ฒฐ์ (๋๋ฏธ)',
+ manageSubscriptionMock: '๊ตฌ๋
๊ด๋ฆฌ(๋๋ฏธ)',
+ restorePurchaseMock: '๊ตฌ๋งค ๋ณต์(๋๋ฏธ)',
+ featureLabels: {
+ scenePacks: 'Scene Packs',
+ soundPacks: 'Sound Packs',
+ profiles: 'Profiles',
+ },
+ utilityPanelTitle: {
+ 'control-center': 'Quick Controls',
+ inbox: '์ธ๋ฐ์ค',
+ paywall: 'PRO',
+ 'manage-plan': 'ํ๋ ๊ด๋ฆฌ',
+ },
+ },
+ quickNotes: {
+ title: '๋ ์ค๋ฅธ ์๊ฐ์ ์ ๊น ์ฃผ์ฐจํด์',
+ placeholder: '๋ ์ค๋ฅธ ์๊ฐ์ ์ ๊น ์ฃผ์ฐจโฆ',
+ submit: '์ ์ฅ',
+ hint: '๋์ค์ ์ธ๋ฐ์ค์์ ์ ๋ฆฌํด์.',
+ },
+ quickSound: {
+ currentSound: 'ํ์ฌ ์ฌ์ด๋',
+ muteAriaLabel: '์์๊ฑฐ',
+ unmuteAriaLabel: '์์๊ฑฐ ํด์ ',
+ volumeAriaLabel: '์ฌ์ด๋ ๋ณผ๋ฅจ',
+ quickSwitch: '๋น ๋ฅธ ์ ํ',
+ },
+ soundPresetControls: {
+ preset: 'Preset',
+ mixerOpen: 'Mixer ํผ์น๊ธฐ',
+ mixerClose: 'Mixer ์ ๊ธฐ',
+ mock: '๋๋ฏธ',
+ masterVolume: '๋ง์คํฐ ๋ณผ๋ฅจ',
+ mute: '๋ฎคํธ',
+ muteToggleAriaLabel: '๋ง์คํฐ ๋ฎคํธ ํ ๊ธ',
+ trackLabels: {
+ white: 'White',
+ rain: 'Rain',
+ cafe: 'Cafe',
+ wave: 'Wave',
+ fan: 'Fan',
+ },
+ },
+ inbox: {
+ empty: '์ง๊ธ์ ๋น์ด ์์ด์. ์ง์ค ์ค ๋ ์ค๋ฅธ ์๊ฐ์ ์ฌ๊ธฐ๋ก ์ฃผ์ฐจํ ์ ์์ด์.',
+ complete: '์๋ฃ',
+ completed: '์๋ฃ๋จ',
+ delete: '์ญ์ ',
+ readOnly: '๋์ค์ ๋ชจ์๋ณด๋ ์ฝ๊ธฐ ์ ์ฉ ์ธ๋ฐ์ค',
+ clearAll: '๋ชจ๋ ๋น์ฐ๊ธฐ',
+ clearConfirmTitle: '์ ๋ง ์ธ๋ฐ์ค๋ฅผ ๋น์ธ๊น์?',
+ clearConfirmDescription: '์ค์๋ผ๋ฉด ํ ์คํธ์์ ์คํ์ทจ์ํ ์ ์์ด์.',
+ clearButton: '๋น์ฐ๊ธฐ',
+ openInboxAriaLabel: '์ธ๋ฐ์ค ์ด๊ธฐ',
+ openInboxTitle: '์ธ๋ฐ์ค',
+ },
+ rightRail: {
+ openQuickControlsAriaLabel: 'Quick Controls ์ด๊ธฐ',
+ openQuickControlsTitle: 'Quick Controls',
+ },
+ paywall: {
+ points: ['ํ๋ฆฌ๋ฏธ์ Scene Packs', 'ํ์ฅ Sound Packs', 'ํ๋กํ ์ ์ฅ / ๋ถ๋ฌ์ค๊ธฐ'],
+ title: 'PRO์์ ๋ ๋ง์ ๊ณต๊ฐ๊ณผ ์ฌ์ด๋๋ฅผ ์ด์ด๋ ์ ์์ด์.',
+ description: '์ ๊ธ ํญ๋ชฉ์ ๋๋ฅธ ์๊ฐ์๋ง ์ด๋ฆฌ๋ ๋๋ฏธ ๊ฒฐ์ ์ํธ์
๋๋ค.',
+ later: '๋์ค์',
+ startPro: 'PRO ์์ํ๊ธฐ',
+ manageTitle: 'PRO ๊ด๋ฆฌ',
+ manageDescription: '๊ฒฐ์ /๋ณต์์ ๋๋ฏธ ๋์์ด๋ฉฐ ์ค์ ์ฐ๋์ ํ์ง ์์์.',
+ openSubscription: '๊ตฌ๋
๊ด๋ฆฌ ์ด๊ธฐ',
+ restorePurchase: '๊ตฌ๋งค ๋ณต์',
+ },
+ statsPanel: {
+ description: '์ค๋ ํ๋ฆ๊ณผ ์ต๊ทผ 7์ผ ๋ฆฌ๋ฌ์ ๊ฐ๋ณ๊ฒ ํ์ธํ์ธ์.',
+ graphPlaceholder: '๊ทธ๋ํ ํ๋ ์ด์คํ๋',
+ },
+ settingsPanel: {
+ reduceMotion: 'Reduce Motion',
+ reduceMotionDescription: 'ํ๋ฉด ์ ํ์ ์กฐ๊ธ ๋ ์ฐจ๋ถํ๊ฒ ํ์ํฉ๋๋ค.',
+ background: '๋ฐฐ๊ฒฝ',
+ backgroundDescription: '๋ชฐ์
์ค์๋ ๋ฐฐ๊ฒฝ scene์ ๋ฐ๊ฟ ์ ์์ด์.',
+ timerPreset: 'ํ์ด๋จธ ํ๋ฆฌ์
',
+ timerPresetDescription: '๊ธฐ๋ณธ ํ๋ฆฌ์
๋ง ๋น ๋ฅด๊ฒ ๊ณ ๋ฅผ ์ ์์ด์.',
+ defaultPreset: '๊ธฐ๋ณธ ํ๋ฆฌ์
',
+ },
+ workspace: {
+ readyToStart: '์ค๋น ์๋ฃ ยท ์์ ๋ฒํผ์ ๋๋ฌ ์ง์ค์ ์์ํด์.',
+ startFailed: '์ธ์
์ ์์ํ์ง ๋ชปํ์ด์. ์ ์ ํ ๋ค์ ์๋ํด ์ฃผ์ธ์.',
+ resumeFailed: '์ธ์
์ ๋ค์ ์์ํ์ง ๋ชปํ์ด์.',
+ abandonFailed: '์ธ์
์ข
๋ฃ๋ฅผ ์๋ฃํ์ง ๋ชปํ์ด์.',
+ pauseFailed: '์ธ์
์ ์ผ์์ ์งํ์ง ๋ชปํ์ด์.',
+ restartFailed: 'ํ์ฌ ํ์ด์ฆ๋ฅผ ๋ค์ ์์ํ์ง ๋ชปํ์ด์.',
+ restarted: 'ํ์ฌ ํ์ด์ฆ๋ฅผ ์ฒ์๋ถํฐ ๋ค์ ์์ํ์ด์.',
+ goalCompleteSyncFailed: 'ํ์ฌ ์ธ์
์๋ฃ๋ฅผ ์๋ฒ์ ๋ฐ์ํ์ง ๋ชปํ์ด์.',
+ nextGoalReady: '๋ค์ ํ ์กฐ๊ฐ ์ค๋น ์๋ฃ ยท ์์ ๋ฒํผ์ ๋๋ฌ ์ด์ด๊ฐ์.',
+ },
+ exitHold: {
+ holdToExitAriaLabel: '๊ธธ๊ฒ ๋๋ฌ ๋๊ฐ๊ธฐ',
+ exit: '๋๊ฐ๊ธฐ',
+ },
+ },
+ soundPlayback: {
+ loadFailed: '์ฌ์ด๋ ํ์ผ์ ๋ถ๋ฌ์ค์ง ๋ชปํ์ด์.',
+ browserDeferred: '๋ธ๋ผ์ฐ์ ๊ฐ ์ฌ์ด๋ ์ฌ์์ ๋ณด๋ฅํ์ด์.',
+ },
+ restart30s: {
+ button: '์จ ๊ณ ๋ฅด๊ธฐ 30์ด',
+ mode: 'BREATHE',
+ toast: '์ ๊น ์จ ๊ณ ๋ฅด๊ณ , ๋ค์ ์ฒ์ฒํ ์์ํด์.',
+ complete: '์ค๋น๋์ด์. ์ง์ค์ผ๋ก ๋์๊ฐ์.',
+ },
+} as const;
diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts
index 7801c06..a2a2e17 100644
--- a/src/store/useAuthStore.ts
+++ b/src/store/useAuthStore.ts
@@ -1,6 +1,7 @@
import { create } from 'zustand';
import Cookies from 'js-cookie';
import { AuthResponse } from '@/features/auth/types';
+import { REFRESH_TOKEN_COOKIE_KEY, TOKEN_COOKIE_KEY } from '@/features/auth/model/constants';
interface AuthState {
accessToken: string | null;
@@ -12,10 +13,6 @@ interface AuthState {
logout: () => void;
}
-// ์ฟ ํค ํค ์์ ์ ์
-const TOKEN_COOKIE_KEY = 'vr_access_token';
-const REFRESH_TOKEN_COOKIE_KEY = 'vr_refresh_token';
-
/**
* VibeRoom ์ ์ญ ์ธ์ฆ(Auth) ์ํ ์ ์ฅ์
*/