[팀 개발 문화 발전시키기] RSS 피드를 통한 IT 매체 구독 & Slack App 연동

Hadooboo·2023년 7월 24일
0
post-thumbnail

RSS 피드를 통해 IT 매체를 구독해야겠다고 마음먹은 이유

요즘은 정말 새로운 기술들이 순식간에 발표되고 쏟아져 나오는 것 같다. 이런 매체들으로부터 정보들을 빠르게 얻는 것은 기술을 내 것으로 만드는 것 이전에 변화에 대처하기 위해 필요한 가장 우선되는 소양이 아닐까 싶다.

특히나 정보는 한 곳에서 나오는 것이 아니라 여러 곳에서 등장한다. 그 정보들을 한 곳으로 모아서 편리하게 확인해야 할 책임은 나에게 있다. 쉬우면서 내가 주체적으로 관리할 수 있는 RSS 피드 방식으로 정보들을 모아보고자 하였다.

개선한 내용

RSS 피드 방식을 선택한 이유

RSS(Really Simple Syndication)는 웹사이트의 내용을 구독하기 위한 정형화된 포맷으로 1999년 출시되어 오래된 역사를 가지고 있다. 지금은 여러 SNS에서 내 취향에 맞는 정보의 조각들을 추천 개념으로 제공하기도 하고 네이버 뉴스 홈과 같이 원하는 대로 구독을 할 수 있는 서비스들도 늘어나서 예전만큼 널리 사용되지는 않아 보이지만 여전히 RSS 피드를 제공하는 서비스도 다수 존재한다.

RSS는 내가 데이터를 요청하여 정보를 페치해오는 방식이다. 이를 Pull 방식 이라고 하자. 이메일 알람, 슬랙 봇 알람, 디스코드 봇 알람 등 내가 어디다 정보를 전송해줄지를 알려주면 서비스 제공하는 쪽에서 피드가 새로 생길 때마다 보내주는 방식도 존재한다. 이런 방식을 Push 방식 이라고 하자. 각각의 특징을 비교하면 다음과 같다.

Pull 방식Push 방식
예시RSS이메일 알람, 슬랙 봇, 디스코드 봇 등
효율성업데이트 된 데이터가 없을 때도 주기적으로 변경 사항이 있는지 요청해봐야 함새로운 정보가 추가될 때만 연락을 보내면 되어 효율적임
신속성실시간으로 필요한 정보의 경우 페치 주기에 잘못 걸리면 문제가 생김실시간 정보를 바로 얻을 수 있으나 서비스 제공자가 정보를 즉각적으로 보내줄 지는 통제할 수 없음
구독 목록 즉각적인 수정 가능 여부OX
서비스 제공자에게 내 정보 공개 여부XO

정보를 효율적이고 신속하게 가져올 수 있는 측면에서는 Pull 방식보다는 Push 방식이 나은 면도 많다. 그러나 나는 서비스 제공자들이 내 이메일 주소나 팀의 슬랙 워크스페이스 주소 등을 아는 것을 원하지 않았다. 구독을 취소하고 싶을 때 삭제 요청을 보내도 언제 해줄지도 모른다. 따라서 나는 RSS 피드 방식을 선택하였다.

RSS 피드를 구독하는 데몬 만들기

RSS는 널리 알려진 기술이고 후술할 Slack App과 같은 솔루션을 이용하면 단순히 링크를 복사 붙여넣기 하는 것만으로도 구독을 쉽게 할 수 있다. 그러나 개발자로서 RSS가 어떻게 구성되고 어떤 원리로 Slack RSS App이 동작할지 상상해서 구현해보는 것은 원리를 이해하는 데 필요하고 도움이 되는 과정이었다.

어떤 피드를 구독할까?

첫 번째로, GeekNews 를 선정하였다. 개발자로서 얻을 수 있는 양질의 자료들이 많았다. IT 최근 동향 정보 수합의 목적에 가장 적합하였다.

다음으로, OpenAI Status 를 선정하였다. 얼마 전에 chatgpt 로그인이 계속 에러가 나서 원인을 알아보고자 들어가본 적이 있었다. 이제 내가 먼저 문제가 있는지 상태 정보를 얻고자 하였다.

특히, GeekNews는 Atom, OpenAI Status는 RSS 방식으로 골라 상호 비교해보고자 하였다.

RSS vs. Atom

지금까지는 RSS라는 말만 써왔지만 사실은 웹 서비스에서 피드를 제공하는 한 가지 방식 예시일 뿐이고 이후에 출시된 Atom이라는 상대적으로 더 정교한 규정이 존재한다. 그리고 각각은 특별한 파일 포맷이 아니라 xml이라는 포맷의 형태를 정의하는 방식일 뿐이다.

사용해본 결과 두 포맷의 차이는 동일한 내용을 서로 다른 단어로 정의하는 차이 정도는 있어도 크게는 없어 보였다. 예를 들어, Atom에서는 entry 인 것을 RSS에서는 item 이라고 부르는 것이 있다. Atom이 더 이후에 표준으로 정의되었고 포함할 수 있는 메타데이터가 더 많다는 내용도 보았지만 결국에는 컨텐츠 제공자가 정보를 다 채워준 RSS가 메타데이터가 빠져 있는 Atom보다 낫다. 단순히 Atom이 RSS보다 좋다가 아니라 case by case로 내용을 확인하고 결정하는 것이 필요하다고 생각한다.

이후 RSS 피드라는 단어를 사용할 때는 위와 같은 과정을 거쳐 선택한 Atom 피드, RSS 피드를 통칭하는 의미로 사용할 것이다.

feedparser 사용해보기

python RSS 피드를 파싱하기 위한 de-facto 패키지는 feedparser 이다. 다양한 버전의 RSS, Atom 피드를 모두 파싱할 수 있다.

사용하는 방법도 매우 간단하다.

>>> import feedparser
>>> data = feedparser.parse('https://some_news_site/rss.xml')
>>> data.feed.title

parse 메소드를 한번만 호출하는 것만으로도 데이터를 모두 가져온 뒤 파싱까지 해 둔 상태로 보관한다.

class FeedParserDict(dict):
    keymap = {
        "channel": "feed",
        "items": "entries",
        "guid": "id",
        "date": "updated",
        "date_parsed": "updated_parsed",
        "description": ["summary", "subtitle"],
        "description_detail": ["summary_detail", "subtitle_detail"],
        "url": ["href"],
        "modified": "updated",
        "modified_parsed": "updated_parsed",
        "issued": "published",
        "issued_parsed": "published_parsed",
        "copyright": "rights",
        "copyright_detail": "rights_detail",
        "tagline": "subtitle",
        "tagline_detail": "subtitle_detail",
    }
    ...
        else:
            realkey = self.keymap.get(key, key)
            if isinstance(realkey, list):
                for k in realkey:
                    if dict.__contains__(self, k):
                        return dict.__getitem__(self, k)
            elif dict.__contains__(self, realkey):
                return dict.__getitem__(self, realkey)
    ...

parse 에서 반환되는 값의 타입인 FeedParserDict소스코드를 살펴보면 위와 같은 딕셔너리 키 매핑 부분이 있다. 딕셔너리에서 키를 이용하여 필드를 얻어올 때 realkey = self.keymap.get(key, key) 와 같은 과정을 거치게 하는데, 예를 들어 items 키로 데이터를 찾으려고 했는데 그런 속성이 없었다면 그 대안이 되는 entries 키로 데이터를 다시 찾는 것이다. 이를 통해 여러 버전, 여러 타입의 피드들을 한번에 관리할 수 있다.

아쉬운 점은 위와 같은 이유로 parse 라는 메소드에 타입 시스템을 적용할 수 없다는 것이다. 내가 어떤 필드를 사용하려고 해도 그 필드가 존재하는지 미리 알 수 없기 때문에 구현하는 과정에 비효율적인 부분들이 많았다. 매번 한 단계 depth를 내려갈 때마다 디버그로 속성들을 찍어 보면서 필드를 선택해야 했다.

slack-sdk 사용해보기

feedparser 패키지를 이용하여 일차적으로 데이터 페치는 완료하였고, 이제 그 내용을 내가 확인할 수 있는 채널로 정제하여 보내는 과정이 필요했다. 그 대상으로 슬랙을 선택하였는데, 이전에 다른 프로젝트를 하다가 incoming webhook을 사용해 본 적도 있었고, webhook을 사용하면 url 주소로 http post 요청을 보내는 것만으로도 간단히 메세지를 보낼 수 있기 때문이다.

과정은 다음과 같이 진행된다.

  1. slack app 사이트에 접속해서 slack bot 만들고 권한 설정하기
  2. slack bot incoming webhook 추가하고 생성된 url 확인하기
  3. slack rich message 만들기(참고: https://api.slack.com/messaging/composing/layouts)
  4. python requests 패키지 이용하여 webhook url로 post 요청 보내기

자세한 과정은 단순 코딩과 사용법을 익혀서 사용한 영역이기 때문에 넘어간다.

cronjob 작업을 실행하는 도커 이미지 만들고 배포하기

지금까지 만든 프로그램은 단순히 1회성으로 RSS 피드를 받아와 슬랙 채널로 전송해주는 역할을 하는 스크립트이다. 그런데 중요한 것은 이 프로그램이 계속해서 켜져 있으면서 새로운 정보를 주기적으로 받아와 내게 보내주어야 한다. 내가 계속해서 프로그램을 주기적으로 실행하는 것은 자동화의 목적에 맞지 않다.

이러한 주기적인 작업을 구현하기 위한 방법으로는 다양한 것들이 있을 것이다. python 코드 내에서 타이머나 반복문 등을 이용해 주기적으로 실행이 되게 할 수도 있고, 프로그램 자체를 주기적으로 실행하도록 linux에 내장되어 있는 crontab 같은 서비스에 등록시켜도 될 것이다.

나는 여기서 도커 컨테이너 내부에 cron을 설치하여 스크립트를 주기적으로 돌리는 방법을 선택하였다. 그 이유는 다음과 같다.

  • 도커라이즈를 하고 싶었다. 사실상 모든 프로그램을 서버에서 실행시킬 때 도커라이즈를 거쳐야 안전한 실행 환경을 보장받는다고 생각한다. os 레벨의 crontab으로 돌리는 것은 응용 프로그램이 아닌 os 레벨에 자주 접근하는 간단한 bash shell script 정도의 수준이어야 한다고 생각한다.
  • 그렇다고 프로그램 내부에 타이머와 같은 장치를 두어 주기적으로 실행하게 하는 것은 프로그램이 중단되고 재시작될 때에 대한 유연성이 낮다고 생각했다. 주기를 바꾸고 싶으면 컨테이너 자체와 프로그램 전부를 재시작해야 한다.
  • 도커 컨테이너 자체의 cron을 이용하는 것은 위에서 언급한 문제들을 해결해준다. 프로그램이 계속 켜져 있는 것이 아니기 때문에 설정이나 페치 주기 등을 스크립트가 실행되고 있지 않은 유휴 시간 동안 바꿔놓는다면 다음 cron 주기에서는 그 변경 사항이 컨테이너 재시작 없이도 반영된다. 동시에 컨테이너는 계속해서 켜져 있기 때문에 내가 어떤 작업을 등록해두었는지 목록에서 쉽게 확인이 가능하고, 버저닝도 된다.

python 실행 환경을 위해 base가 Alpine Linux로 되어 있는 이미지를 고르게 되었는데, Alpine Linux는 기본적으로 crontab과 같은 서비스가 등록되어 있지 않다고 한다. 따라서 dcron 이라는 패키지를 apk로 다운받아서 백그라운드로 실행하는 과정을 Dockerfile Entrypoint에 작성하였다.

참고로, 도커 이미지 생성 과정에서 정의될 수 없고 이미지가 실제 컨테이너로 시작될 때 초기에 실행되어야 하는 부분들은 Entrypoint에서 정의해야 한다. 맨날 헷갈리는 Entrypoint vs. CMD의 차이를 쉽게 기억할 수 있는 예시이다. Entrypoint는 말 그대로 도커 컨테이너가 시작될 때 반드시 진행되어야 하는 선작업을 의미하며, docker run과 같은 커맨드로 실행할 경우에 마지막에 붙은 arguments들이 entrypoint 프로그램에 대한 인자가 된다. 그러나 CMD는 기본적으로 어떤 프로그램으로 실행하겠다라고 조금 약하게 선언한 구조로, 원하는 경우 docker run 뒤에 다른 arguments를 붙이면 그 프로그램으로 대신 실행한다.

Slack RSS App 이용하기

데몬 운영의 어려움

RSS 피드를 구독하는 데몬을 다 만들긴 했지만 관리의 측면에서 몇 가지 어려움이 있었다.

  • cronjob으로 주기적으로 실행되는 작업을 계속해서 구동할 서버가 필요한데, 서버에 켜 놓고 24/7 꺼지지 않도록 관리해야 한다.
  • 어디까지 페치했고, 어디서부터 새로운 피드인지 확인하려면 인스턴스의 종료 및 재실행에도 일관성이 유지될 수 있도록 어디까지 구독했는지에 대한 책갈피를 영속적으로 저장해야 한다. 이를 위해서는 데이터베이스 모듈이나 도커 볼륨을 통한 파일시스템 저장 등의 과정이 필요한데, 프로그램의 복잡도가 커진다.
  • 요청 실패에 대한 로그 관리와 이를 내가 알아차리기 위한 프로세스가 필요하다.

이런 운영 상의 복잡한 점들이 있기 때문에 Slack App을 사용하기로 마음먹었다.

Slack App의 장점

우선 슬랙 앱은 내가 회사에 출근해서 자리에 앉아 있는 동안 항상 켜져 있는 메신저 프로그램이다. 모든 정보가 모일 지점으로 슬랙을 선택하는 것은 가장 합리적인 선택이다.

또한, 슬랙 앱을 사용하는 것은 위에서 언급한 운영 상의 문제점을 제거해준다. 슬랙 내부적으로 서버를 운영할테니 위에서 말한 운영 상의 문제점을 핸들링할 책임을 슬랙으로 전가한 셈이다.

마지막으로 슬랙 앱은 동적으로 구독 링크 추가, 삭제도 가능하고 이를 슬랙 채널에서 slash command로 간단히 처리할 수 있어 편리하다.

사용 후기

위에서 RSS 피드 데몬을 만들었던 것과 비교도 안 될 만큼 간단하고 편리했다. 버튼 하나로 슬랙 워크스페이스에 앱을 추가할 수 있었고, /feed subscribe <url> slash command로 데몬으로 만들었던 2개의 링크에 대한 구독을 바로 시작할 수 있었다. 또한, 추가가 워낙 간단하다 보니 golang/go github의 tag release 내용, sbs 뉴스 경제 섹션 도 새로 구독 목록에 더할 수 있었다. 너무 정보가 많이 온다 싶으면 /feed remove <id> slash command로 쉽게 구독 취소도 가능하다.

당연히 단점도 있다. 슬랙 메세지로 오는 포맷을 내 맘대로 구성할 수 없었고, 페치하는 주기도 선택할 수 없었다. 이런 커스터마이징에 불만이 생기면 다시 RSS 피드 데몬을 개선하면 된다. 그러나 RSS 피드는 단순 정보 수합의 목적으로 사용하기 때문에 어떤 포맷으로 오든, 조금 늦게 오든 큰 문제는 없어서 아마 이대로 계속 사용하지 않을까 싶다.

회고

RSS라는 개념을 들어보기만 했었는데 실제 내용과 형식을 확인하고 RSS 피드 데몬을 간단하지만 직접 만들어보는 경험을 한 것도 좋았다. 이런 구현 과정 속에서 한계점을 분석하고 적절한 시점에 솔루션으로 넘어간 것도 나쁘지 않았다.

무엇보다 좋은 것은 이제부터 다양한 정보를 빠르게 습득할 수 있을 것 같은 기분이다. 또한, 내가 구독을 통해 받은 내용을 사원들에게 공유하며 의견을 물어보고 생각들을 들을 수 있었다. 이것이 팀 개발 문화의 발전이 아닐까.

profile
'왜'를 궁금해하는 개발자

1개의 댓글

comment-user-thumbnail
2023년 7월 24일

정보 감사합니다.

답글 달기