From 51ccdf1ce3658e4cffead47a11c064059744624b Mon Sep 17 00:00:00 2001 From: kimminna Date: Thu, 16 Jul 2026 14:14:17 +0900 Subject: [PATCH] =?UTF-8?q?feat(web):=20robots.txt=C2=B7sitemap.xml=20SEO?= =?UTF-8?q?=20=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EC=84=A4=EC=A0=95=20(#250)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 배포 도메인을 SITE_URL 공유 상수로 분리했습니다 - 인증이 필요한 비공개 라우트를 크롤링에서 제외하는 robots.txt를 추가했습니다 - 공개 라우트와 로케일별 hreflang alternates를 포함한 sitemap.xml을 추가했습니다 --- apps/timo-web/app/[locale]/layout.tsx | 3 +-- apps/timo-web/app/robots.ts | 30 +++++++++++++++++++++++++++ apps/timo-web/app/sitemap.ts | 24 +++++++++++++++++++++ apps/timo-web/constants/site.ts | 1 + 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 apps/timo-web/app/robots.ts create mode 100644 apps/timo-web/app/sitemap.ts create mode 100644 apps/timo-web/constants/site.ts diff --git a/apps/timo-web/app/[locale]/layout.tsx b/apps/timo-web/app/[locale]/layout.tsx index 2d70ca73..1172807c 100644 --- a/apps/timo-web/app/[locale]/layout.tsx +++ b/apps/timo-web/app/[locale]/layout.tsx @@ -6,6 +6,7 @@ import { getTranslations, setRequestLocale } from "next-intl/server"; import type { Metadata } from "next"; +import { SITE_URL } from "@/constants/site"; import { routing } from "@/i18n/routing"; import { AuthProvider } from "@/providers/auth/AuthProvider"; import { LanguageSyncProvider } from "@/providers/locale/LanguageSyncProvider"; @@ -19,8 +20,6 @@ const pretendard = localFont({ display: "swap", }); -const SITE_URL = "https://timo.kr"; - const OG_LOCALE: Record<(typeof routing.locales)[number], string> = { en: "en_US", ko: "ko_KR", diff --git a/apps/timo-web/app/robots.ts b/apps/timo-web/app/robots.ts new file mode 100644 index 00000000..010dd897 --- /dev/null +++ b/apps/timo-web/app/robots.ts @@ -0,0 +1,30 @@ +import type { MetadataRoute } from "next"; + +import { ROUTES } from "@/constants/routes"; +import { SITE_URL } from "@/constants/site"; +import { routing } from "@/i18n/routing"; + +const PRIVATE_ROUTES = [ + ROUTES.ONBOARDING, + ROUTES.HOME, + ROUTES.TODAY, + ROUTES.FOCUS, + ROUTES.STATISTICS, + ROUTES.SETTINGS, +] as const; + +const disallow = routing.locales.flatMap((locale) => [ + ...PRIVATE_ROUTES.map((route) => `/${locale}${route}`), + `/${locale}/oauth`, +]); + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + disallow, + }, + sitemap: `${SITE_URL}/sitemap.xml`, + }; +} diff --git a/apps/timo-web/app/sitemap.ts b/apps/timo-web/app/sitemap.ts new file mode 100644 index 00000000..d040237d --- /dev/null +++ b/apps/timo-web/app/sitemap.ts @@ -0,0 +1,24 @@ +import type { MetadataRoute } from "next"; + +import { ROUTES } from "@/constants/routes"; +import { SITE_URL } from "@/constants/site"; +import { routing } from "@/i18n/routing"; + +const PUBLIC_ROUTES = ["", ROUTES.LOGIN, ROUTES.POLICY] as const; + +export default function sitemap(): MetadataRoute.Sitemap { + const lastModified = new Date(); + + return PUBLIC_ROUTES.map((route) => ({ + url: `${SITE_URL}/${routing.defaultLocale}${route}`, + lastModified, + alternates: { + languages: Object.fromEntries( + routing.locales.map((locale) => [ + locale, + `${SITE_URL}/${locale}${route}`, + ]), + ), + }, + })); +} diff --git a/apps/timo-web/constants/site.ts b/apps/timo-web/constants/site.ts new file mode 100644 index 00000000..fc19d7f6 --- /dev/null +++ b/apps/timo-web/constants/site.ts @@ -0,0 +1 @@ +export const SITE_URL = "https://timo.kr";