슬랙으로 RSS 알림 보내기 (3) - RSS 피드 가져오기

2025. 2. 23. 21:37Nodejs

반응형

지난 글에서는 서버리스 프레임워크를 사용하여 프로젝트를 생성하고, 간단한 샘플 코드를 작성하여 실행해 보았습니다. 이제부터 본격적으로 기능을 구현해 나가겠습니다. 이번 글에서는 RSS 피드를 가져오는 부분에 대해 살펴보겠습니다.

  1. 프로젝트 설정
  2. RSS 피드 가져오기
  3. 슬랙에 메시지 전송하기
  4. 중복 알림 방지하기
  5. 배포

1. 레포지토리 목록 관리

먼저, RSS 피드를 가져올 레포지토리 목록을 준비해야 합니다. 단순히 코드에서 변수로 선언하여 관리할 수도 있지만, 더 편리하게 관리하기 위해 CSV 파일을 활용하려고 합니다. src/data 폴더 아래에 github-repo.csv 파일을 생성하고, 아래와 같이 작성합니다.

 

[src/data/github-repo.csv]

packageName,sourceUrl,slackChannelId
PNPM,https://github.com/pnpm/pnpm/releases.atom,C0123L3210J
Node.js,https://github.com/nodejs/node/releases.atom,C1234L56S7J

 

  • packageName : 패키지명입니다. 메시지나 에러에서 식별하기 위해서 사용합니다.
  • sourceUrl : 릴리즈 알림을 받을 GitHub 레포지토리 주소에 release.atom을 추가하면 됩니다.
  • slackChannelId : 알림을 받을 슬랙 채널의 ID입니다.

2. 레포지토리 목록 가져오기

이제 github-repo.csv 파일을 읽어서 Javascript 객체로 변환해야 합니다. 이를 위해 csv-parser 라이브러리를 사용합니다.

$ pnpm add csv-parser

 

src/service 폴더 아래에 github-release-notification.ts 파일을 생성하고, GitHubReleaseNotificationService 클래스를 선언합니다. 이 클래스에서 주요 비즈니스 로직을 관리하게 될 것입니다. 주요 로직의 흐름은 run 함수에 정리해 두었으며, 이제 하나씩 구현해 나가도록 하겠습니다.

 

[src/service/github-release-notification.ts]

export class GitHubReleaseNotificationService {
  async run(): Promise<void> {
    /**
      * TODO
      * RSS 피드 조회
      * 중복 알림 제거
      * 알림 전송
      * 알림 내역 저장
      */
  }
}

 

CSV 파일을 읽어 Javascript 객체로 변환하는 loadFeedSources 함수를 추가하고, 이를 run 함수에서 활용하도록 수정합니다.

 

[src/service/github-release-notification.ts]

import * as fs from 'fs';
import csvParser from 'csv-parser';


export type FeedSource = {
  packageName: string;
  sourceUrl: string;
  slackChannelId: string;
}

export class GitHubReleaseNotificationService {
  private async loadFeedSources(): Promise<FeedSource[]> {
    const feedSources: FeedSource[] = [];
    const stream = fs.createReadStream("src/data/github-repo.csv", "utf-8").pipe(csvParser());
    for await (const data of stream) {
      feedSources.push(data);
    }
    return feedSources;
  }
	
  async run(): Promise<void> {
    const feedSources = await this.loadFeedSources();
    for await (const feedSource of feedSources) {
      /**
        * TODO
        * RSS 피드 조회
        * 중복 알림 제거
        * 알림 전송
        * 알림 내역 저장
        */
    }
  }
}

3. RSS 피드 조회

RSS 피드를 조회하기 위해 rss-parser 라이브러리를 사용합니다. rss-parser는 RSS 피드를 파싱하여 Javascript 객체로 변환하는 라이브러리입니다.

$ pnpm add rss-parser

 

 

이 라이브러리는 여러 서비스에서 반복적으로 사용되므로, 별도의 서비스로 분리하여 싱글톤 패턴으로 관리하겠습니다. src/service 폴더 아래에 rss-parse.service.ts 를 만들고 아래와 같이 코드를 작성합니다.

 

[src/service/rss-parse.service.ts]

import RSSParser from 'rss-parser';

export const rssParser = new RSSParser();

 

rss-parser가 반환하는 값은 모든 속성이 옵셔널이므로, 누락된 값이 없는지 검사하는 함수를 추가합니다. src/lib 폴더 아래에 validator.ts 파일을 추가하고 아래와 같이 작성합니다.

 

[src/lib/validator.ts]

export const isFullyFilled = <T extends Record<string, unknown>>(
  obj: T
): obj is Required<T> => {
  return Object.values(obj).every((value) => !isNil(value));
};

 

 

이제 feedSource의 sourceUrl을 통해 RSS 피드를 조회하고, Javascript 객체로 반환하는 parseRSS 함수를 추가하고, 이를 run 함수에 활용하도록 수정합니다.

 

[src/service/github-release-notification.ts]

...
// add
import { rssParser } from './rss-parse.service';
import { isFullyFilled } from '../lib/validator';

...

// add
export type FeedItem = {
  id: string;
  version: string;
  link: string;
  date: string;
}

export class GitHubReleaseNotificationService {
  ...
	
  // add
  private async parseRSS(sourceUrl: string): Promise<FeedItem[]> {
    const feed = await rssParser.parseURL(sourceUrl);
    const items = feed.items.reduce<FeedItem[]>((acc, cur) => {
      // feed.items 배열의 요소들은 모든 속성이 옵셔널이므로,
      // 유효한 데이터만 처리할 수 있도록 검증합니다.
      if (isFullyFilled(cur)) {
        const { title, link, isoDate } = cur;
        acc.push({
          id: link,
          version: title,
          link,
          date: isoDate
        });
      }
      return acc;
    }, []);
    return items;
  }
	
  async run(): Promise<void> {
    const feedSources = await this.loadFeedSources();
    for await (const feedSource of feedSources) {
      const { packageName, sourceUrl, slackChannelId } = feedSource;
      
      // RSS 피드 조회
      const items = await this.parseRSS(sourceUrl);
      
      // 테스트를 위해 items를 출력해보겠습니다.
      console.log(items);
      
      // 중복 알림 제거
      // 알림 전송
      // 알림 내역 저장
    }
  }
}

 

지금까지 작성한 코드를 테스트하기 위해서 src/handler.ts 파일과 serverless.yml 파일의 functions 부분을 아래와 같이 수정합니다.

 

[src/handler.ts]

import { GitHubReleaseNotificationService } from "./service/github-release-notification.service";

export const notifyAll = () => {
  const gitHubReleaseNotificationService = new GitHubReleaseNotificationService();
  await gitHubReleaseNotificationService.run();
}

 

[serverless.yml]

...

functions:
  notifyAll:
    handler: src/handler.notifyAll
    events:
      - schedule: rate(1 hour)

 

이제 sls invoke 명령어를 사용하여 배포한 함수를 실행할 수 있습니다. 아래 명령어를 실행하면, 가져온 RSS 피드가 출력되는 것을 확인할 수 있습니다. 참고로, sls invoke local은 배포된 함수가 아닌 로컬 환경에서 작성한 함수를 실행하는 명령어입니다.

$ sls invoke local --function notifyAll

 

 

다음 글에서는

이번 글에서는 rss-parser 라이브러리를 활용하여 RSS 피드를 조회하고, 가져온 데이터를 원하는 형태로 변환하는 과정을 다뤘습니다. 또한, 여러 개의 RSS 피드 소스를 효율적으로 관리하기 위해 CSV 파일을 만들고, csv-parser를 활용하여 해당 파일을 읽어오는 기능을 구현했습니다. 다음 글에서는 가져온 데이터를 슬랙으로 전송하는 기능을 구현해 보겠습니다.

 

2025.02.15 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (1) - 개요

2025.02.19 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (2) - 프로젝트 설정

2025.02.23 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (3) - RSS 피드 가져오기

2025.02.26 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (4) - 슬랙에 메시지 전송하기

2025.03.01 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (5) - 중복 알림 방지하기

2025.03.03 - [Nodejs] - 슬랙으로 RSS 알림 보내기 (6) - 배포

반응형