Learning Objectives
이번 주 학습 목표
1
Google Sheets OAuth2 자격증명을 설정하고 n8n에서 Append Row로 데이터를 기록할 수 있다
2
Schedule Trigger로 매일 정해진 시각에 자동 실행되는 워크플로를 구축할 수 있다
3
Code 노드 JavaScript로 RSI·이동평균·볼린저밴드 3대 기술적 지표를 직접 계산할 수 있다
4
포트폴리오 대시보드에 조건부 서식을 적용해 과매수·과매도를 한눈에 식별할 수 있다
이번 주의 한 문장: "숫자는 기억하고, AI는 판단한다." 이번 주는 AI가 판단할 재료인 기술적 지표 1차원 전체를 시트에 쌓아 올립니다.
Progress · 3D Framework
지금 어디에 있는가 — 3D 프레임 진행도
8주 여정 중 1차원(가격)을 완성하는 단계입니다. 이번 주가 끝나면 여러분은 "가격은 물론 RSI·볼린저·이동평균까지 매일 자동으로 기록되는 시트"를 갖게 됩니다. 이 시트가 바로 W3~W4 AI 에이전트의 주 식량입니다.
📊
1차원 — 가격 + 지표 ✓
W2(이번 주)에 완성. 시세 + RSI + 이동평균 + 볼린저밴드까지 자동 계산·기록.
📰
2차원 — 뉴스 센티먼트
W3에서 추가. Alpha Vantage NEWS_SENTIMENT로 "왜" 차원 탑재.
📈
3차원 — 차트 Vision
W4에서 추가. Chart-IMG + Claude Vision으로 패턴 인식.
🤖 에이전트 관점에서 본 이번 주
여전히 "에이전트"는 아닙니다. 하지만 에이전트의 컨텍스트 창을 채우는 작업입니다. AI에게 "판단해줘"라고 할 때 현재 가격만 주는 것과 "가격+RSI+이동평균+볼린저+어제 가격 대비 변동"을 주는 것은 판단의 질이 완전히 다릅니다. 시트는 에이전트의 기억 장치입니다.
Session 1 · 60min
1교시 — Google Sheets 연동 (OAuth2 세팅)
n8n의 Google Sheets 노드는 공식 코어 노드로 Append Row(행 추가) · Update Row(행 업데이트) · Get Rows(행 조회) 등 주요 작업을 모두 지원합니다. 가장 큰 장벽은 OAuth2 인증인데, n8n Cloud에서는 두 가지 선택지가 있습니다.
옵션 1: Managed OAuth (n8n Cloud 전용, 가장 빠름) ⭐
n8n Cloud 사용자라면 Google Cloud Console 설정 없이 바로 로그인할 수 있는 "Sign in with Google" 방식이 제공됩니다. 초청 워크스페이스(hyeok2.app.n8n.cloud)를 쓰는 수강생은 이 옵션을 권장합니다.
Managed OAuth 설정 (2분)
- 워크플로에 Google Sheets 노드 추가 → Operation: Append Row 선택
- Credential 드롭다운에서 Create new credential 클릭
- Authentication 방식에서 OAuth2 (Recommended) 선택
- Sign in with Google 버튼 클릭 → 팝업에서 본인 Google 계정 선택
- 권한 승인 (Sheets 읽기/쓰기) → 자동으로 n8n으로 돌아옴 → Connection Successful 표시
옵션 2: Custom OAuth (Docker 셀프호스팅 사용자)
로컬 Docker나 자체 서버로 n8n을 운영하는 경우 Google Cloud Console에서 OAuth 클라이언트를 직접 만들어야 합니다. 본 과정에서는 과제가 아닌 참고용으로만 설명합니다.
📌 요약 — 커스텀 OAuth가 필요한 단계:
① Google Cloud Console 프로젝트 생성 · ② Sheets API + Drive API 활성화 · ③ OAuth Consent Screen 설정 (Test User에 본인 이메일 추가 필수) · ④ OAuth Client ID 생성 (Web Application) · ⑤ n8n의 callback URL을 Authorized redirect URIs에 등록 · ⑥ Client ID / Secret을 n8n Credentials에 붙여넣기. 상세는 docs.n8n.io의 Google credentials 문서 참고.
연습용 스프레드시트 만들기
Google Sheets 준비 (3분)
- Google Drive에서 새 Sheets 생성 → 이름
Invest_Dashboard_W2
- 시트 탭 1:
portfolio — 헤더 행 작성: timestamp | ticker | price | change_pct | currency
- 시트 탭 2:
indicators — 헤더 행 작성: timestamp | ticker | price | rsi14 | ma20 | bb_upper | bb_lower | signal
- URL에서 Spreadsheet ID 복사 (
/d/와 /edit 사이 문자열)
- n8n의 Google Sheets 노드에서 Document: By ID → ID 붙여넣기
첫 Append Row 실행
📊
Google Sheets — Append Row
OUTPUT
W1 마지막 Set 노드 뒤에 연결. 이전 노드의 필드를 컬럼에 매핑합니다.
Resource: Sheet Within Document
Operation: Append Row
Document: By ID → {Spreadsheet ID}
Sheet: portfolio
Mapping Column Mode: Map Automatically
🚨 OAuth 자주 겪는 문제
"Access denied" 또는 403 에러
(Custom OAuth) Google Cloud Console의 OAuth Consent Screen → Test Users에 본인 Gmail을 추가하지 않은 경우. Test User 등록 후 재시도.
"Redirect URI mismatch"
n8n Credential 화면에 표시된 OAuth Redirect URL을 Google Cloud Console의 Authorized redirect URIs에 정확히 복사. http/https, 포트번호, 슬래시까지 완전 일치 필요.
Append는 성공하는데 빈 행이 추가됨
Mapping Column Mode가 Map Manually인데 필드를 지정하지 않은 경우. Map Automatically로 바꾸거나, 수동 매핑에서 컬럼명-값 페어를 명시적으로 지정.
Session 2 · 30min
2교시 — Schedule Trigger로 자동화 전환
W1에서는 Manual Trigger로 "Execute Workflow" 버튼을 눌러야만 실행됐습니다. 이제 Schedule Trigger로 바꿔 매일 정해진 시각에 무인 실행되도록 만듭니다.
⏰
Schedule Trigger
TRIGGER
Cron 표현식 또는 간단한 Trigger Interval로 설정 가능. 초보자는 Trigger Interval 권장.
Trigger Rules: Add Rule
Trigger Interval: Days
Days Between Triggers: 1
Trigger at Hour: 9
Trigger at Minute: 0
Cron 표현식 (고급 옵션)
더 세밀한 스케줄이 필요하면 Cron Expression을 직접 씁니다. crontab.guru에서 검증하세요.
| 표현식 | 의미 | 투자 활용 |
0 9 * * * | 매일 오전 9시 | 한국장 개장 30분 전 브리핑 |
0 9 * * 1-5 | 평일 오전 9시 | 주말 제외 루틴 |
0 22 * * 1-5 | 평일 오후 10시 | 미국장 개장 30분 전 |
*/5 9-15 * * 1-5 | 평일 장중 5분마다 | 실시간 모니터링 (W4+) |
0 15 * * 1-5 | 평일 오후 3시 | 한국장 마감 30분 전 체크 |
⚠️ 타임존 주의: n8n Cloud의 기본 타임존은 계정 생성 시 설정됩니다. 강사 워크스페이스는 Asia/Seoul로 설정되어 있어 0 9 * * *는 한국시간 오전 9시를 의미합니다. Docker 셀프호스팅은 기본 America/New_York이므로 환경변수 GENERIC_TIMEZONE=Asia/Seoul를 반드시 설정.
Session 3 · 90min
3교시 — Code 노드로 기술적 지표 계산
기술적 지표는 투자에서 가장 오래된 도구지만 여전히 강력합니다. Claude나 GPT도 훈련 데이터에서 이 지표들을 수없이 봤기 때문에 숫자로 입력받으면 즉시 문맥을 이해합니다. 이번 3교시에서는 3대 지표를 JavaScript로 직접 구현합니다.
💡 왜 공식을 직접 구현하나: Alpha Vantage 같은 API도 지표를 제공하지만 API 호출 횟수가 낭비되고 커스터마이징이 어렵습니다. JavaScript 한 번 작성해두면 무료로 무한 재사용 + RSI 기간(14일/21일 등) 자유 변경 가능.
지표 1: 단순 이동평균 (MA · Moving Average)
📊 20일 이동평균 (MA20)
TREND
최근 20일 종가의 평균.
가격이 MA20 위면 상승 추세, 아래면 하락 추세로 단순 판단. 가장 기초적이지만 여전히 핵심 지표.
MA20 = (C₁ + C₂ + ... + C₂₀) / 20
가격 > MA20: 상승
가격 < MA20: 하락
가격 ≈ MA20: 중립
지표 2: RSI (Relative Strength Index)
⚡ RSI(14) — 상대강도지수
MOMENTUM
최근 14일간 상승폭과 하락폭의 비율을 0~100으로 표현.
70 초과 = 과매수(조정 가능성), 30 미만 = 과매도(반등 가능성). 단일 지표로는 가장 널리 쓰이는 모멘텀 지표.
RS = AvgGain / AvgLoss
RSI = 100 − (100 / (1 + RS))
RSI < 30: 과매도
RSI > 70: 과매수
30 ≤ RSI ≤ 70: 정상
지표 3: 볼린저밴드 (Bollinger Bands)
🎯 볼린저밴드 (20, 2)
VOLATILITY
MA20을 중심으로 표준편차 × 2만큼 위/아래에 밴드를 그림.
가격이 상단 밴드 돌파 시 과열, 하단 터치 시 바닥 시도. 밴드 폭이 좁아지는 스퀴즈는 큰 변동성 직전 신호.
Middle = MA20
Upper = MA20 + 2σ
Lower = MA20 − 2σ
Upper 돌파: 과열
Lower 터치: 반등 시도
밴드 스퀴즈: 변동성 임박
Code 노드 실습 — 3대 지표 통합 계산기
Yahoo Finance의 chart 엔드포인트는 종가 배열을 제공합니다. 이 배열을 받아 3개 지표를 한 번에 계산하는 Code 노드를 작성합니다.
입력 데이터 확인 (이전 HTTP Request 노드)
Yahoo 응답에서 종가 배열은 $json.chart.result[0].indicators.quote[0].close에 있습니다. range=60d로 호출하면 약 60개 종가가 옵니다.
Code 노드 — JavaScript (Run Once for Each Item)
// ─── 입력: Yahoo chart 응답에서 종가 배열 추출 ───
const raw = $input.item.json.chart.result[0].indicators.quote[0].close;
const closes = raw.filter(v => v !== null);
const symbol = $input.item.json.chart.result[0].meta.symbol;
const currentPrice = closes[closes.length - 1];
// ─── 1. 20일 이동평균 ───
function calcMA(arr, period) {
const slice = arr.slice(-period);
const sum = slice.reduce((a, b) => a + b, 0);
return sum / slice.length;
}
const ma20 = calcMA(closes, 20);
// ─── 2. RSI(14) — Wilder 방식 ───
function calcRSI(arr, period) {
const recent = arr.slice(-(period + 1));
let gains = 0;
let losses = 0;
for (let i = 1; i < recent.length; i++) {
const diff = recent[i] - recent[i - 1];
if (diff > 0) gains += diff;
else losses += -diff;
}
const avgGain = gains / period;
const avgLoss = losses / period;
if (avgLoss === 0) return 100;
const rs = avgGain / avgLoss;
return 100 - (100 / (1 + rs));
}
const rsi14 = calcRSI(closes, 14);
// ─── 3. 볼린저밴드(20, 2) ───
function calcBB(arr, period, mult) {
const slice = arr.slice(-period);
const mean = slice.reduce((a, b) => a + b, 0) / period;
const variance = slice.reduce((a, b) => a + (b - mean) * (b - mean), 0) / period;
const std = Math.sqrt(variance);
return {
upper: mean + mult * std,
middle: mean,
lower: mean - mult * std
};
}
const bb = calcBB(closes, 20, 2);
// ─── 4. 신호 판정 ───
let signal = 'HOLD';
if (rsi14 < 30 && currentPrice < bb.lower) {
signal = 'BUY_STRONG';
} else if (rsi14 < 35) {
signal = 'BUY_WATCH';
} else if (rsi14 > 70 && currentPrice > bb.upper) {
signal = 'SELL_STRONG';
} else if (rsi14 > 65) {
signal = 'SELL_WATCH';
}
// ─── 5. 출력 ───
return {
timestamp: new Date().toISOString(),
ticker: symbol,
price: currentPrice.toFixed(2),
rsi14: rsi14.toFixed(2),
ma20: ma20.toFixed(2),
bb_upper: bb.upper.toFixed(2),
bb_lower: bb.lower.toFixed(2),
signal: signal
};
💡 Code 노드 모드 선택:
Run Once for Each Item: 티커마다 독립적으로 실행(권장). $input.item.json으로 접근.
Run Once for All Items: 모든 티커를 한 번에 처리. $input.all()로 배열 접근.
검증 — 계산 결과 확인
Code 노드 실행 후 Output 탭에서 rsi14 값이 0~100 범위인지, bb_upper > bb_lower인지, ma20이 최근 종가와 비슷한 수준인지 확인합니다. 수치 이상이면 대부분 종가 배열에 null이 섞여있거나 기간 부족(60일 미만)이 원인입니다.
Session 4 · 40min
4교시 — 포트폴리오 대시보드 완성
전체 워크플로 구조
🔧 W2 최종 워크플로 — 매일 9시 자동 실행
Schedule→평일 오전 9시 자동 실행 (0 9 * * 1-5)
HTTP ×3→Yahoo(주식·지수, range=60d) · CoinGecko · FRED — 각 자산 60일 히스토리 수집
Merge→3개 응답을 배열로 합치기 (Mode: Append All)
Code (Indicators)→각 티커에 대해 RSI·MA20·볼린저·신호 계산 (Run for Each Item)
Sheets (indicators)→indicators 탭에 계산 결과 Append Row — 시간별 이력 누적
Sheets (portfolio)→portfolio 탭에 가격 스냅샷 Append Row (별도 이력)
예상 출력 — indicators 탭 미리보기
| timestamp | ticker | price | rsi14 | ma20 | bb_upper | bb_lower | signal |
| 1 | 2026-04-21 09:00 | AAPL | 178.32 | 58.4 | 175.80 | 182.10 | 169.50 | HOLD |
| 2 | 2026-04-21 09:00 | SPY | 512.45 | 62.1 | 508.20 | 518.70 | 497.70 | HOLD |
| 3 | 2026-04-21 09:00 | bitcoin | 62450.00 | 28.3 | 68200 | 72500 | 63900 | BUY_STRONG |
| 4 | 2026-04-21 09:00 | ethereum | 3120.50 | 34.8 | 3280 | 3450 | 3110 | BUY_WATCH |
| 5 | 2026-04-21 09:00 | ^GSPC | 5142.80 | 71.2 | 5080 | 5150 | 5010 | SELL_WATCH |
조건부 서식 적용 (Sheets에서 직접 설정)
signal 컬럼에 색상 자동 표시
- Sheets에서 H열(signal) 전체 선택 → 서식 → 조건부 서식
- 규칙 1:
BUY_STRONG 포함 → 배경 진한 녹색, 텍스트 흰색
- 규칙 2:
BUY_WATCH 포함 → 배경 연두색
- 규칙 3:
SELL_STRONG 포함 → 배경 빨간색, 텍스트 흰색
- 규칙 4:
SELL_WATCH 포함 → 배경 연한 빨강
- 규칙 5:
HOLD → 배경 노란색 (기본)
✨ 체감 효과: 매일 오전 9시에 Sheets를 열면 한눈에 과매수 빨간색·과매도 녹색이 보입니다. 이게 진짜 "자동화된 대시보드"입니다. 수동으로 1시간 걸리던 작업이 0분이 됩니다.
Test & Debug
실행 테스트와 디버깅
🚨 이번 주 자주 발생하는 오류
Code 노드에서 "closes is undefined"
이전 노드 경로 확인. Yahoo는 chart.result[0].indicators.quote[0].close, CoinGecko는 OHLC 엔드포인트 사용 시 $json이 직접 2차원 배열. 자산군별 경로 차이에 주의.
RSI가 항상 100 또는 0으로 계산됨
계산 기간 동안 상승만 있었거나 하락만 있었던 경우(분모가 0). 정상 동작. range를 60d 이상으로 늘리면 완화.
Schedule Trigger가 활성화됐는데 실행 안 됨
워크플로를 Active 토글 ON으로 저장해야 스케줄이 작동. 비활성 상태에서는 Manual 실행만 가능. 우측 상단 Inactive/Active 스위치 확인.
Append Row가 헤더 행을 덮어씀
Header Row 파라미터 확인. 기본 1로 지정(첫 행 = 헤더). First Data Row는 2로 설정.
Completion Checklist
이번 주 완료 체크리스트
✅ 아래 항목이 모두 체크되면 W2 완료
Google Sheets OAuth2 자격증명 설정 완료 (Managed 또는 Custom)
Invest_Dashboard_W2 스프레드시트에 portfolio·indicators 두 탭 준비
Manual 실행으로 Sheets에 첫 행 Append 성공
Schedule Trigger를 평일 오전 9시로 설정하고 워크플로 Active 전환
Code 노드로 RSI·MA20·볼린저밴드 계산 결과가 합리적 범위(RSI 0~100) 확인
signal 컬럼에 BUY/SELL/HOLD 값이 조건에 따라 정확히 생성됨
Sheets 조건부 서식으로 signal 색상 자동 표시 작동
다음날 오전 9시 이후 시트에 새 행이 자동으로 추가됐는지 확인
Homework
이번 주 과제
📝 HOMEWORK · W2
나만의 신호 규칙 + 추가 지표 구현
- 본인 전략에 맞게 signal 판정 규칙 수정 (예: RSI 임계값 25/75로 변경, 볼린저 조건 제거 등)
- Code 노드에 MACD 또는 5일 이동평균 중 하나 추가 구현
- 전일 대비 수익률 컬럼 추가 (
change_pct = (오늘 - 어제) / 어제 × 100)
- Scheduler를 미국장 개장 시각(한국 22:30)용으로 복제 생성
- 선택 챌린지: indicators 탭에서 signal이 BUY_STRONG인 종목만 필터링해 새 "alerts" 탭에 복사
💡 W3 미리보기: 다음 주에는 2차원(뉴스 센티먼트)를 추가합니다. Alpha Vantage NEWS_SENTIMENT API로 종목별 뉴스를 자동 수집하고, AI Agent 노드로 Claude를 연결해 "오늘의 한 줄 코멘트"를 생성합니다. W2에서 쌓은 indicators 시트 데이터가 Claude의 컨텍스트로 들어가 가격+뉴스 통합 판단이 가능해집니다.
References
참고 자료
| 자료 | 용도 |
n8n Google Sheets Docs
docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.googlesheets | 모든 operation과 파라미터 레퍼런스 |
n8n Google Credentials
docs.n8n.io/integrations/builtin/credentials/google | OAuth2 단계별 설정 공식 가이드 |
n8n Code node Docs
docs.n8n.io/code/code-node | JavaScript·Python 모드, $input 문법 |
| crontab.guru | Cron 표현식 실시간 검증·번역 |
| Investopedia — RSI | RSI 공식과 해석 가이드 (영문 표준 설명) |
| TradingView Chart School | 볼린저밴드·MA 등 지표별 시각 예제 |