n8n RSS 필터링 자동화 – 불필요한 기사 제외하는 방법

매일 쏟아지는 RSS 기사, 일일이 걸러내기 지친다면

여러 뉴스 사이트에서 RSS 피드를 수집하는 자동화를 만들었는데, 막상 돌려보니 문제가 생겼다. [포토], [인사], [부음] 같은 기사가 섞여서 들어오는 거다. 정작 필요한 건 본문 기사인데 매번 수동으로 지우는 게 번거로웠다. n8n RSS 필터링 로직을 직접 만들어서 해결한 과정을 정리했다.

n8n RSS 필터링 자동화 – 불필요한 기사 제외하는 방법 관련 이미지
n8n RSS 필터링 자동화 – 불필요한 기사 제외하는 방법 관련 이미지

왜 RSS 피드에는 불필요한 기사가 섞여 들어올까

RSS 피드는 기본적으로 해당 사이트의 최신 기사를 전부 내려준다. 카테고리별로 구분해서 주는 경우도 있지만,

대부분의 종합 피드는 포토 뉴스, 인사 발령, 부고 기사까지 전부 포함된다. 피드 자체에서 필터링 옵션을 제공하지 않기 때문에, 수신하는 쪽에서 직접 걸러내는 수밖에 없다.

결국 제목에 특정 키워드가 포함됐는지 확인하고, 해당 기사를 제외하는 로직을 만들어야 했다.

n8n에서는 Code 노드를 활용하면 이런 필터링을 자유롭게 구현할 수 있다.

n8n RSS 필터링 워크플로우 전체 흐름

이번에 구성한 워크플로우는 다음 순서로 동작한다.

첫 번째, RSS 피드 URL 목록을 배열로 구성한다. 여러 사이트를 수집한다면 URL을 하나씩 하드코딩하는 것보다 배열로 관리하는 게 유지보수에 좋다.

두 번째, HTTP Request 노드로 각 URL에 GET 요청을 보낸다. n8n의 RSS Read 노드도 있지만, 여러 피드를 동적으로 처리하려면 HTTP Request가 더 유연하다.

세 번째, Code 노드에서 XML을 파싱한다. RSS 피드는 XML 형식으로 오기 때문에 JSON으로 변환하는 과정이 필요하다.

네 번째, HTML 엔티티 디코딩을 처리한다. 제목에 &, < 같은 인코딩된 문자가 있으면 정상적으로 변환해준다.

다섯 번째, 제목 기반 필터링을 적용한다. [포토], [인사], [부음], [부고] 같은 패턴이 포함된 기사를 제외한다.

여섯 번째, 필터를 통과한 기사만 다음 노드로 넘긴다.

n8n 설치가 처음이라면 WSL 설치부터 첫 워크플로우까지 가이드를 먼저 참고하면 좋다.

핵심 코드: 제목 기반 필터링 로직

Code 노드에서 사용한 필터링 코드는 다음과 같다.

const excludePatterns = ['[포토]', '[인사]', '[부음]', '[부고]'];

const filtered = articles.filter(article => {
  return !excludePatterns.some(pattern => article.title.includes(pattern));
});

return filtered.map(article => ({ json: article }));

excludePatterns 배열에 제외할 키워드를 정의한다.

some() 메서드로 제목에 해당 패턴이 하나라도 포함되면 true를 반환하고, 그 기사는 필터링된다.

느낌표(!)로 반전시켜서 패턴이 없는 기사만 통과시키는 구조다.

나중에 제외할 키워드가 늘어나면 excludePatterns 배열에 추가만 하면 된다. [속보], [단독] 같은 패턴을 오히려 포함시키고 싶다면 반대 로직으로 바꿀 수도 있다.

XML 파싱과 HTML 엔티티 디코딩 처리

RSS 피드는 XML 형식이라 그대로 사용할 수 없다. Code 노드에서 XML을 JSON으로 변환해야 한다.

n8n의 Code 노드는 기본적으로 xml2js 같은 라이브러리를 직접 사용할 수 없어서, 간단한 정규식 파싱이나 외부 Function 노드를 활용한다.

HTML 엔티티 디코딩도 중요하다. RSS 피드의 제목에 &가 &로 표시되는 경우가 많다. 이걸 처리하지 않으면 필터링 조건에서 정확히 매칭되지 않을 수 있다.

function decodeHtmlEntities(text) {
  return text
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, "'");
}

이 함수를 제목 필터링 전에 적용하면 인코딩된 특수문자 때문에 필터가 제대로 동작하지 않는 문제를 방지할 수 있다.

배열 방식으로 바꾸니 중복 체크가 쉬워졌다

처음에는 기사 1개씩 다음 노드로 넘기는 방식으로 만들었다. 그런데 이렇게 하면 중복 기사 체크 로직을 구현하기가 어렵다.

기사가 하나씩 오면 이전에 어떤 기사가 왔는지 비교할 대상이 없기 때문이다.

전체 기사를 배열로 한번에 넘기는 방식으로 바꾸니 중복 체크가 훨씬 깔끔해졌다. 배열 안에서 title이나 link 기준으로 중복을 제거하면 된다.

const uniqueArticles = filtered.filter((article, index, self) => 
  index === self.findIndex(a => a.link === article.link)
);

link 필드를 기준으로 중복을 제거하는 코드다. 같은 기사가 여러 피드에 중복으로 올라오는 경우에 유용하다.

n8n RSS 필터링 적용 후 달라진 점

이전에는 수집된 기사 목록을 열어서 [포토], [부음] 같은 기사를 눈으로 찾아 지웠다.

하루에 100개 이상 기사가 들어오면 이것만 해도 시간이 꽤 걸렸다.

필터링 자동화를 적용하고 나서는 실제로 읽을 기사만 남아서 후처리 시간이 확 줄었다.

필터 조건을 배열로 관리하니 새로운 패턴 추가도 쉽다. 예를 들어 특정 언론사의 광고성 기사가 [AD] 같은 태그를 달고 온다면 excludePatterns에 추가만 하면 된다.

FAQ

n8n RSS Read 노드 대신 HTTP Request를 쓴 이유는?

RSS Read 노드는 단일 URL을 처리하기엔 편하지만, 여러 피드를 동적으로 순회하려면 HTTP Request가 더 유연하다.

URL 목록을 배열로 관리하고 반복 처리하는 구조에는 HTTP Request가 적합하다.

필터링 조건을 정규식으로 바꿀 수 있나?

가능하다. includes() 대신 정규식 test()를 사용하면 더 복잡한 패턴도 처리할 수 있다.

예를 들어 대소문자 무시, 부분 일치 같은 조건을 추가할 때 정규식이 유용하다.

XML 파싱을 별도 노드로 분리하는 게 나은가?

처음에는 분리했다가 Code 노드 하나로 통합했다. 노드 수가 많아지면 워크플로우가 복잡해지고 디버깅이 어려워진다.

단순한 파싱과 필터링이면 Code 노드 하나로 처리하는 게 깔끔하다.

필터링된 기사 수를 로그로 남기려면?

Code 노드 마지막에 console.log()를 추가하면 n8n 실행 로그에서 확인할 수 있다. 또는 별도 변수에 담아서 다음 노드에서 Slack이나 이메일로 전송하는 방법도 있다.

특정 키워드가 포함된 기사만 남기는 방식으로 바꿀 수 있나?

필터 조건을 반대로 바꾸면 된다. excludePatterns 대신 includePatterns를 만들고, some() 결과가 true인 것만 통과시키면 특정 키워드 포함 기사만 수집하는 구조가 된다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤