@@ -22,7 +22,32 @@ import DwellTimeTracker from '@/infra/analytics/components/DwellTimeTracker';
2222import ScrollDepthTracker from '@/infra/analytics/components/ScrollDepthTracker' ;
2323import JsonLd from '@/infra/seo/JsonLd' ;
2424import { getMDXComponents } from '@/blog/ui/mdx/components' ;
25- import { SITE_URL } from '@/site/config/site' ;
25+ import { SITE_AUTHOR , createSiteUrl } from '@/site/config/site' ;
26+
27+ function createPostUrl ( slug : string ) : string {
28+ return createSiteUrl ( `/blog/${ slug } ` ) ;
29+ }
30+
31+ function createPostOgImageUrl ( {
32+ title,
33+ date,
34+ tags,
35+ } : {
36+ title : string ;
37+ date : string ;
38+ tags ?: string [ ] ;
39+ } ) : string {
40+ const params = new URLSearchParams ( {
41+ title,
42+ date,
43+ } ) ;
44+
45+ if ( tags && tags . length > 0 ) {
46+ params . set ( 'tags' , tags . join ( ',' ) ) ;
47+ }
48+
49+ return `${ createSiteUrl ( '/api/og' ) } ?${ params . toString ( ) } ` ;
50+ }
2651
2752export async function generateStaticParams ( ) {
2853 return getAllFeedSlugs ( ) ;
@@ -40,25 +65,45 @@ export async function generateMetadata({
4065 return { title : '글을 찾을 수 없습니다' } ;
4166 }
4267
68+ const postUrl = createPostUrl ( post . slug ) ;
69+ const ogImageUrl = createPostOgImageUrl ( post ) ;
70+ const modifiedTime = post . updated ?? post . date ;
71+
4372 return {
4473 title : post . title ,
4574 description : post . description ,
75+ alternates : {
76+ canonical : postUrl ,
77+ } ,
4678 openGraph : {
4779 title : post . title ,
4880 description : post . description ,
81+ url : postUrl ,
4982 type : 'article' ,
5083 publishedTime : post . date ,
51- authors : [ 'Eunu' ] ,
84+ modifiedTime,
85+ authors : [ SITE_AUTHOR . name ] ,
5286 tags : post . tags ,
5387 images : [
5488 {
55- url : `/api/og?title= ${ encodeURIComponent ( post . title ) } &date= ${ post . date } &tags= ${ post . tags ?. join ( ',' ) || '' } ` ,
89+ url : ogImageUrl ,
5690 width : 1200 ,
5791 height : 630 ,
5892 alt : post . title ,
5993 } ,
6094 ] ,
6195 } ,
96+ twitter : {
97+ card : 'summary_large_image' ,
98+ title : post . title ,
99+ description : post . description ,
100+ images : [
101+ {
102+ url : ogImageUrl ,
103+ alt : post . title ,
104+ } ,
105+ ] ,
106+ } ,
62107 } ;
63108}
64109
@@ -88,6 +133,9 @@ export default async function BlogPostPage({
88133 } ) ;
89134 const readingTimeLabel = post . readingTime ? `약 ${ post . readingTime } 분` : null ;
90135 const mdxComponents = getMDXComponents ( { } ) ;
136+ const postUrl = createPostUrl ( post . slug ) ;
137+ const ogImageUrl = createPostOgImageUrl ( post ) ;
138+ const modifiedTime = post . updated ?? post . date ;
91139
92140 return (
93141 < >
@@ -163,16 +211,25 @@ export default async function BlogPostPage({
163211 data = { {
164212 '@context' : 'https://schema.org' ,
165213 '@type' : 'BlogPosting' ,
214+ mainEntityOfPage : {
215+ '@type' : 'WebPage' ,
216+ '@id' : postUrl ,
217+ } ,
218+ url : postUrl ,
166219 headline : post . title ,
167220 description : post . description ,
168221 author : {
169222 '@type' : 'Person' ,
170- name : 'Eunu' ,
223+ name : SITE_AUTHOR . name ,
224+ url : SITE_AUTHOR . profileUrl ,
225+ sameAs : SITE_AUTHOR . sameAs ,
171226 } ,
172227 datePublished : post . date ,
173- image : [
174- `${ SITE_URL } /api/og?title=${ encodeURIComponent ( post . title ) } &date=${ post . date } &tags=${ post . tags ?. join ( ',' ) || '' } ` ,
175- ] ,
228+ dateModified : modifiedTime ,
229+ image : [ ogImageUrl ] ,
230+ keywords : post . tags ?? [ ] ,
231+ inLanguage : 'ko-KR' ,
232+ isAccessibleForFree : true ,
176233 } }
177234 />
178235
0 commit comments