본문으로 건너뛰기
취준생을 위한 n8n 실전 워크플로우 레시피 · 11/44시리즈 보기 →

구글 뉴스 RSS 링크는 왜 안 열리나 — n8n 서브워크플로우로 원문 URL 디코드해서 노션에 쌓기

· 약 13분
Datapopcorn CEO / AI automation educator
이 글과 연결된 글 보기
7편 · 20개 연결
100%
n8n Data Table 처음 써보기 — 트렌드 3개를 합쳐 매일 아침 분석 주제 뽑는 워크플로우구글 뉴스 RSS 링크는 왜 안 열리나 — n8n 서브워크플로우로 원문 URL 디코드해서 노션에 쌓기n8n으로 정적 웹서버 운영하기 — 수료증 발급부터 진위 확인까지, 멱등 처리로 안전하게벡터DB 없이 실시간으로 사이트를 읽는 챗봇 — 경복대학교 안내 챗봇 해부n8n으로 반복업무 자동화 포트폴리오 만들기구글시트에 쌓인 데이터를 인스타그램 카드뉴스로 — n8n 시트 인스타그램 자동발행서브워크플로우를 Tool로 감싸 AI Agent에게 서버 점검시키기 — 그리고 시스템 프롬프트만으로는 부족한 이유

드래그로 이동 · 휠/두 손가락으로 확대 · 방향키로 글 선택 · 0으로 맞춤. 실선: 본문 링크, 점선: 시리즈·주제의 대표 연결.

표시된 글 7편 목록
전체 글 연결 탐색

구글 뉴스 RSS를 노션에 쌓아 본 사람은 한 번쯤 겪습니다. 페이지의 URL 속성을 눌러 보면 기사 원문이 아니라 news.google.com/rss/articles/...로 시작하는 긴 주소가 들어 있고, 어느 언론사 기사인지 주소만 봐서는 알 수 없습니다. 이 글의 워크플로우는 RSS Feed Trigger로 새 기사를 받고, 그 주소를 원문 URL로 되돌리는 디코드 로직을 별도 서브워크플로우로 분리해 Execute Workflow로 부른 뒤, 원문 URL·태그·대표 이미지를 붙여 노션 데이터베이스에 페이지로 쌓습니다. 따라 만들면 "워크플로우를 둘로 나누고 입출력을 약속하는" 방법이 손에 익고, 그 방법은 디코드 말고 다른 반복 로직에도 그대로 쓰입니다.

이 워크플로우 하나로 보여줄 수 있는 것​

뉴스 5개 메일 편이 RSS Read 노드의 기본 사용법이었고 따릉이 파이프라인 편이 API 수집·전처리·적재였다면, 이 편은 시리즈에서 처음으로 워크플로우를 둘로 나눕니다. 노드 수보다 구조가 중요한 편이라, 면접에서 설명할 거리가 네 가지 생깁니다.

  • 이벤트형 트리거 — 정해진 시각이 아니라 새 기사가 올라올 때 도는 RSS Feed Trigger를 써본 경험
  • 서브워크플로우 분리 — 반복해서 쓸 로직을 별도 워크플로우로 빼고, Execute Workflow와 Execute Workflow Trigger로 입출력을 약속해 본 경험
  • 공식 노드 없는 처리 — HTTP Request와 HTML 노드만으로 페이지에서 값을 뽑고 다른 요청에 넣어 본 경험
  • 노션 적재 — Notion 노드로 데이터베이스 페이지를 만들고, 노드가 못 하는 부분은 Notion API를 직접 불러 채워 본 경험

이 중 두 번째가 이 편의 핵심입니다. 디코드 로직은 이 워크플로우에서만 쓰는 게 아니라 구글 뉴스 링크를 다루는 모든 워크플로우에서 필요한데, 그때마다 노드 여섯 개를 복사하는 대신 한 번 만들어 두고 부르는 쪽이 낫습니다. 그 "한 번 만들어 두고 부르는" 구조가 서브워크플로우입니다.

배경 — 뉴스 메일 편에서 미뤄 둔 문제​

뉴스 5개 메일 편의 한계 목록에 "링크가 기사 원문 주소가 아니다"라는 항목이 있었습니다. 구글 뉴스 RSS 피드의 link는 https://news.google.com/rss/articles/CBMi... 형태의 주소이고, 브라우저에서 열면 구글을 거쳐 원문으로 넘어가지만 노션이나 시트에 쌓아 두면 news.google.com 주소만 남습니다. 어느 언론사인지 알 수 없고, 나중에 본문을 가져오려 해도 그 주소로는 기사 HTML을 얻을 수 없습니다.

원본은 데이터팝콘 n8n 인스턴스에 유즈케이스 교재로 만들어 둔 "Google Headline News RSS Feed" 워크플로우와, 같은 번호를 단 "Extract And Decode Google News RSS URLs to Clean Article Links" 서브워크플로우 두 개입니다. 서브워크플로우 쪽은 캔버스 스티키 노트에 출처 링크가 남아 있는 커뮤니티 템플릿을 가져와 정리한 것이라, 스티키 노트가 영문이고 노드마다 설명이 붙어 있습니다. 설치나 기본 노드 설명은 n8n으로 반복업무 자동화 포트폴리오 만들기에 있으니 이 글에서는 워크플로우 자체만 다룹니다.

선행조건은 노션 통합 하나뿐이고 무료입니다. 노션에서 Internal Integration을 만들어 토큰을 n8n의 Notion credential에 넣고, 기사를 쌓을 데이터베이스에 그 통합을 연결해 두면 됩니다.

구조 — Trigger / Task / Target​

  • Trigger — 구글 뉴스 검색 피드를 1분마다 확인하는 RSS Feed Trigger. 스티키 노트에는 "뉴스가 올라올 때마다 실행"이라고 적혀 있습니다
  • Task — 5개만 남기고 → 서브워크플로우로 원문 URL 디코드 → 제목 기준으로 원래 아이템과 합치기 → 원문 HTML에서 대표 이미지와 섹션 추출
  • Target — 노션 데이터베이스 페이지(URL·태그 속성 + 본문 블록) 생성 후 이미지 블록 추가

원본 캔버스에는 이 흐름 말고도 Aggregate → Gmail로 가는 TARGET(1) 브랜치와, 디코드 없이 RSS 트리거에서 슬랙·노션으로 바로 보내는 "SIMPLE VERSION"이 함께 있습니다. 메일 브랜치는 뉴스 메일 편과 같은 구조라 여기서는 다루지 않고, SIMPLE VERSION은 아래에서 "왜 디코드가 필요한지"를 보여주는 비교 대상으로만 씁니다.

만들기​

0. 노션 데이터베이스 준비​

데이터베이스에는 속성 세 개가 있으면 됩니다. 기본 제목 속성, URL(URL 타입), 태그(다중 선택). 속성 이름은 뒤의 Notion 노드에서 그대로 고르기 때문에, 다른 이름으로 만들었다면 노드에서 그 이름을 고르면 됩니다. 데이터베이스 우측 상단 메뉴에서 만들어 둔 통합을 연결하는 것을 잊지 마세요. 연결하지 않으면 n8n에서 데이터베이스 목록에 아예 뜨지 않습니다.

1. RSS Feed Trigger — 스케줄 대신 새 기사가 올라올 때​

RSS Feed Trigger 노드의 Feed URL에 구글 뉴스 검색 피드를 넣고 Poll Time을 Every Minute으로 둡니다. 원본은 n8n을 검색어로 쓰고 있고, 스티키 노트에 주요 뉴스용과 키워드 검색용 주소 두 개가 정리돼 있습니다.

https://news.google.com/rss?hl=ko&gl=KR&ceid=KR:ko
https://news.google.com/rss/search?q=키워드입력&hl=ko&gl=KR&ceid=KR%3Ako

hl, gl, ceid는 언어·지역 파라미터입니다. 서브워크플로우 쪽 스티키 노트가 ISO 639-1 기준으로 바꾸라고 안내하는 부분인데, 한국어 기사면 위 값을 그대로 두면 됩니다.

뉴스 메일 편의 Schedule Trigger + RSS Read 조합과 무엇이 다른지는 한 번 짚고 가야 합니다. RSS Read는 실행할 때마다 피드 전체를 읽어 오는 일반 노드라, 매일 아침 한 번 읽어 상위 5개를 자르는 요약 메일에 맞습니다. RSS Feed Trigger는 폴링 주기마다 피드를 확인해 이전에 보지 못한 아이템만 내보내는 트리거라, 새 기사가 없으면 실행 자체가 일어나지 않습니다. 기사를 빠짐없이 아카이브에 쌓는 이 편의 목적에는 트리거 쪽이 맞습니다. 실습 중에는 트리거 노드의 테스트 실행으로 피드 아이템을 받아 보면 됩니다.

2. Limit — 5개만​

Limit 노드에서 Max Items를 5, Keep을 Last Items로 둡니다. 스티키 노트의 "5개만 수집"이 이 노드입니다. 최신 기사를 원한다면 트리거 출력의 순서를 먼저 확인하고 First Items와 Last Items 중 맞는 쪽을 고르세요.

개수를 제한하는 이유는 서브워크플로우 쪽 스티키 노트에 적혀 있습니다. "전체 워크플로우가 HTTP 요청을 많이 보내니 최대 3개를 권한다"는 메모입니다. 기사 하나당 뒤에서 최소 세 번(기사 링크 GET, 디코드 POST, 원문 GET)의 요청이 나가니, 원본의 5개도 실습에는 충분히 많은 편입니다.

3. Execute Workflow + Merge — 서브워크플로우를 부르고 결과를 되받기​

Limit의 출력은 두 갈래로 나갑니다. 하나는 Execute Workflow 노드로, 다른 하나는 Merge 노드의 두 번째 입력으로 갑니다. Execute Workflow의 출력은 Merge의 첫 번째 입력으로 들어가고, Merge는 Mode를 Combine, Fields to Match를 title로 둡니다. 서브워크플로우가 디코드한 URL을 돌려주면 제목이 같은 원래 아이템에 붙여 주는 구조입니다.

여기서 입출력 계약이 정해집니다. 서브워크플로우로 들어가는 것은 RSS 아이템 그대로(title, link, guid, contentSnippet…)이고, 돌아오는 것은 title과 google_news_url 두 필드뿐입니다. 서브워크플로우가 굳이 title을 같이 돌려주는 이유가 바로 Merge의 조인 키로 쓰기 위해서입니다. 이 약속만 지키면 서브워크플로우 안을 어떻게 고쳐도 부모 쪽은 손댈 필요가 없습니다.

한 가지 알아둘 것은, 원본 export의 Execute Workflow 노드가 가리키는 워크플로우 이름이 백업 폴더에 없다는 점입니다. 대신 노드 옆 스티키 노트에 "URL 디코드 news.google -> 원본 뉴스 URL"이라고 적혀 있고, 서브워크플로우 파일의 출력 모양이 Merge가 기대하는 title + google_news_url과 정확히 맞습니다. 그러니 다음 단계에서 그 서브워크플로우를 만들고 이 노드에서 From list로 골라 연결하면 됩니다.

4. 서브워크플로우 — 디코더 만들기​

새 워크플로우를 하나 만들고 Execute Workflow Trigger(캔버스 이름은 "When Executed by Another Workflow")를 놓습니다. Input data mode는 Accept all data입니다. 부모가 넘긴 RSS 아이템이 그대로 들어오고, 이후 노드 여섯 개가 순서대로 이어집니다. 각 노드 옆 스티키 노트에 역할이 적혀 있어서 그걸 따라 옮깁니다.

  1. Get encoded news URL — HTTP Request, GET {{ $json.link }}. 스티키 노트대로 "HTML 콘텐츠를 가져오는" 단계입니다.
  2. Extract decoding keys — HTML 노드, Extract HTML Content. div에서 data-n-a-sg 속성을 signature로, data-n-a-ts 속성을 timestamp로 뽑습니다. 스티키 노트에 따르면 디코드에 필요한 변수는 이 둘과 "URL에 이미 들어 있는 base64 문자열" 세 개입니다.
  3. Map needed keys — Set 노드. signature, timestamp는 그대로 넘기고 base64Str에 {{ $('When Executed by Another Workflow').item.json.guid }}를 넣습니다. RSS 아이템의 guid가 그 base64 문자열입니다.
  4. Prepare decoding variables — Code 노드. 세 값을 디코드 요청이 요구하는 본문 형태로 조립합니다.
return $input.all().map(item => {
const gn_art_id = item.json.base64Str;
const timestamp = item.json.timestamp;
const signature = item.json.signature;

const articlesReq = [
'Fbv4je',
`["garturlreq",[["X","X",["X","X"],null,null,1,1,"US:en",null,1,null,null,null,null,null,0,1],"X","X",1,[1,1,1],1,1,null,0,0,null,0],"${gn_art_id}",${timestamp},"${signature}"]`,
];

return {
json: {
f_req: JSON.stringify([[articlesReq]])
}
};
});
  1. Call decoding URL — HTTP Request, POST https://news.google.com/_/DotsSplashUi/data/batchexecute. Body Content Type은 Form-Urlencoded, 파라미터는 f.req 하나에 {{ $json.f_req }}. 헤더에는 Content-Type: application/x-www-form-urlencoded;charset=UTF-8과 브라우저 User-Agent, Referer: https://www.google.com/ 세 개가 들어 있습니다. Response 옵션에서 Include Response Headers and Status를 켜고 Response Format을 Text로 둡니다.
  2. Decoded url — Set 노드. title에 {{ $('When Executed by Another Workflow').item.json.title }}, google_news_url에 {{ JSON.parse(JSON.parse($json.data.split('\n\n')[1])[0][2])[1] }}를 넣습니다. 스티키 노트가 "구글이 URL 앞에 불필요한 임의 문자를 붙인다"고 설명하는 단계로, 응답 텍스트를 빈 줄로 잘라 두 번째 조각을 JSON으로 두 번 파싱하면 원문 URL이 나옵니다.

이 다섯 단계의 요청·응답 형식은 스티키 노트가 밝히듯 리버스 엔지니어링으로 알아낸 것이고 구글이 문서로 공개한 것이 아닙니다. 그래서 위 코드의 'Fbv4je'나 배열 안의 숫자가 왜 그 값인지는 이 글도 설명하지 못합니다. 그대로 옮기되, 언제든 깨질 수 있는 부분이라는 걸 알고 쓰는 게 맞습니다.

서브워크플로우를 부모와 따로 테스트하는 방법도 원본에 남아 있습니다. 트리거 노드에 RSS 아이템 5개가 핀 데이터로 고정돼 있어서, 부모 워크플로우를 돌리지 않고 이 캔버스에서 실행 버튼만 눌러도 디코드 결과를 볼 수 있습니다. 서브워크플로우로 분리했을 때 얻는 가장 실용적인 이점이 이 단독 테스트입니다.

5. Set·HTTP·HTML — 디코드된 URL로 원문에서 태그와 이미지 뽑기​

부모 워크플로우로 돌아옵니다. Merge 뒤의 Set 노드(원본 이름 "Arrange Fields")는 title, url, content(피드의 contentSnippet), isoDate({{ $json.isoDate.toDateTime() }}) 네 필드로 정리합니다. 여기서 고쳐야 할 것이 하나 있습니다. 원본 export는 url에 {{ $json.link }}를 넣고 있는데, 이건 디코드 전의 news.google.com 주소입니다. 서브워크플로우가 돌려준 값을 쓰려면 {{ $json.google_news_url }}로 바꿔야 합니다. 이걸 안 바꾸면 서브워크플로우를 붙이고도 노션에는 리다이렉트 주소가 쌓입니다.

다음은 HTTP Request 노드(원본 이름 "Get News")로 {{ $json.url }}을 GET 해서 원문 HTML을 받고, HTML 노드(원본 이름 "Extract Image & Category")에서 메타 태그 세 개를 뽑습니다.

  • category — meta[name="og:category"]의 content 속성
  • image — meta[property="og:image"]의 content 속성
  • section — meta[name="article:section"]의 content 속성

원본 export에는 Set 노드와 이 HTTP Request 노드 사이의 연결이 빠져 있고 Set 뒤는 메일 브랜치로만 이어져 있습니다. 따라 만들 때는 Set → HTTP Request로 직접 이어 주세요.

6. Notion — 데이터베이스 페이지를 만들고 이미지 블록 붙이기​

Notion 노드에서 Resource를 Database Page, Operation을 Create로 두고 데이터베이스를 고릅니다. Title에는 {{ $('Arrange Fields').item.json.title }}, 속성에는 URL에 {{ $('Arrange Fields').item.json.url }}, 태그에 {{ $json.section || $json.category || "일반" }}을 넣습니다. Blocks에 문단 하나를 추가해 {{ $('Arrange Fields').item.json.content }}를 넣으면 페이지 본문에 요약이 들어갑니다. Simplify는 꺼 둡니다. 다음 노드가 생성된 페이지의 id를 써야 하기 때문입니다.

$('Arrange Fields').item을 쓰는 이유는 HTML 노드의 출력에 image, category, section 세 값만 남고 제목과 URL은 없기 때문입니다. n8n은 아이템의 짝을 기억하고 있어서, 앞 노드 이름으로 같은 줄의 값을 되찾아 올 수 있습니다. 태그 표현식은 article:section이 있으면 그것을, 없으면 og:category를, 둘 다 없으면 "일반"을 넣는 순서입니다.

마지막 노드는 대표 이미지를 페이지에 붙이는 HTTP Request입니다. PATCH https://api.notion.com/v1/blocks/{{ $json.id }}/children으로 Notion API를 직접 부르고, Authentication은 Predefined Credential Type → Notion API로 앞 노드와 같은 credential을 씁니다. Body는 다음 JSON입니다.

{
"children": [
{
"object": "block",
"type": "image",
"image": {
"type": "external",
"external": {
"url": "{{ $('Extract Image & Category').item.json.image }}"
}
}
}
]
}

Notion 노드의 Blocks 옵션에는 문단·제목·목록 같은 텍스트 블록만 있어서, 이미지 블록은 이렇게 API를 직접 부르는 방식으로 추가한 것으로 보입니다. 명함 OCR 편에서 본 "공식 노드가 못 하면 HTTP Request + Predefined Credential"의 또 다른 예입니다.

원본 캔버스에 남아 있는 흔적​

이 워크플로우는 교재용이라 운영 데이터는 없지만, 두 캔버스에 남은 흔적이 따라 만들 때 도움이 됩니다.

부모 워크플로우 쪽에는 앞에서 말한 두 가지가 있습니다. Execute Workflow가 가리키는 이름이 백업에 없고, Set 노드가 link를 쓰고 있으며 HTTP Request로 가는 연결이 끊겨 있습니다. 같은 폴더에 "Google News URL Decoder (Sub-workflow)"라는 이름의 초기 버전도 있는데, 노드 구성은 같고 디코드 요청에 User-Agent·Referer 헤더가 없으며 스티키 노트도 없습니다. 헤더가 붙고 설명이 달린 쪽이 나중 버전이니 그쪽을 기준으로 삼으면 됩니다. 캔버스 아래쪽의 SIMPLE VERSION은 RSS Feed Trigger에서 Slack 노드와 Notion 노드로 바로 가는 세 노드짜리 흐름으로, 트리거에 기사 13개가 핀 데이터로 남아 있습니다. 이걸 먼저 돌려서 노션 URL 속성에 news.google.com 주소가 쌓이는 걸 보고 나면 디코드 단계가 왜 필요한지 설명할 필요가 없어집니다.

서브워크플로우 쪽에는 트리거 핀 데이터 외에 본 흐름과 연결되지 않은 노드 두 종류가 더 있습니다. 하나는 RSS Read → Limit으로 피드를 따로 읽어 보는 테스트 체인이고, 다른 하나는 디코드 결과를 하나로 모은 Aggregate 노드인데 여기에도 출력이 핀으로 남아 있습니다. 그 출력에서 google_news_url이 국내 언론사 도메인의 기사 주소로 바뀐 것을 확인할 수 있습니다. 스티키 노트의 마지막 문장은 "이 워크플로우는 동작하는 동안만 동작한다"입니다. 만든 사람이 스스로 붙인 경고입니다.

한계​

  • 비공식 디코드 — 스티키 노트가 밝히듯 디코드 절차는 리버스 엔지니어링이고 구글이 문서화하지 않았습니다. 응답 형식이 바뀌면 Decoded url 노드의 파싱 표현식부터 깨집니다. 서브워크플로우로 분리해 둔 덕분에 그때 고칠 곳은 한 군데입니다.
  • 요청 빈도 — 스티키 노트에 "너무 자주 돌리면 구글이 IP를 차단할 수 있다"는 경고가 있습니다. 1분 폴링에 기사당 요청 세 번이면 적지 않으니, 실습이 끝나면 Poll Time을 늘리거나 Limit을 3으로 줄이는 게 안전합니다.
  • 제목으로 조인 — Merge가 title로 합치기 때문에 제목이 같은 기사가 두 개 들어오면 URL이 섞일 수 있습니다. 서브워크플로우가 guid를 함께 돌려주고 그걸로 매칭하면 더 안전합니다.
  • 중복 페이지 — 같은 기사가 다시 들어와도 노션에 이미 있는지 확인하는 단계가 없습니다. Notion 노드의 Get Many로 URL을 검색한 뒤 없을 때만 만드는 분기를 넣어야 운영에 쓸 수 있습니다.
  • 이미지 없는 기사 — og:image가 없는 페이지면 마지막 PATCH가 빈 URL로 나갑니다. IF 노드로 image가 있을 때만 이미지 블록을 붙이게 해 두세요.
  • 본문 없음 — 페이지 본문에 들어가는 건 피드의 요약(contentSnippet)뿐입니다. 서브워크플로우 스티키 노트는 원문 URL을 얻은 뒤 본문 추출 서비스나 HTML 노드로 기사 텍스트를 가져오는 확장을 제안하고 있습니다.

포트폴리오·면접 포인트​

응용은 검색어만 바꿔도 됩니다. 지원하려는 회사 이름을 넣으면 그 회사 기사가 태그·대표 이미지와 함께 노션에 쌓이는 아카이브가 되고, 면접 전날 갤러리 뷰로 훑기 좋습니다. 한 단계 더 가려면 디코더 서브워크플로우를 그대로 두고 부모 워크플로우를 하나 더 만들어 보세요. 같은 디코더를 슬랙 알림과 구글시트 적재 두 곳에서 부르는 구조가 포트폴리오에서 "재사용을 설계했다"는 증거가 됩니다.

면접에서는 "디코드 로직을 왜 같은 워크플로우 안에 두지 않고 분리했나요?"라는 질문이 올 수 있습니다. 답은 세 가지입니다. 다른 워크플로우에서도 같은 로직이 필요해서 한 번만 만들려고, 핀 데이터로 서브워크플로우만 따로 테스트하려고, 그리고 구글 쪽이 바뀌었을 때 고칠 곳을 한 군데로 두려고. 여기에 "부모와 서브워크플로우 사이에 title과 google_news_url이라는 입출력 계약을 정해 두었다"고 덧붙이면, 노드를 이어 본 사람이 아니라 구조를 설계한 사람으로 보입니다.

다음 액션​

이 편에서 og:image와 article:section을 뽑는 데 잠깐 쓴 HTML 노드가 다음 편의 주인공입니다. 온라인 서점의 베스트셀러 페이지를 HTTP Request로 받아 HTML 노드의 CSS 선택자만으로 제목·저자·가격을 뽑아 시트에 쌓는 웹 스크래핑 편인데, 코드는 한 줄도 없습니다. 이 편의 서브워크플로우까지 만들어 봤다면 그 편은 반나절이면 충분합니다.