From c32f03a588bd1f5d19e61ee7f6a373f5d4d21282 Mon Sep 17 00:00:00 2001 From: choiboa Date: Mon, 19 Jan 2026 23:32:06 +0900 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=20SEO=20=EB=B0=8F=20?= =?UTF-8?q?=ED=8D=BC=ED=8F=AC=EB=A8=BC=EC=8A=A4=20=EC=B5=9C=EC=A0=81?= =?UTF-8?q?=ED=99=94=20=EC=B2=B4=EA=B3=84=20=EA=B5=AC=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../(shell)/(category)/[category]/page.tsx | 48 +++++++++++++++++-- src/app/(layout)/(shell)/page.tsx | 2 +- src/app/(layout)/posts/[slug]/page.tsx | 9 +++- src/app/layout.tsx | 21 ++++++-- src/app/robots.ts | 13 +++++ src/app/sitemap.ts | 28 +++++++++++ 6 files changed, 111 insertions(+), 10 deletions(-) create mode 100644 src/app/robots.ts create mode 100644 src/app/sitemap.ts diff --git a/src/app/(layout)/(shell)/(category)/[category]/page.tsx b/src/app/(layout)/(shell)/(category)/[category]/page.tsx index 4906d03..d8d4976 100644 --- a/src/app/(layout)/(shell)/(category)/[category]/page.tsx +++ b/src/app/(layout)/(shell)/(category)/[category]/page.tsx @@ -1,16 +1,54 @@ import CategoryPage from "@/app/(layout)/(shell)/(category)/[category]/_components/CategoryPage"; import { CATEGORY_CONFIG, CategorySlug } from "@/config/categories"; - import { SearchParams } from "@/types/route.types"; +import { Metadata } from "next"; import { notFound } from "next/navigation"; +type CategoryPageProps = { + params: Promise<{ category: string }>; + searchParams: Promise; +}; + +export async function generateMetadata( + { params }: Pick +): Promise { + const { category } = await params; + if (!(category in CATEGORY_CONFIG)) { + return {}; + } + + const slug = category as CategorySlug; + const config = CATEGORY_CONFIG[slug]; + + const baseUrl = "https://b0o0a.com"; + const url = `${baseUrl}/category/${slug}`; + + const title = config.title; + const description = + config.description ?? ""; + + return { + title, + description, + alternates: { + canonical: url, + }, + openGraph: { + type: "website", + url, + title, + description, + }, + twitter: { + card: "summary_large_image", + }, + }; +} + export default async function CategoryRoutePage({ params, searchParams, -}: { - params: Promise<{ category: string }>; - searchParams: Promise; -}) { +}: CategoryPageProps) { const { category } = await params; const slug = category as CategorySlug; diff --git a/src/app/(layout)/(shell)/page.tsx b/src/app/(layout)/(shell)/page.tsx index 291d1d5..5e3ece6 100644 --- a/src/app/(layout)/(shell)/page.tsx +++ b/src/app/(layout)/(shell)/page.tsx @@ -24,7 +24,7 @@ export default async function HomePage({ return (
- + ; + params: Promise; }; +export function generateStaticParams() { + return velitePosts.map((post) => ({ + slug: post.slug, + })) +} + export async function generateMetadata({ params, }: PageProps): Promise { diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ae245ab..87ec62a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,15 +1,30 @@ +import { Analytics } from "@vercel/analytics/react"; import { Metadata } from "next"; import { ThemeProvider } from "next-themes"; import localFont from "next/font/local"; import "./globals.css"; -import { Analytics } from "@vercel/analytics/react"; export const metadata: Metadata = { - title: "B-log", - description: "boa's dev blog", + metadataBase: new URL("https://b0o0a.com"), + title: { + default: "B-log", + template: "%s | B-log", + }, + description: "FE 개발자 최보아의 프로젝트, 기술 인사이트, 개발 기록 블로그", icons: { icon: "/favicon.svg", }, + twitter: { + card: "summary_large_image", + images: ["/post-fallback.png"], + }, + openGraph: { + type: "website", + siteName: "B-log", + }, + alternates: { + canonical: "/", + }, }; const pretendard = localFont({ diff --git a/src/app/robots.ts b/src/app/robots.ts new file mode 100644 index 0000000..cfc2e4c --- /dev/null +++ b/src/app/robots.ts @@ -0,0 +1,13 @@ +import type { MetadataRoute } from "next"; + +export default function robots(): MetadataRoute.Robots { + const baseUrl = "https://b0o0a.com"; + + return { + rules: { + userAgent: "*", + allow: "/", + }, + sitemap: `${baseUrl}/sitemap.xml`, + }; +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 0000000..a624437 --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,28 @@ +import type { MetadataRoute } from "next"; +import { velitePosts } from "@/lib/posts"; +import { CATEGORY_CONFIG } from "@/config/categories"; + +export default function sitemap(): MetadataRoute.Sitemap { + const baseUrl = "https://b0o0a.com"; + + const posts = velitePosts + .filter((post) => !post.draft) + .map((post) => ({ + url: `${baseUrl}/posts/${post.slug}`, + lastModified: new Date(post.date), + })); + + const categories = Object.values(CATEGORY_CONFIG).map((category) => ({ + url: `${baseUrl}/category/${category.slug}`, + lastModified: new Date(), + })); + + const staticPages: MetadataRoute.Sitemap = [ + { + url: baseUrl, + lastModified: new Date(), + }, + ]; + + return [...staticPages, ...categories, ...posts]; +}