RSS를 정의해 보면, Really Simple Syndication(정말 간단하게 콘텐츠를 한 곳에서 만들고, 여러 곳에서 자동으로 배포·공유하는)라고 할 수 있습니다.
RSS는 어떤 사이트에 새로운 콘텐츠가 올라왔을 때 각각의 해당 사이트에 방문하지 않고, 한 곳에서 콘텐츠를 이용하는 방법인데요, 쉽게 생각하면, 여러 언론사 사이트를 모두 방문할 필요 없이 다양한 기사를 네이버뉴스 한 곳에서 볼 수 있는 것과 같다고 보면 됩니다.
RSS 예시로 뭐가 있을까?
예시로는 블로그가 있습니다. 블로그는 주로 개인이 운영하다보니 업데이트 주기가 길고, 관심이 가는 블로그가 몇 개부터 심하게는 몇백~몇천 개까지 될 수 있어요. 그런데 이러한 블로그들을 모두 즐겨찾기 해 놓고 일일이 새글이 올라왔는지 확인하기란 너무나도 귀찮은 일이므로, RSS리더 한 곳에서 업데이트된 소식만 받을 수 있는 RSS 기능이 쓰이기 시작한 것입니다.
스마트폰에서 이용하면 더욱 편리한데요, 팟캐스트도 RSS를 이용하여 배포하는 방식입니다. 팟캐스트 제작자는 새로운 에피소드(mp3 파일)를 서버에 업로드하고 RSS 피드 파일에 새 에피소드 정보를 추가합니다. 이 RSS 피드에는: 제목, 설명, mp3 파일 위치(URL), 날짜 등이 담겨있는데 구독자 입장에서는 Apple Podcasts, Spotify 등 팟캐스트 앱에서 팟캐스트를 "구독"하게 되는거죠. 이는 실제로는 그 팟캐스트의 RSS 피드 URL을 등록하는 것인데 앱이 주기적으로 RSS 피드를 확인해서 새 에피소드가 있으면 자동으로 알림이 가는것입니다.
현재 국내외 거의 모든 블로그에서 RSS를 지원하고 있으며, 그 외에 일부 언론사나 커뮤니티 사이트에서도 지원하고 있어요. 하지만 대부분의 언론사는 기사 전문을 제공하지 않는데, 그 이유는 페이지뷰와 그를 통한 광고수의 산정에 좋을 게 없기 때문으로 보입니다 😅
간단하게 정리하자면 RSS 구독은 내가 배포자에게서 긁어오는 것 이라고 할 수 있어요.
RSS 피드 만들기
블로그 구독자들이 새 글을 자동으로 받아볼 수 있게 해주는 기능을 만들어 보겠습니다.
기본 구조
RSS 피드는 XML 형식으로 크게 두가지 정보로 나뉘어요.
1.채널 정보 - 블로그 전체에 대한 정보
블로그 제목, URL, 설명
마지막 업데이트 시간
언어 설정 등
2.아이템 목록 - 각각의 글 정보
글 제목과 링크
발행일
간단한 설명
카테고리/태그
RSS 피드 API - 최근 20개 글들을 XML 형식으로 제공
Next.js의 Route Handler를 사용했습니다. /app/rss.xml/route.ts 파일을 만들면 블로그 도메인/rss.xml로 접근할 수 있어요.
import{NextResponse}from"next/server";import{ getNotionPosts, getNotionLogs }from"@/lib/notion";// RSS 피드에 사용될 사이트 기본 정보를 상수로 정의constSITE_URL="https://www.hyebin.me";constSITE_TITLE="이혜빈의 개발블로그";constSITE_DESCRIPTION="개발을 기록하는 개발블로그입니다.";// XML에서 특수 의미를 가진 문자들(&, <, >, ", ')을 안전한 엔티티로 변환// 이렇게 하지 않으면 XML 파싱 오류가 발생 가능성이 있음functionescapeXml(text: string): string {return text
.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'");}// RSS XML 생성 함수functiongenerateRssXml(posts: any[]): string {const rssItems = posts
.filter((post)=> post.published&& post.slug).slice(0,20).map((post)=>{const pubDate =newDate(post.date).toUTCString();const postUrl =`${SITE_URL}/${post.category}/${post.slug}`;return` <item>
<title>${escapeXml(post.title||"Untitled")}</title>
<link>${postUrl}</link>
<guid>${postUrl}</guid>
<description>${escapeXml(post.description||"")}</description>
<pubDate>${pubDate}</pubDate>
<category>${escapeXml(post.category||"post")}</category>
${post.tags?.map((tag: string)=>`<category>${escapeXml(tag)}</category>`).join("\n ")||""} </item>`;}).join("");return`<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>${escapeXml(SITE_TITLE)}</title>
<link>${SITE_URL}</link>
<description>${escapeXml(SITE_DESCRIPTION)}</description>
<language>ko-KR</language>
<lastBuildDate>${newDate().toUTCString()}</lastBuildDate>
<atom:link href="${SITE_URL}/rss.xml" rel="self" type="application/rss+xml" />
${rssItems} </channel>
</rss>`;}// 각 글들을 RSS 아이템으로 변환exportasyncfunctionGET(){try{// 일반 포스트와 개발 로그를 모두 RSS에 포함시키기 위해 합침//Promise.all을 사용한 이유는 getNotionPosts()와 getNotionLogs() 두 함수를 병렬로 실행하기 위함const[posts, logs]=awaitPromise.all([getNotionPosts(),getNotionLogs()]);// 포스트와 로그를 합쳐서 날짜순으로 정렬const allContent =[...posts,...logs].sort((a, b)=>newDate(b.date).getTime()-newDate(a.date).getTime());const rssXml =generateRssXml(allContent);returnnewNextResponse(rssXml,{headers:{"Content-Type":"application/rss+xml; charset=utf-8","Cache-Control":"public, max-age=3600, s-maxage=3600",//브라우저와 CDN에서 1시간(3600초) 동안 캐시},});}catch(error){console.error("Error generating RSS feed:", error);returnnewNextResponse("Error generating RSS feed",{status:500});}}
robots.txt - 검색 엔진용 설정 파일 생성
///public/robots.txtUser-agent:*//모든 검색엔진(*)에게 모든 페이지(/) 크롤링을 허용Allow:/Sitemap: https://www.hyebin.me/sitemap.xml//sitemap 위치를 알려주는 기본적이고 안전한 설정
💡
robots.txt의 역할은 무엇일까요?
1.크롤링 가이드라인 제공 - 검색엔진에게 어떤 페이지를 크롤링해도 되는지 알려줌
2.sitemap 위치 알림 - 사이트맵이 어디에 있는지 검색엔진에 알려줌
3.크롤링 효율성 향상 - 불필요한 페이지 크롤링을 방지해 서버 부하 감소
4.SEO 모범 사례 - 검색엔진 최적화의 기본 요소
robots.txt는 없어도 되지만 있으면 좋은 이유는 Google 등 주요 검색엔진이 robots.txt를 먼저 확인하기 때문입니다. sitemap.xml 위치를 명시적으로 알려고 향후 특정 경로를 크롤링에서 제외하고 싶을 때도 유용합니다.