아래 글을 통해 네이버 증권에서 데이터를 크롤링해서 [전일 데이터]를 [매일 원하는 시간]에 자동으로 구글 스프레드시트에 업데이트하는 방법을 알아봤다.
▼매일 자동으로 전일 데이터 업데이트하는 방법▼
https://ironyoo.tistory.com/36
[AI자동화] 네이버 증권 데이터 구글 스프레드시트 매일 업데이트하기(API 연동)
📌이 글에 나오는 툴 정보📌1️⃣ n8n -url: https://n8n.io/ -여러 api를 한 번에 연동해 나만의 AI 에이전트 생성 가능 "구독 필요"2️⃣firecrawl -url: https://www.firecrawl.dev/ -네이버 쇼핑, 네이버 뉴스, 구
ironyoo.tistory.com
이번 글에서는 [전 일 데이터]가 아닌 [최근 7일 혹은 최근 30일]과 같은 특정 기간 데이터를 업데이트하는 방법을 공유하려 한다. 이전 글에서 이어지는 내용이므로, 이번 글을 위해 이전 글을 우선 읽는 것을 권한다.
최근 7거래일 기준 데이터를 업데이트하려면,
n8n의 Loop over items을 활용한다. 아래 이미지에서 ①~③ 과정을 반복하며 일별 데이터를 업데이트하는 것이다. 여기서 기간을 한정 짓지 않으면 무한으로 loop이 돌게 된다. 그래서 code in javascript에서 기간 조건을 지정하는 코드를 작성해 준다. 코딩을 1도 모르는 나는 챗gpt가 작성해준 코드를 그대로 복붙해 해결했다.
✨오늘의 작업 요약✨

첫번째, 기본 세팅
①우선 manual trigger를 추가한다. (각 과정마다 수동으로 실행해보고 결과를 확인, 오류 여부를 파악하기 위함)
②loop over items를 추가해 준다. 여기서 batch size 는 1로 설정해두어도 된다.
batch size란?
한 번의 루프 사이클에서 몇 개의 item을 묶어서 다음 노드로 보낼지를 의미한다.
만약 20개 데이터를 보내야 한다면?
1 ► 1개씩 처리 (20번 반복)
5 ► 5개씩 처리 (4번 반복)
20 ► 한 번에 전부 처리 (1번 반복)


그러면 아래와 같이 loop이 나오게 되는데 loop "+"를 클릭해 원하는 기능을 계속 붙이면 된다. (나중에 replace me는 삭제하면 된다.)

아래 작업에서 ①번과 ③번은 이전 글에 자세히 적어두었으니 제외하고 설명하겠다. (이전 글과 동일하게 설정하면 된다.)
►이전 글 참고► https://ironyoo.tistory.com/36

두번째, Code in javascript 를 활용해 최근 7일 거래일 기준 데이터 업데이트하기
이전 글을 통해 https request를 연결했다면 "+"를 클릭해 code in javascript를 연결한다.

그러면 아래와 같은 화면이 뜨는데, 기본적으로 적혀있는 code는 삭제한다.

챗gp에게 아래 기준으로 n8n code in javascript에 넣을 코드를 작성해 달라고 이야기하면 코드를 뱉어낸다.
해외 지표의 전날 종가 데이터가 필요하다면?
아래 코드를 복붙하면 된다.
[기준1] S&P500/다우지수 등 해외 지표에 적용할 코드로 "전날 종가 데이터 업데이트"
1. 최근 7거래일 데이터가 업데이트되어야 함
2. 월요일의 경우, 전 주 금요일 데이터가 업데이트되어야 함
3. 한국 시간 기준으로 전날 종가 데이터가 들어가야 함
(예를 들어 구글 스프레드시트 2월 20일 행에 2월 19일 데이터가 들어가야 함)
4. 주말에는 데이터가 업데이트되지 않도록 할 것
5. 일별 시세에 데이터가 없다면 공란으로 둘 것
▼기준1에 해당하는 코드▼
// n8n Function node (copy & paste)
// 요구조건 최종 반영
// 1) 최근 7거래일(= 최근 7개 KST 평일 행) 업데이트
// 2) 월요일(행 날짜)에는 전주 금요일(종가) 데이터로 채움
// 3) KST 기준 전날 종가를 해당 날짜 행에 업데이트
// 예: KST 2/20 행에는 2/19 종가
// 4) 전날 종가 데이터가 없으면 공란
// --- KST 날짜를 안정적으로 얻기 (Asia/Seoul 고정)
function getKstYMD(date = new Date()) {
const fmt = new Intl.DateTimeFormat("en-CA", {
timeZone: "Asia/Seoul",
year: "numeric",
month: "2-digit",
day: "2-digit",
});
const parts = fmt.formatToParts(date);
const y = Number(parts.find(p => p.type === "year").value);
const m = Number(parts.find(p => p.type === "month").value);
const d = Number(parts.find(p => p.type === "day").value);
return { y, m, d };
}
const pad2 = (n) => String(n).padStart(2, "0");
const iso = (y, m, d) => `${y}-${pad2(m)}-${pad2(d)}`;
// --- 날짜 연산: UTC Date로(YYYY-MM-DD 단위) 처리
function ymdToUtcDate(y, m, d) {
return new Date(Date.UTC(y, m - 1, d)); // UTC 00:00
}
function addDaysUtc(dt, days) {
const x = new Date(dt.getTime());
x.setUTCDate(x.getUTCDate() + days);
return x;
}
function utcToIso(dt) {
return `${dt.getUTCFullYear()}-${pad2(dt.getUTCMonth() + 1)}-${pad2(dt.getUTCDate())}`;
}
function dowUtc(dt) {
return dt.getUTCDay(); // 0 Sun ... 6 Sat
}
function isWeekdayUtc(dt) {
const d = dowUtc(dt);
return d >= 1 && d <= 5; // Mon~Fri
}
function prevTradingDayUtc(dt) {
// dt는 "타겟(행) 날짜" (월~금)
// 전날 종가: 기본 -1일
// 단, 월요일이면 -3일(전주 금요일)
const d = dowUtc(dt);
if (d === 1) return addDaysUtc(dt, -3); // Mon -> Fri
return addDaysUtc(dt, -1); // Tue~Fri -> previous day
}
// --- 1) KST "오늘" 날짜 (행 생성 끝점은 오늘 포함)
const { y, m, d } = getKstYMD(new Date());
const kstTodayUtc = ymdToUtcDate(y, m, d); // KST 오늘 날짜를 UTC Date로 표현
// --- 2) 최근 7개 KST 평일(월~금) "행 날짜" 생성 (오늘 포함, 오래된->최신)
const targetDates = [];
let cursor = new Date(kstTodayUtc.getTime());
while (targetDates.length < 7) {
if (isWeekdayUtc(cursor)) targetDates.push(new Date(cursor.getTime()));
cursor = addDaysUtc(cursor, -1);
}
targetDates.reverse();
// --- 3) Firecrawl 결과 / daily 파싱 (yyyy.MM.dd -> yyyy-mm-dd)
const root = items[0].json;
const symbol = root.symbol ?? "SPX";
const daily =
root?.data?.json?.daily ??
root?.data?.formats?.[0]?.json?.daily ??
root?.daily ??
[];
const dailyMap = new Map(); // isoDate -> record
for (const r of daily) {
if (!r?.date) continue;
const parts = String(r.date).split(".");
if (parts.length !== 3) continue;
const yy = Number(parts[0]);
const mm = Number(parts[1]);
const dd = Number(parts[2]);
if (!yy || !mm || !dd) continue;
dailyMap.set(iso(yy, mm, dd), r);
}
const updatedAt = new Date().toISOString().slice(0, 19).replace("T", " ");
// --- 4) 출력: 행 날짜(target)마다 "전날 종가(source)"를 강제로 조회
const output = targetDates.map((targetUtc) => {
const targetIso = utcToIso(targetUtc);
// ✅ 핵심: 타겟 날짜의 "전날 종가" 날짜로 강제 이동
const sourceUtc = prevTradingDayUtc(targetUtc);
const sourceIso = utcToIso(sourceUtc);
const found = dailyMap.get(sourceIso) ?? null;
return {
json: {
// 행(타겟) 기준 키/날짜
key: `${symbol}_${targetIso}`,
symbol,
date: targetIso,
// 값은 전날 종가(source) 기준
close: found?.close ?? "",
diff: found?.diff ?? "",
rate: found?.rate ?? "",
volume: found?.volume ?? "",
amount: found?.amount ?? "",
updated_at: updatedAt,
// 디버깅용 (원치 않으면 삭제)
source_date: found ? sourceIso : "",
is_missing: found ? false : true,
rule_applied: dowUtc(targetUtc) === 1 ? "Mon->Fri(prev close)" : "Prev day close",
}
};
});
return output;
❗️단, 해당 코드는 S&P500 용으로 위 내용에서 아래 부분을 수정해야 한다.
const symbol = root.symbol ?? "SPX";
예를 들면,
네이버 증권 S&P500 페이지 링크를 보면 "?symbol=SPI@SPX" 이 부분이 있다.
https://finance.naver.com/world/sise.naver?symbol=SPI@SPX
네이버 증권 다우지수 페이지 링크도 마찬가지다.
https://finance.naver.com/world/sise.naver?symbol=DJI@DJI
그래서 만약 위 코드를 다우지수용으로 변경하려면 코드 일부를 아래와 같이 수정해야 한다.
const symbol = root.symbol ?? "DJI";
만약 국내 지표라 전날 종가 데이터가 필요없다면,
아래 코드를 복붙하면 된다.
[기준2] 코스피 등 국내 지표에 적용할 코드로 "당일 종가 데이터 업데이트"
1. 최근 7거래일 데이터가 업데이트되어야 함
2. 월요일의 경우, 전 주 금요일 데이터가 업데이트되어야 함
3. 주말에는 데이터가 업데이트되지 않도록 할 것
4. 일별 시세에 데이터가 없다면 공란으로 둘 것
// n8n Code node
// 목적: KOSPI 최근 7거래일 데이터 생성
// - 당일 종가는 당일 날짜에 업데이트
// - 최근 7거래일만
// - 주말은 네이버 daily에 없으므로 자동 제외
const root = items[0].json;
const symbol = "KOSPI";
// daily 경로 방어 처리
const daily =
root?.data?.json?.daily ??
root?.data?.formats?.[0]?.json?.daily ??
root?.daily ??
[];
if (!daily || daily.length === 0) {
return [];
}
const pad2 = (n) => String(n).padStart(2, "0");
// yyyy.MM.dd → 파싱
const parsed = daily
.filter(r => r?.date)
.map(r => {
const parts = String(r.date).split(".");
if (parts.length !== 3) return null;
const y = Number(parts[0]);
const m = Number(parts[1]);
const d = Number(parts[2]);
if (!y || !m || !d) return null;
const dateObj = new Date(y, m - 1, d);
const isoDate = `${y}-${pad2(m)}-${pad2(d)}`;
return { ...r, dateObj, isoDate };
})
.filter(Boolean);
// 최신순 정렬
parsed.sort((a, b) => b.dateObj - a.dateObj);
// 최근 7거래일
const last7 = parsed.slice(0, 7);
const updatedAt = new Date().toISOString().slice(0,19).replace("T"," ");
return last7.map(r => ({
json: {
key: `${symbol}_${r.isoDate}`,
symbol,
date: r.isoDate, // ✅ 당일 종가는 당일 날짜에 그대로 들어감
close: r.close ?? "",
diff: r.diff ?? "",
rate: r.rate ?? "",
volume: r.volume ?? "",
amount: r.amount ?? "",
updated_at: updatedAt,
is_missing: false
}
}));
결론적으로 아래와 같이 지표들이 자동으로 들어가는 것을 볼 수 있다.

매일 정해진 시간에 맞춰 업데이트하려면 schedule trigger를 추가해서 업데이트 주기, 시간을 설정하면 된다.

아래 2개 글과 이번 글까지 내용을 잘 활용하면, 데일리로 업데이트해야 하는 데이터들을 자동화할 수 있다. 예를 들면 마케터의 경우 구글 스프레드시트로된 데일리 리포트에 로우 데이터 업데이트하는 걸 자동화시킬 수 있다. 메타/구글 API 연동해 구글 스프레드시트에 매일 최근 30일 데이터를 업데이트하도록 하면 되기 때문.
| 1 | [AI자동화] 경제 지표 구글 스프레드시트 매일 업데이트하기(구글 파이낸스 API 연동) >>알수있는 것: N8N에 기본적인 API 연동 방법 |
https://ironyoo.tistory.com/33 |
| 2 | [AI자동화] 네이버 증권 데이터 구글 스프레드시트 매일 업데이트하기(크롤링, API 연동) >>알수있는 것: 전날 종가와 같은 데이터 업데이트 |
https://ironyoo.tistory.com/36 |
아직 터득해 나가고 있는 중이지만 지금까지 쓴 글은 걸음마에 불과하다. N8N로 해보고 싶은 것들이 많아 여러 시도 중인데, 그 결과는 이곳에 하나하나 차근히 올리겠다.
'AI > [AI_Personal] trial and error' 카테고리의 다른 글
| [AI자동화] 네이버 증권 데이터 구글 스프레드시트 매일 업데이트하기(크롤링, API 연동) (0) | 2026.02.16 |
|---|---|
| [AI자동화] 경제 지표 구글 스프레드시트 매일 업데이트하기(구글 파이낸스 API 연동) (0) | 2026.02.09 |
| GPTs action에 api 연동하여 쇼핑검색, 뉴스 크롤링하기(네이버, 구글) (0) | 2024.06.25 |