11import React , { useState , useEffect } from 'react' ;
2- import { MemoizedRichText } from '../MemoizedComponents' ;
32import {
43 link ,
54 card ,
65 content ,
6+ previewContainer ,
77 previewImage ,
88 title ,
99 description ,
1010 siteName ,
11- caption ,
11+ favicon ,
12+ urlText ,
1213} from './styles.css' ;
13- import { RichTextItem } from '../RichText/RichTexts' ;
1414
1515interface OpenGraphData {
1616 title : string ;
1717 description : string ;
1818 image : string ;
1919 siteName : string ;
20+ url : string ;
21+ favicon ?: string ;
2022}
2123
2224export interface BookmarkProps {
2325 url : string ;
24- caption ?: RichTextItem [ ] ;
2526}
2627
27- // 실제 프로덕션에서는 서버 사이드에서 처리하거나 전용 API를 사용해야 합니다
28+ // OpenGraph 데이터를 가져오는 함수
2829const fetchOpenGraphData = async ( url : string ) : Promise < OpenGraphData > => {
2930 try {
31+ const apiUrl = `https://api.microlink.io/?url=${ encodeURIComponent ( url ) } ` ;
32+ const response = await fetch ( apiUrl ) ;
33+ const data = await response . json ( ) ;
34+
35+ if ( ! response . ok ) {
36+ throw new Error ( 'Failed to fetch metadata' ) ;
37+ }
38+
39+ const { status, data : metaData } = data ;
40+
41+ if ( status !== 'success' ) {
42+ throw new Error ( 'API returned error status' ) ;
43+ }
44+
3045 const parsedUrl = new URL ( url ) ;
3146 const domain = parsedUrl . hostname ;
32- const siteName = domain . split ( '.' ) [ 1 ] || domain ;
3347
34- // 임시로 더미 데이터를 반환
3548 return {
36- title : domain ,
37- description : 'No description available' ,
38- image : '' ,
39- siteName : siteName ,
49+ title : metaData . title || domain ,
50+ description : metaData . description || 'No description available' ,
51+ image : metaData . image ?. url || '' ,
52+ siteName : metaData . publisher || domain ,
53+ url : metaData . url || '' ,
54+ favicon :
55+ metaData . logo ?. url ||
56+ `https://www.google.com/s2/favicons?domain=${ domain } &sz=64` ,
4057 } ;
41- } catch {
58+ } catch ( error ) {
59+ console . error ( 'Error fetching OpenGraph data:' , error ) ;
60+
61+ const parsedUrl = new URL ( url ) ;
62+ const domain = parsedUrl . hostname ;
63+
4264 return {
43- title : url ,
44- description : 'Invalid URL ' ,
65+ title : domain ,
66+ description : 'No description available ' ,
4567 image : '' ,
46- siteName : 'Unknown' ,
68+ url : '' ,
69+ siteName : domain ,
70+ favicon : `https://www.google.com/s2/favicons?domain=${ domain } &sz=64` ,
4771 } ;
4872 }
4973} ;
5074
51- const Bookmark : React . FC < BookmarkProps > = ( { url, caption } ) => {
75+ const Bookmark : React . FC < BookmarkProps > = ( { url } ) => {
5276 const [ ogData , setOgData ] = useState < OpenGraphData | null > ( null ) ;
5377 const [ error , setError ] = useState ( false ) ;
5478
@@ -68,28 +92,33 @@ const Bookmark: React.FC<BookmarkProps> = ({ url, caption }) => {
6892 return (
6993 < a href = { url } target = "_blank" rel = "noopener noreferrer" className = { link } >
7094 < div className = { card } >
71- { ogData ?. image && (
72- < img
73- className = { previewImage }
74- src = { ogData . image }
75- alt = { ogData . title }
76- loading = "lazy"
77- />
78- ) }
7995 < div className = { content } >
80- < h4 className = { title } > { ogData ?. title || url } </ h4 >
81- { ogData ?. description && (
82- < p className = { description } > { ogData . description } </ p >
83- ) }
84- { ogData ?. siteName && (
85- < div className = { siteName } > { ogData . siteName } </ div >
86- ) }
87- { /* {caption && caption.length > 0 && (
88- <div className={caption}>
89- <MemoizedRichText richTexts={caption} />
90- </div>
91- )} */ }
96+ < div >
97+ < h4 className = { title } > { ogData ?. title || url } </ h4 >
98+ < p className = { description } > { ogData ?. description || '' } </ p >
99+ </ div >
100+ < div className = { siteName } >
101+ { ogData ?. favicon && (
102+ < img src = { ogData . favicon } alt = "" className = { favicon } />
103+ ) }
104+ < span className = { urlText } > { ogData ?. url || '' } </ span >
105+ </ div >
92106 </ div >
107+ { ogData ?. image && (
108+ < div className = { previewContainer } >
109+ < img
110+ className = { previewImage }
111+ src = { ogData . image }
112+ alt = { ogData . title }
113+ loading = "lazy"
114+ onError = { ( e ) => {
115+ // 이미지 로드 실패 시 처리
116+ const target = e . target as HTMLImageElement ;
117+ target . style . display = 'none' ;
118+ } }
119+ />
120+ </ div >
121+ ) }
93122 </ div >
94123 </ a >
95124 ) ;
0 commit comments