고양이hyebin
RSS가 뭐지?
RSS가 뭐지?
September 29, 2025

RSS란?

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. 1.채널 정보 - 블로그 전체에 대한 정보
  • 블로그 제목, URL, 설명
  • 마지막 업데이트 시간
  • 언어 설정 등
  1. 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 피드에 사용될 사이트 기본 정보를 상수로 정의
const SITE_URL = "https://www.hyebin.me";
const SITE_TITLE = "이혜빈의 개발블로그";
const SITE_DESCRIPTION = "개발을 기록하는 개발블로그입니다.";


// XML에서 특수 의미를 가진 문자들(&, <, >, ", ')을 안전한 엔티티로 변환
// 이렇게 하지 않으면 XML 파싱 오류가 발생 가능성이 있음
function escapeXml(text: string): string {
  return text
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

// RSS XML 생성 함수
function generateRssXml(posts: any[]): string {
  const rssItems = posts
    .filter((post) => post.published && post.slug)
    .slice(0, 20)
    .map((post) => {
      const pubDate = new Date(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>${new Date().toUTCString()}</lastBuildDate>
    <atom:link href="${SITE_URL}/rss.xml" rel="self" type="application/rss+xml" />
    ${rssItems}
  </channel>
</rss>`;
}

// 각 글들을 RSS 아이템으로 변환
export async function GET() {
  try {
  // 일반 포스트와 개발 로그를 모두 RSS에 포함시키기 위해 합침
  //Promise.all을 사용한 이유는 getNotionPosts()와 getNotionLogs() 두 함수를 병렬로 실행하기 위함
    const [posts, logs] = await Promise.all([
      getNotionPosts(),
      getNotionLogs()
    ]);
    
    // 포스트와 로그를 합쳐서 날짜순으로 정렬
    const allContent = [...posts, ...logs].sort((a, b) => 
      new Date(b.date).getTime() - new Date(a.date).getTime()
    );
    
    const rssXml = generateRssXml(allContent);

    return new NextResponse(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);
    return new NextResponse("Error generating RSS feed", { status: 500 });
  }
}

robots.txt - 검색 엔진용 설정 파일 생성

///public/robots.txt

User-agent: * //모든 검색엔진(*)에게 모든 페이지(/) 크롤링을 허용
Allow: /

Sitemap: https://www.hyebin.me/sitemap.xml //sitemap 위치를 알려주는 기본적이고 안전한 설정
💡
robots.txt의 역할은 무엇일까요?
  1. 1.크롤링 가이드라인 제공 - 검색엔진에게 어떤 페이지를 크롤링해도 되는지 알려줌
  2. 2.sitemap 위치 알림 - 사이트맵이 어디에 있는지 검색엔진에 알려줌
  3. 3.크롤링 효율성 향상 - 불필요한 페이지 크롤링을 방지해 서버 부하 감소
  4. 4.SEO 모범 사례 - 검색엔진 최적화의 기본 요소

robots.txt는 없어도 되지만 있으면 좋은 이유는 Google 등 주요 검색엔진이 robots.txt를 먼저 확인하기 때문입니다. sitemap.xml 위치를 명시적으로 알려고 향후 특정 경로를 크롤링에서 제외하고 싶을 때도 유용합니다.

제가 쓰는 리더기는 https://feedly.com/i/my/me 입니다. 찾아본 결과 가장 쉽고 유명하더라구요. 심지어 무료로 이용이 가능해요.

위에서 만들어본 RSS 피드를 바로 추가해보겠습니다:-)

피들리 사이트에 들어가서 Follow Sources 를 누르고 팔로우 하기 원하는 rss 링크를 넣어주세요.

Image

만들어 둔 개발 블로그 RSS가 잘 뜨는것을 확인할 수 있습니다! 이제 팔로우 하면 끝!

Image
Image

이제 원하는 블로그들을 하나씩 추가해서 한눈에 글을 파악할 수 있게 되었습니다!

참고

https://namu.wiki/w/RSS