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

경제지표 API로 매일 투자 리포트 받기 — AI에게 cURL부터 만들게 하는 n8n 워크플로우

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

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

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

공공데이터포털에서 무료 인증키 하나만 받으면, n8n이 매일 코스피 지수와 관심 종목 시세를 받아 Gemini가 시장 총평·종목별 의견·내일 볼 포인트를 적은 리포트를 만들어 줍니다. 이 편에서 가져갈 기술은 리포트보다 그 앞 단계입니다. API 문서와 인증키를 AI에게 주고 cURL을 받아 HTTP Request 노드의 Import cURL에 붙여 넣으면, 처음 보는 공공 API도 몇 분 안에 노드가 됩니다.

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

응급실 API 편에서 공공데이터를 시트에 쌓았고, 포켓몬 API 편에서 받은 데이터를 AI가 분석하게 했습니다. 이 편은 그 둘을 합친 뒤 한 가지를 더 얹습니다. 노드를 손으로 채우는 대신 AI에게 만들게 하는 방법입니다.

  • API 문서를 노드로 바꾸기 — 문서와 인증키를 AI에게 주고 cURL을 받아 Import cURL로 HTTP Request 노드를 완성한 경험
  • 두 갈래 호출을 하나로 — 지수 API와 종목 API를 따로 부른 뒤 Merge로 합쳐 같은 모양의 데이터로 만든 경험
  • 데이터를 문장으로 — 숫자 몇 줄을 LLM 프롬프트에 넣어 사람이 읽을 리포트로 바꾼 경험
  • 리포트를 보낼 형태로 — 마크다운 답변을 HTML 메일 본문으로 변환한 경험

첫 번째가 이 편의 핵심입니다. 공공데이터포털 API는 수천 개인데 n8n에 내장 노드가 있는 건 하나도 없습니다. HTTP Request 노드로 직접 불러야 하는데, 문서를 읽고 파라미터를 하나씩 옮기는 일이 입문자를 가장 많이 막습니다. 그 일을 AI에게 넘기는 법을 익히면 어떤 API든 같은 속도로 붙일 수 있습니다.

배경 — 매일 아침 지수와 종목을 확인하는 일​

관심 종목이 몇 개 있으면 아침마다 증권 앱을 열어 지수를 보고, 종목을 하나씩 눌러 보고, 머릿속으로 "오늘은 어떤 분위기인가"를 정리하게 됩니다. 이 워크플로우는 그 몇 분을 리포트 한 통으로 바꿉니다. 데이터팝콘 n8n 인스턴스에 교육용으로 만들어 둔 것이고, 같은 주제로 두 가지 버전이 있습니다. 이 글의 주 흐름은 "투자 모니터링 시스템"이고, Basic LLM Chain으로 다시 짠 "AI 투자 리포트 봇"은 변형본으로 곳곳에서 비교합니다.

데이터는 공공데이터포털의 금융위원회 주식시세정보와 지수시세정보 API를 씁니다. 둘 다 무료이고 활용신청은 자동 승인입니다. 제목의 "경제지표"는 정확히는 코스피 지수와 종목 종가라는 시장 쪽 지표인데, 같은 포털에 금리·환율 같은 다른 지표 API도 있어서 이 편의 방법을 그대로 옮기면 됩니다. 설치나 기본 노드 설명은 n8n으로 반복업무 자동화 포트폴리오 만들기에 있으니 여기서는 워크플로우만 다룹니다.

한 가지 먼저 말해두면, 이 리포트는 정보 제공용이지 투자 판단이 아닙니다. LLM이 쓰는 "대응 전략"은 종가와 등락률 몇 줄을 보고 만든 문장이라는 점을 잊지 마세요.

구조 — Trigger / Task / Target​

  • Trigger — 스케줄. 트리거 하나에서 두 갈래로 갈라집니다
  • Task — 지수 API와 종목 API를 각각 호출 → Set으로 같은 컬럼 이름으로 정리 → Merge로 합치기 → Gemini가 리포트 작성 → Code가 HTML 메일 본문으로 변환
  • Target — 원본에는 보내는 노드가 없습니다. Code 노드가 html과 subject 두 필드를 만들어 두고 끝나서, Gmail 노드를 붙이는 건 따라 만드는 사람 몫입니다

만들기​

0. 인증키 받기​

공공데이터포털에 로그인해 위 두 API 페이지에서 활용신청을 하면 바로 인증키가 나옵니다. 두 API 모두 같은 키로 호출합니다. 페이지의 요청 파라미터 표와 응답 항목 표가 곧 AI에게 줄 재료이니 탭을 닫지 마세요.

1. HTTP Request — AI에게 cURL을 만들게 하고 Import cURL로 붙이기​

원본 캔버스의 스티키 노트 첫 줄이 이 워크플로우의 만드는 법입니다. "api 문서와 인증키를 가지고 cURL을 만들어달라고 안내하기". 즉 HTTP Request 노드를 열고 URL과 파라미터를 하나씩 채우는 게 아니라, AI에게 문서를 주고 cURL 명령을 받은 뒤 그걸 통째로 노드에 넣습니다.

API 페이지의 요청 파라미터 표를 복사해서 AI에게 이렇게 요청합니다.

아래는 공공데이터포털 "금융위원회 주식시세정보" API의 요청 파라미터 표입니다.
엔드포인트: https://apis.data.go.kr/1160100/service/GetStockSecuritiesInfoService/getStockPriceInfo
인증키: {API_KEY}

(여기에 파라미터 표 붙여넣기)

종목명이 "삼성전자"인 시세를 JSON으로 1건만 받는 cURL 명령을 만들어 주세요.
serviceKey, resultType, itmsNm, numOfRows, pageNo를 쿼리 파라미터로 넣어 주세요.

AI가 돌려주는 cURL은 대개 이런 모양입니다.

curl -G "https://apis.data.go.kr/1160100/service/GetStockSecuritiesInfoService/getStockPriceInfo" \
--data-urlencode "serviceKey={API_KEY}" \
--data-urlencode "resultType=json" \
--data-urlencode "itmsNm=삼성전자" \
--data-urlencode "numOfRows=1" \
--data-urlencode "pageNo=1"

n8n에서 HTTP Request 노드를 추가하면 파라미터 패널 상단에 Import cURL 버튼이 있습니다. 눌러서 위 명령을 붙여 넣으면 URL, Send Query Parameters, 쿼리 파라미터 다섯 개가 한 번에 채워집니다. 원본의 두 HTTP Request 노드가 정확히 이 형태입니다. 지수 API도 같은 방식으로 한 번 더 하면 되는데, 이쪽은 종목명 대신 idxNm=코스피를 넣습니다.

붙여 넣은 직후에 할 일이 하나 있습니다. 쿼리 파라미터 칸에 들어간 serviceKey 값을 지우고, Authentication을 Generic Credential Type → Query Auth로 바꿔 Name에 serviceKey, Value에 인증키를 넣는 것입니다. 원본은 파라미터에 키가 그대로 박혀 있는데, 이렇게 두면 워크플로우를 내보내거나 화면을 공유할 때 키가 같이 나갑니다. 참고로 포털이 인증키를 Encoding·Decoding 두 가지로 보여주면 Decoding 쪽을 씁니다. n8n이 쿼리 값을 한 번 더 인코딩하기 때문입니다.

2. Data Table — 관심 종목 목록​

종목 API는 한 번에 종목 하나만 조회하니, 관심 종목 수만큼 호출해야 합니다. 원본은 n8n에 내장된 Data Table에 stocks라는 표를 만들고 name 컬럼에 종목명을 넣어 둔 뒤 Get row(s)로 읽습니다. 행 하나가 아이템 하나가 되어 다음 HTTP Request 노드로 들어가고, 그 노드의 itmsNm 값을 {{ $json.name }}으로 바꾸면 종목마다 한 번씩 호출됩니다. Data Table 대신 구글시트를 읽어도 되고, 실습이면 Edit Fields 노드에 종목명 두세 개를 직접 적어도 됩니다.

3. Set — 두 응답을 같은 모양으로​

API 응답은 response.body.items.item 배열 안에 들어옵니다. 지수 쪽과 종목 쪽의 필드 이름이 달라서(idxNm과 itmsNm) Set 노드로 같은 컬럼 이름으로 맞춥니다. 원본의 종목 정리 노드는 이렇게 돼 있습니다.

  • 종목명: {{ $json.response.body.items.item[0].itmsNm }}
  • 현재가: {{ $json.response.body.items.item[0].clpr }}
  • 등락: {{ $json.response.body.items.item[0].vs }}
  • 등락률: {{ $json.response.body.items.item[0].fltRt }}

코스피 쪽 Set은 종목명을 KOSPI로 고정하고 나머지는 같습니다. 컬럼 이름을 현재가로 지었지만 API 필드는 clpr, 즉 종가입니다. 이 API는 실시간이 아니라 하루 한 번 갱신되는 일별 데이터라는 점을 기억해 두세요. 아래 한계에서 다시 다룹니다.

4. Merge — 두 갈래를 하나로​

Merge 노드의 기본 모드(Append)로 코스피 아이템 1개와 종목 아이템 N개를 이어 붙입니다. 원본은 이 노드 이름을 코스피+종목으로 지었고, 다음 단계의 프롬프트가 이 이름으로 데이터를 가져옵니다.

5. AI 리포트 — 프롬프트, 그리고 변형본이 Aggregate를 넣은 이유​

원본은 Google Gemini 노드(Message a model, gemini-2.5-flash)에 아래 프롬프트를 넣었습니다. 시장 데이터 줄은 Merge 결과 전체를 한 줄씩 나열하는 표현식입니다.

당신은 대한민국 최고의 주식 투자 전략가입니다. 아래 제공되는 오늘의 시장 데이터를 분석하여 투자 리포트를 작성해 주세요.

## 오늘 날짜: {{ $now.format('yyyy-MM-dd') }}

## 시장 데이터
{{ $('코스피+종목').all().map(item => `- ${item.json.종목명}: 현재가 ${item.json.현재가} (등락률: ${item.json.등락률})`).join('\n') }}

## 작성 지침:
1. 시장 총평: 코스피 지수의 움직임을 바탕으로 오늘 시장의 전반적인 분위기를 한 문장으로 요약하세요.
2. 종목별 분석: 관심 종목(삼성전자, SK하이닉스 등) 각각에 대해 현재 상태와 간단한 기술적 의견을 제시하세요.
3. 투자 전략: 위 데이터를 종합해 볼 때, 내일 시장에서 주의 깊게 봐야 할 포인트나 대응 전략을 전문가답게 조언해 주세요.
4. 말투: 신뢰감 있고 전문적인 어조(~입니다, ~할 것으로 보입니다)를 사용하세요.
5. 주의: HTML 태그를 쓰지 말고 순수 텍스트로만 답변하세요. (줄바꿈은 사용 가능)

그런데 이 구조에는 눈에 잘 안 보이는 낭비가 있습니다. Merge가 아이템을 1+N개 내보내면 Gemini 노드는 아이템마다 한 번씩, 즉 리포트를 1+N번 씁니다. 프롬프트가 .all()로 전체를 읽으니 내용은 매번 같고, 뒤의 Code 노드는 $input.first()로 첫 번째만 씁니다. 종목이 다섯 개면 다섯 번 부르고 네 번은 버리는 셈입니다.

변형본 "AI 투자 리포트 봇"은 Merge 뒤에 Aggregate 노드(All Item Data)를 넣어 아이템을 하나로 묶은 다음 Basic LLM Chain에 Google Gemini Chat Model을 붙였습니다. 따라 만든다면 이 구조를 권합니다. LLM은 한 번만 불리고, 모델을 바꾸고 싶을 때 서브노드만 갈아 끼우면 됩니다. 이때 Aggregate 결과는 data 배열에 들어오므로 프롬프트의 시장 데이터 줄은 아래처럼 바꿔야 합니다. 원본 두 버전 어디에도 없는, 따라 만들 때 쓸 수정본입니다.

{{ $json.data.map(s => `- ${s.종목명}: 현재가 ${s.현재가} (등락률: ${s.등락률})`).join('\n') }}

Basic LLM Chain의 답은 $json.text로 나옵니다. 원본의 Gemini 노드는 $json.content.parts[0].text였으니, 다음 단계의 Edit Fields에서 analysis 필드의 표현식도 같이 바꿉니다.

6. Code — 리포트를 HTML 메일 본문으로​

Edit Fields에서 analysis(리포트 본문), today, kospi(Merge 결과 중 종목명이 KOSPI인 것), stocks(나머지)를 정리한 뒤 Code 노드가 HTML을 만듭니다. Gemini가 마크다운 비슷한 문장을 돌려주기 때문에 간단한 변환기가 앞에 붙어 있습니다.

const data = $input.first().json;

const markdownToHtml = (text) => {
if (!text) return '';
return text
.replace(/^### (.*$)/gim, '<h4 style="color: #1a73e8; margin-top: 20px;">$1</h4>')
.replace(/^## (.*$)/gim, '<h3 style="color: #1a73e8;">$1</h3>')
.replace(/^- (.*$)/gim, '<li style="margin-left: 20px;">$1</li>')
.replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
.replace(/\n/g, '<br>');
};

const analysisHtml = markdownToHtml(data.analysis);
// ... 코스피 카드, 관심 종목 표, AI 분석 영역을 인라인 스타일 HTML로 조립 ...

return [{ json: { html, subject: `[투자리포트] ${today} 시장 요약` } }];

가운데 생략한 부분은 코스피 지수 카드, 종목별 표, 등락률이 양수면 빨간색·음수면 파란색으로 칠하는 헬퍼입니다. 이런 HTML 템플릿은 원하는 모양을 AI에게 설명하고 받는 편이 빠릅니다. 만들어진 html과 subject는 Gmail 노드(Send, Email Type: HTML)에 그대로 넣으면 됩니다. 원본에는 이 노드가 없으니 여기서부터가 여러분의 작업입니다.

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

실행 이력 대신 캔버스에 남은 흔적 네 가지를 옮깁니다. 셋은 따라 만들 때 고쳐야 할 것이고, 하나는 배울 것입니다.

첫째, 본 흐름과 연결되지 않은 HTTP Request 노드가 하나 떠 있습니다. 종목명이 삼성전자로 고정된 단건 호출인데, 종목 목록을 Data Table로 빼기 전에 한 종목으로 응답 모양을 확인하기에 딱 좋은 형태입니다. Import cURL로 붙여 넣은 노드를 먼저 이렇게 단독 실행해서 response.body.items.item 경로를 눈으로 확인하고, 그다음 표현식을 넣는 순서를 권합니다.

둘째, 트리거 노드 이름은 "매일 오전 7시"인데 설정은 매주 월요일 14시로 되어 있습니다. 변형본도 같은 설정입니다. 따라 만들 때는 Trigger Interval을 Days로 두되, 시각은 API 갱신 이후로 잡으세요. 포털 안내(2026년 8월 기준)에 따르면 두 API 모두 기준일 다음 영업일 13시 이후에 갱신됩니다. 워크플로우 설정의 timezone도 한 번 확인해 두는 게 좋습니다.

셋째, 변형본의 프롬프트는 $('코스피+종목')을 참조하는데 변형본의 Merge 노드 이름은 Merge입니다. 그대로 실행하면 참조할 노드가 없다는 에러가 납니다. 노드 이름을 바꾸면 그 이름을 쓰는 표현식이 전부 깨진다는 걸 보여주는 흔적이라, 5번 단계의 수정본 표현식처럼 $json 기준으로 쓰는 편이 안전합니다.

넷째, 배울 것입니다. 변형본은 리포트 제목을 {{ $('HTTP Request - 코스피').item.json.response.body.items.item[0].basDt }}기준 리포트로 만듭니다. 오늘 날짜가 아니라 API가 돌려준 기준일자를 제목에 박는 방식인데, 하루 지연되는 데이터를 다룰 때 "이 숫자가 언제 것인가"를 리포트 자체가 말하게 하는 좋은 습관입니다.

한계​

  • 하루 늦은 데이터 — 위에서 말한 대로 D+1 갱신입니다. 아침 7시에 돌리면 어제가 아니라 그저께 종가를 받습니다. 실시간 시세가 필요하면 증권사 API로 바꿔야 하고, 이 워크플로우는 "전일 마감 정리" 용도로 보는 게 맞습니다.
  • 기술적 의견의 근거 부족 — 프롬프트는 "기술적 의견"과 "대응 전략"을 요구하지만 모델이 받는 건 종가와 등락률 한 줄뿐입니다. 그럴듯한 문장이 나오지만 근거는 없습니다. 최소한 며칠치 데이터를 numOfRows로 더 받아 넘기거나, 프롬프트에서 "주어진 데이터로 알 수 없는 것은 말하지 말라"고 묶어야 합니다.
  • 코스피 등락 값 — 코스피 Set 노드는 등락과 등락률 둘 다 fltRt를 씁니다. 종목 쪽처럼 등락은 vs로 바꿔야 리포트 카드의 숫자가 맞습니다.
  • 빈 응답 처리 없음 — 휴장일이나 종목명 오타면 item 배열이 비고 item[0]에서 에러가 납니다. Set 노드 앞에 IF로 걸러 주거나, Error Trigger 편의 방식으로 실패를 알리게 해 두세요.

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

스티키 노트의 나머지 두 줄이 그대로 확장 과제입니다. 하나는 "경제지표와 특정 산업군을 넣으면 매수·유지·매도 판단 봇 만들기", 다른 하나는 "서브워크플로우를 만들어 AI가 정성적 분석을 하도록 하기"입니다. 둘 다 이 편의 뼈대에 데이터 소스 하나, 프롬프트 한 단계를 더하는 일이라 포트폴리오로 키우기 좋습니다. 예를 들어 같은 포털의 환율·금리 API를 세 번째 갈래로 붙이고, 반도체나 이차전지처럼 산업군 종목을 Data Table에 묶은 뒤 "이 지표 조합에서 이 산업군을 어떻게 볼지"를 별도 서브워크플로우가 답하게 하는 식입니다.

면접에서 "HTTP Request 노드를 왜 AI로 만들었나요?"라는 질문이 오면, 공공 API는 내장 노드가 없어서 문서를 읽고 파라미터를 옮기는 게 병목인데 그 부분을 cURL 생성으로 표준화하면 API가 바뀌어도 같은 속도로 붙일 수 있다고 답하면 됩니다. "왜 Aggregate를 넣었나요?"에는 LLM 노드가 아이템마다 실행되기 때문에 묶지 않으면 종목 수만큼 같은 리포트를 만들게 된다는 점을 설명하세요. 둘 다 노드 사용법이 아니라 비용과 구조를 이해했다는 신호입니다.

다음 액션​

다음 편은 호텔 가격입니다. Amadeus API로 특정 도시의 호텔 요금을 받아 추이를 AI가 요약하는 워크플로우인데, 이 편의 "API 문서 → cURL → Import cURL" 순서가 그대로 쓰이고, 인증이 쿼리 키 하나에서 OAuth 토큰 발급으로 한 단계 복잡해집니다. 공공 API로 손에 익힌 방법이 상용 API에서도 통하는지 확인하는 편이 될 겁니다.