같은 링크 프리뷰 기능을 만들다 지쳐서 API로 만들었습니다
Source: Dev.to

지난 몇 년 동안 내가 만든 모든 앱은 같은 기능을 필요로 했다: URL을 붙여넣고 미리보기 카드를 보여주는 것. Slack도 그렇고, Discord도 그렇고, 모든 CMS도 그렇다. 매번 같은 Cheerio 스크래핑 코드를 작성하고, Open Graph 태그와 관련된 동일한 엣지 케이스를 처리하며, Twitter Cards가 property 대신 name을 사용하고 인터넷 절반이 이를 잘못 이해하는 문제를 디버깅해야 했다.
얼마 전, 나는 이 모든 것을 독립형 API로 추출해 RapidAPI에 올렸다. 다른 사람들도 같은 코드를 작성하고 있을 것이라고 생각했다.
실제 반환 내용
URL을 전달하면 6개의 레이어에 걸친 구조화된 메타데이터를 반환합니다:
- Open Graph – 제목, 설명, 차원 정보가 포함된 이미지, 기사 메타데이터
- Twitter Cards – 카드 유형, 사이트, 작성자; 실제로 존재하는 태그만 포함하고, 대체 추정은 하지 않음
- HTML meta – title 태그, 메타 설명, canonical, 테마 색상
- Icons – 우선 순위 체인에 따라 가장 고품질의 파비콘을 자동 선택
- Feeds – RSS, Atom, JSON Feed 링크를 탐색
- JSON‑LD – 모든 script 블록을 파싱하고,
BreadcrumbList보다Article/Product를 우선시
응답은 병합된 최상위 뷰(제목은 존재하는 소스 중 첫 번째를 사용; OG → Twitter → title 태그 순)와 원시 파싱 레이어를 모두 제공하므로, 필요에 따라 직접 로직을 적용할 수 있습니다.
{
"title": "GitHub · Build and ship software on a single, collaborative platform",
"description": "Join the world's most widely adopted...",
"image": {
"url": "https://github.githubassets.com/images/modules/site/social-cards/campaign-social.png",
"width": 1200,
"height": 630
},
"favicon": "https://github.githubassets.com/favicons/favicon.svg",
"siteName": "GitHub",
"type": "website",
"themeColor": "#1e2327",
"openGraph": { ... },
"twitter": { ... },
"feeds": [],
"jsonLd": { "@type": "WebSite", ... },
"responseTime": 234
}
올바르게 처리하기 번거로운 부분들
-
OG 태그는
property를 사용하고, 트위터는name을 사용합니다.
Open Graph 사양은이라고 명시하고, Twitter Cards는이라고 명시합니다. 많은 사이트가 이를 뒤바꾸어 사용하기 때문에 파서는 두 접두사에 대해 두 속성을 모두 확인합니다. -
여러 개의
og:image태그가 유효합니다.
OG 사양은 태그를 반복해서 배열을 지원합니다.og:image:width와 같은 구조화된 속성은 가장 최근에 선언된og:image에 적용됩니다. 대부분의 스크래퍼는 첫 번째 이미지만 가져오고 나머지는 무시합니다. -
JSON‑LD 블록은 엉망입니다.
일반적인 뉴스 기사 페이지에는 여러 개의 JSON‑LD 블록이 포함될 수 있습니다(예:BreadcrumbList,Organization, 실제Article등). 모든 블록을 파싱한 뒤 올바른 것을 선택해야 합니다. -
파비콘에는 우선순위가 있습니다.
180×180 크기의 Apple 터치 아이콘이 보통 가장 높은 품질이며, 다음으로 32×32 표준 아이콘, 그 뒤에 일반적인/favicon.ico가 뒤따릅니다. 대부분의 구현은 찾은 첫 번째 “만 가져옵니다. -
URL이 어디에나 상대 경로로 존재합니다.
OG 이미지와 피드 링크는 종종 상대 경로입니다. 이를 올바르게 해석하려면 리다이렉트 후의 최종 URL을 기준으로 삼아야 합니다.
기술적 접근 방식
서비스는 VPS에 호스팅된 Fastify 서버에서 실행되며, HTML 파싱을 위해 Cheerio를 사용합니다. 헤드리스 브라우저나 Puppeteer는 사용하지 않고, HTML을 가져와 파싱만 수행하여 캐시 미스 시 응답 시간을 500 ms 이하, 캐시 히트 시 5 ms 이하로 유지합니다.
SSRF 방어가 가장 많은 시간을 소요했습니다. API가 임의의 URL을 받아들이기 때문에, 먼저 호스트명을 해석하고, 결과 IP를 사설 대역 차단 리스트와 비교한 뒤, DNS 재바인딩 공격을 방지하기 위해 해석된 IP에 직접 연결합니다.
같은 인프라에 Text Analytics API도 구축했습니다: 텍스트를 전달하면 가독성 점수(Flesch‑Kincaid, Coleman‑Liau, SMOG 등), 키워드 밀도, 바이그램, 트라이그램, 예상 독서 시간을 반환합니다. 문자열에 대한 순수 수학 연산만 수행하며, 응답 시간은 10 ms 미만으로, 콘텐츠 최적화 도구와 작문 보조기에 유용합니다.
사용해 보기
Both APIs are on RapidAPI with a free tier (500 requests/month):
- LinkPreview – URL 메타데이터 추출
- TextAnalytics – 가독성 점수, 키워드 밀도, 텍스트 메트릭
무료 티어는 테스트와 프로토타이핑에 충분합니다. 파서가 잘 처리하지 못하는 엣지 케이스를 만나면 진심으로 알려 주세요—인터넷의 거친 HTML을 파싱하는 일은 영원한 프로젝트입니다.