Files
blog-frontend/src/app/sitemap.ts
wypark 0273cae6e4
All checks were successful
Deploy blog-frontend / build-and-deploy (push) Successful in 2m30s
.
2026-05-28 21:45:30 +09:00

61 lines
1.7 KiB
TypeScript

import { MetadataRoute } from 'next';
interface SitemapPost {
slug: string;
updatedAt?: string;
createdAt?: string;
}
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const baseUrl = 'https://blog.wypark.me';
// API 주소 환경변수 사용 (없으면 로컬)
const apiUrl = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8080';
// 1. 고정된 정적 페이지들
const routes: MetadataRoute.Sitemap = [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 1,
},
{
url: `${baseUrl}/archive`,
lastModified: new Date(),
changeFrequency: 'daily',
priority: 0.8,
},
];
try {
// 2. API에서 게시글 목록 가져오기
const response = await fetch(`${apiUrl}/api/posts?size=1000&sort=createdAt,desc`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
next: { revalidate: 3600 }
});
if (!response.ok) {
throw new Error('Failed to fetch posts');
}
const json = await response.json() as { data?: { content?: SitemapPost[] } };
const posts = json.data?.content || [];
// 3. 게시글 데이터를 사이트맵 형식으로 변환
const postRoutes = posts.map((post) => ({
// 🛠️ 핵심 수정: slug를 encodeURIComponent로 감싸서 특수문자(&, 한글 등)를 안전하게 처리합니다.
url: `${baseUrl}/posts/${encodeURIComponent(post.slug)}`,
lastModified: new Date(post.updatedAt || post.createdAt || Date.now()),
changeFrequency: 'weekly' as const,
priority: 0.8,
}));
return [...routes, ...postRoutes];
} catch (error) {
console.error('Sitemap generation error:', error);
return routes;
}
}