n8n이란?
오픈소스 워크플로우 자동화 플랫폼 — AI와 400개 이상의 서비스를 코드 없이 연결하는 셀프호스팅 자동화 엔진 완전 가이드
# Docker 한 줄로 즉시 실행
docker run -it --rm -p 5678:5678 -v ~/.n8n:/home/node/.n8n n8nio/n8n
# → http://localhost:5678 접속, 계정 생성 후 바로 사용
n8n이란 무엇인가?
n8n(발음: "n-eight-n" 또는 "nodemation")은 독일 스타트업 n8n GmbH가 2019년 공개한 오픈소스 워크플로우 자동화 플랫폼입니다. 코드 없이 시각적 에디터로 자동화를 구축하면서도, 필요하면 JavaScript/Python 코드를 자유롭게 삽입할 수 있는 로우코드(Low-Code) 접근 방식이 특징입니다.
2024~2025년 AI 붐과 함께 n8n은 LLM 기반 AI 에이전트 워크플로우를 가장 쉽게 구축할 수 있는 플랫폼으로 주목받고 있습니다. Claude, OpenAI, Gemini 등 주요 AI API와의 기본 통합을 제공하며, 메모리·도구 사용·RAG 파이프라인까지 노드 조합으로 완성할 수 있습니다.
핵심 특징
- 셀프호스팅 우선: 데이터를 자체 서버에서 처리 — 프라이버시와 비용 절감
- 공정 코드 라이선스: Sustainable Use License — 영리 목적 SaaS 재판매 제외 시 무료
- 400+ 통합 노드: Slack, GitHub, PostgreSQL, HTTP Request, AI 노드 등
- 시각적 에디터: 드래그 앤 드롭으로 워크플로우 설계
- 코드 삽입 가능: Code 노드로 JS/Python 코드 직접 실행
- AI 네이티브: AI Agent, LLM Chain, 벡터 스토어 노드 기본 제공
- 다양한 트리거: Webhook, Cron, 이벤트, 수동 실행
- 실행 이력: 각 실행의 입력/출력 데이터를 시각적으로 확인
- 팀 협업: 사용자 권한(Owner/Member/Viewer), 프로젝트 분리
- 템플릿 마켓플레이스: 900개+ 공개 워크플로우 템플릿 즉시 사용
n8n 아키텍처: 외부 트리거 → 워크플로우 노드 체인 → 액션 대상
n8n vs 다른 자동화 도구 비교
| 항목 | n8n | Zapier | Make (Integromat) | Airflow | Temporal |
|---|---|---|---|---|---|
| 호스팅 | 셀프호스팅(무료)/Cloud | Cloud 전용 | Cloud 전용 | 셀프호스팅 | 셀프호스팅/Cloud |
| 비용 | 셀프호스팅 무료 | $20~$69+/월 | $9~$29+/월 | 인프라만 | 인프라만 |
| 코드 작성 | 선택적(JS/Python) | 제한적 | 제한적 | Python 필수 | Go/TypeScript |
| AI 통합 | 전용 AI 노드 다수 | 기본 OpenAI | OpenAI 지원 | 커스텀 코드 | 커스텀 코드 |
| 데이터 프라이버시 | 완전 자체 보관 | Zapier 경유 | Make 경유 | 완전 자체 | 완전 자체 |
| 학습 난이도 | 중간 | 쉬움 | 중간 | 높음 | 매우 높음 |
| 오픈소스 | ✅ Sustainable Use | ❌ | ❌ | ✅ Apache 2.0 | ✅ MIT |
| 실시간 이벤트 | ✅ Webhook | ✅ (제한) | ✅ | 제한적 | ✅ |
| 적합 대상 | 개발자·기업 자동화 | 비개발자 | 비개발자 | 데이터 엔지니어 | 백엔드 개발자 |
- 민감한 데이터(고객 정보, 소스코드)를 외부 서버에 보내고 싶지 않을 때
- AI 에이전트 워크플로우를 빠르게 프로토타이핑할 때
- 월 구독 비용을 절감하고 싶을 때 (팀 규모 확장 시)
- 커스텀 로직이 필요하지만 전체를 코드로 짜기엔 부담될 때
- 복잡한 조건 분기, 루프, 멀티스텝 자동화가 필요할 때
설치 방법
방법 1: Docker (가장 추천)
# 기본 실행 (데이터 보존 포함)
docker run -d \
--name n8n \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
--restart unless-stopped \
n8nio/n8n
# 특정 버전 고정 (운영 환경 권장)
docker run -d \
--name n8n \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
--restart unless-stopped \
n8nio/n8n:2.16.1 # 버전 명시
방법 2: Docker Compose (운영 환경)
# docker-compose.yml
version: '3.8'
services:
n8n:
image: n8nio/n8n:2.16.1
restart: unless-stopped
ports:
- "5678:5678"
environment:
- WEBHOOK_URL=https://n8n.yourdomain.com
- GENERIC_TIMEZONE=Asia/Seoul
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=n8n
- DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:15-alpine
restart: unless-stopped
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n"]
interval: 10s
retries: 5
volumes:
n8n_data:
postgres_data:
# .env 파일
N8N_ENCRYPTION_KEY=$(openssl rand -hex 32)
DB_PASSWORD=$(openssl rand -hex 16)
# 실행
docker compose up -d
docker compose logs -f n8n
방법 3: npm (로컬 개발)
# Node.js 20.19+ 필요
node --version # v20.19.0 이상 확인
npm install -g n8n
# 실행
n8n start
# 터널링으로 외부 Webhook 테스트 (개발용)
n8n start --tunnel
주요 환경 변수 전체 참조
| 환경 변수 | 기본값 | 설명 |
|---|---|---|
N8N_PORT | 5678 | 서버 포트 |
N8N_HOST | localhost | 서버 호스트 (0.0.0.0 = 모든 인터페이스) |
N8N_PROTOCOL | http | http 또는 https |
WEBHOOK_URL | - | 외부 Webhook URL (Nginx 뒤 배치 시) |
N8N_ENCRYPTION_KEY | 자동 생성 | 자격증명 암호화 키 (운영 필수 고정) |
N8N_USER_MANAGEMENT_JWT_SECRET | 자동 | JWT 서명 키 |
DB_TYPE | sqlite | sqlite / postgresdb / mysqldb |
DB_POSTGRESDB_* | - | PostgreSQL 연결 정보 |
EXECUTIONS_MODE | regular | regular / queue (대용량) |
EXECUTIONS_DATA_MAX_AGE | 336 (2주) | 실행 이력 보관 시간 |
EXECUTIONS_DATA_PRUNE | true | 오래된 이력 자동 삭제 |
EXECUTIONS_DATA_PRUNE_MAX_COUNT | 10000 | 최대 보관 실행 수 |
N8N_CONCURRENCY_PRODUCTION_LIMIT | -1 (무제한) | 동시 실행 워크플로우 수 |
N8N_LOG_LEVEL | info | error/warn/info/verbose/debug |
N8N_LOG_OUTPUT | console | console / file |
GENERIC_TIMEZONE | UTC | 타임존 (예: Asia/Seoul) |
N8N_DIAGNOSTICS_ENABLED | true | 원격 진단 전송 (운영 시 false 권장) |
N8N_VERSION_NOTIFICATIONS_ENABLED | true | 버전 알림 표시 |
N8N_TEMPLATES_ENABLED | true | 템플릿 마켓플레이스 표시 |
NODE_FUNCTION_ALLOW_BUILTIN | * | Code 노드에서 허용할 Node.js 내장 모듈 |
NODE_FUNCTION_ALLOW_EXTERNAL | - | Code 노드에서 허용할 npm 패키지 (쉼표 구분) |
N8N_PAYLOAD_SIZE_MAX | 16 (MB) | 최대 요청 크기 |
# 특정 패키지 허용
NODE_FUNCTION_ALLOW_EXTERNAL=lodash,moment,axios,cheerio
# 모든 패키지 허용 (보안 주의)
NODE_FUNCTION_ALLOW_EXTERNAL=*
허용된 패키지는 Code 노드에서 require('lodash')로 바로 사용 가능합니다.
핵심 개념
워크플로우(Workflow)
워크플로우는 n8n의 기본 단위입니다. 노드(Node)가 연결(Connection)로 이어진 방향 그래프입니다.
- 활성(Active): Trigger 노드가 이벤트를 감지하면 자동 실행
- 비활성(Inactive): 수동 실행 / 테스트만 가능
- 워크플로우 설정: 에러 워크플로우 지정, 타임존, 실행 제한, 저장 정책
노드(Node) 분류
| 분류 | 역할 | 주요 예시 |
|---|---|---|
| Trigger | 워크플로우 시작 이벤트 | Webhook, Schedule, Email Trigger, GitHub Trigger, Chat Trigger |
| Action | 데이터 조회·변경·전송 | HTTP Request, Slack, Gmail, PostgreSQL, Google Sheets |
| Data Transform | 데이터 가공·변환 | Code, Set, Edit Fields, Filter, Sort, Aggregate |
| Flow Control | 실행 흐름 제어 | IF, Switch, Loop, Merge, Wait, SplitInBatches |
| AI / LangChain | LLM·에이전트 실행 | AI Agent, Basic LLM Chain, OpenAI, Anthropic |
| Helper | 유틸리티 | Date & Time, Crypto, HTML Extract, RSS Read |
데이터 구조 — 아이템(Item)과 Binary
노드 간 전달 데이터는 아이템(Item) 배열입니다.
각 아이템은 json(구조화 데이터)과 선택적으로 binary(파일/이미지 등) 필드를 가집니다.
// 아이템 배열 구조
[
{
"json": {
"id": 1,
"name": "홍길동",
"email": "hong@example.com"
}
},
{
"json": { "id": 2, "name": "김철수" },
"binary": {
"data": {
"data": "base64EncodedContent...",
"mimeType": "application/pdf",
"fileName": "report.pdf",
"fileSize": 102400
}
}
}
]
// Code 노드에서 Binary 데이터 처리
const item = $input.first();
// binary 존재 확인
if (item.binary?.data) {
const { mimeType, fileName } = item.binary.data;
console.log(`파일: ${fileName} (${mimeType})`);
}
// Binary → Buffer 변환
const buffer = Buffer.from(item.binary.data.data, 'base64');
return [{ json: { size: buffer.length } }];
Paired Items (아이템 페어링)
여러 노드의 출력을 Merge할 때, 각 아이템이 어느 원본 아이템에서 나왔는지 추적하는 메커니즘입니다. Merge by Key 모드에서 올바른 JOIN을 위해 중요합니다.
// Code 노드에서 pairedItem 명시적 설정
return $input.all().map((item, idx) => ({
json: {
...item.json,
processed: true
},
pairedItem: { item: idx } // 원본 아이템 인덱스 연결
}));
표현식(Expression) 완전 가이드
n8n 노드 필드에 {{ }} 구문으로 동적 값을 삽입합니다.
내부적으로 tmpl.js 템플릿 엔진을 사용하며, 단일 JavaScript 표현식을 지원합니다.
내장 변수 전체 목록
| 변수 | 타입 | 설명 |
|---|---|---|
$json | object | 현재 노드 입력 아이템의 json 필드 |
$input | object | 현재 노드 전체 입력 (.all(), .first(), .last(), .item(n)) |
$items("노드명") | array | 특정 이름의 노드 출력 배열 |
$node["노드명"].json | object | 특정 노드의 첫 번째 아이템 json |
$node["노드명"].context | object | Loop 노드의 반복 상태(noItemsLeft, currentRunIndex 등) |
$env | object | 환경 변수 ($env.MY_VAR) |
$vars | object | n8n 전역 변수 (UI에서 설정) |
$workflow | object | 워크플로우 메타데이터 (id, name, active) |
$execution | object | 실행 정보 (id, mode, resumeUrl) |
$now | Luxon DateTime | 현재 시각 (Luxon 객체) |
$today | Luxon DateTime | 오늘 자정 (Luxon 객체) |
$prevNode | object | 이전 노드 정보 (name, outputIndex, runIndex) |
$runIndex | number | Loop 내 현재 반복 인덱스 (0부터) |
$itemIndex | number | 현재 처리 중인 아이템 인덱스 (0부터) |
$parameter | object | 노드 파라미터 값 참조 |
$secret | object | n8n Vault 시크릿 (Enterprise) |
Luxon 날짜/시간 처리
n8n은 Luxon 라이브러리를 내장하여 날짜/시간 처리를 지원합니다.
// 현재 시각
{{ $now.toISO() }} // "2026-02-18T03:00:00.000+09:00"
{{ $now.toFormat('yyyy-MM-dd HH:mm') }} // "2026-02-18 03:00"
{{ $now.toFormat('yyyy년 MM월 dd일') }} // "2026년 02월 18일"
// 날짜 계산
{{ $now.plus({ days: 7 }).toISODate() }} // 7일 후 날짜
{{ $now.minus({ hours: 1 }).toISO() }} // 1시간 전
{{ $now.startOf('month').toISODate() }} // 이번 달 1일
{{ $now.endOf('month').toISODate() }} // 이번 달 말일
// Unix 타임스탬프 변환
{{ $now.toSeconds() }} // Unix 초
{{ $now.toMillis() }} // Unix 밀리초
{{ DateTime.fromSeconds($json.timestamp).toISO() }} // 역변환
// 요일 확인
{{ $now.weekday }} // 1(월)~7(일)
{{ $now.weekdayLong }} // "Monday" 등
// 기간 계산
{{ $now.diff(DateTime.fromISO($json.createdAt), 'days').days.toFixed(0) }} 일 경과
문자열·배열 조작
// 문자열
{{ $json.name.toUpperCase() }}
{{ $json.email.split('@')[0] }} // 이메일 앞부분
{{ $json.text.substring(0, 100) + '...' }} // 100자 자르기
{{ $json.html.replace(/<[^>]*>/g, '') }} // HTML 태그 제거
{{ $json.url.includes('https') ? '보안' : '비보안' }}
// 배열
{{ $json.tags.join(', ') }}
{{ $json.items.length }}
{{ $json.scores.reduce((a, b) => a + b, 0) / $json.scores.length }} // 평균
{{ $json.items.filter(i => i.active).map(i => i.name).join(', ') }}
// JSON
{{ JSON.stringify($json.metadata) }}
{{ JSON.parse($json.configStr).timeout }}
고급 표현식 패턴
// 다른 노드 아이템 참조
{{ $items("HTTP Request")[0].json.data }}
{{ $items("PostgreSQL").length }}
// Nullish coalescing (값 없으면 기본값)
{{ $json.description ?? '설명 없음' }}
{{ $json.user?.email ?? 'unknown@example.com' }}
// 복잡한 조건
{{ ['admin', 'manager'].includes($json.role) ? '관리자' : '일반 사용자' }}
// Loop 노드 내 반복 정보
{{ $node["SplitInBatches"].context.currentRunIndex }} // 현재 배치 번호
{{ $node["SplitInBatches"].context.noItemsLeft }} // 마지막 배치 여부
// 실행 메타데이터
{{ $execution.id }} // 실행 고유 ID
{{ $workflow.name }} // 워크플로우 이름
{{ $execution.mode }} // "trigger" | "manual" | "retry"
트리거 노드 완전 가이드
Webhook Trigger
외부 시스템이 HTTP 요청을 보내면 워크플로우를 시작합니다. n8n에서 가장 많이 사용되는 트리거입니다.
# Webhook 노드 설정 상세
## 기본 설정
HTTP Method: GET / POST / PUT / PATCH / DELETE / HEAD (복수 선택 가능)
Path: my-webhook # https://n8n.example.com/webhook/my-webhook
Response Mode:
- Immediately # 즉시 200 OK 반환 (비동기)
- When Last Node Finishes # 워크플로우 완료 후 응답 (동기)
- Using 'Respond to Webhook' Node # 원하는 노드에서 응답
## 인증 옵션
Authentication: None / Basic Auth / Header Auth / JWT Auth
## 요청 데이터 접근
{{ $json.body }} # POST 본문
{{ $json.query }} # 쿼리 파라미터
{{ $json.headers }} # HTTP 헤더
{{ $json.params }} # URL 경로 파라미터
## 테스트 URL (비활성 상태) vs 운영 URL (활성 상태)
테스트: https://n8n.example.com/webhook-test/my-webhook
운영: https://n8n.example.com/webhook/my-webhook
# Webhook 수신 데이터 구조 예시 (POST JSON)
{
"body": { "user": "홍길동", "action": "signup" },
"headers": {
"content-type": "application/json",
"x-signature": "sha256=abc123..."
},
"query": { "source": "email" },
"params": {}
}
Schedule Trigger (Cron)
# 인터벌 방식
Trigger Interval: Minutes / Hours / Days / Weeks / Months
예) Every 30 minutes → 30분마다 실행
# 커스텀 Cron 표현식
Cron Expression: 0 9 * * 1-5
┬ ┬ ┬ ┬ ┬
│ │ │ │ └─ 요일 (0=일, 1=월 ... 5=금, 6=토)
│ │ │ └─── 월 (1-12)
│ │ └───── 일 (1-31)
│ └─────── 시 (0-23)
└───────── 분 (0-59)
# 실용 예시
"0 9 * * 1-5" → 평일 오전 9시
"0 0 1 * *" → 매월 1일 자정
"0 */2 * * *" → 2시간마다
"30 8 * * 1" → 매주 월요일 8:30
"0 9,12,18 * * *" → 하루 3번 (9시, 12시, 18시)
기타 트리거 노드
| 트리거 | 용도 | 특이사항 |
|---|---|---|
| Email Trigger (IMAP) | 새 이메일 수신 시 실행 | 폴링 방식, 30~60초 간격 |
| Chat Trigger | n8n 내장 채팅 UI 메시지 | 웹 임베드 가능 |
| Execute Workflow Trigger | 다른 워크플로우에서 호출 | 서브워크플로우 필수 |
| Error Trigger | 워크플로우 오류 발생 시 | 에러 알림 워크플로우용 |
| GitHub Trigger | PR, Push, Issue 등 이벤트 | GitHub Webhook 자동 등록 |
| Slack Trigger | Slack 메시지/이벤트 | Bot Token + Event API |
| Form Trigger | n8n 내장 폼 제출 | 별도 폼 UI 자동 생성 |
| SSE Trigger | Server-Sent Events 수신 | 실시간 스트림 데이터 |
| RabbitMQ Trigger | 메시지 큐 컨슈머 | AMQP 프로토콜 |
| Kafka Trigger | Kafka 토픽 컨슈머 | 대용량 이벤트 처리 |
핵심 노드 완전 레퍼런스
HTTP Request
# 주요 설정
Method: GET / POST / PUT / PATCH / DELETE / HEAD / OPTIONS
URL: https://api.example.com/{{ $json.resourceId }}
# Authentication
- None
- Header Auth: Authorization: Bearer {{ $env.API_TOKEN }}
- Basic Auth: username + password
- OAuth2: 자격증명으로 자동 토큰 갱신
- API Key: 쿼리 또는 헤더에 키 자동 삽입
# Body 전송 방식
Content Type: JSON / Form-Data / Form-URL-Encoded / Binary / RAW
JSON Body:
{
"name": "{{ $json.name }}",
"tags": {{ JSON.stringify($json.tags) }}
}
# 응답 처리
Response Format: JSON / Text / File / Auto-detect
Full Response: true → { status, headers, body } 포함
# 실패 처리
Continue on Fail: true # 에러여도 계속 진행
Retry On Fail: true # 자동 재시도
Max Tries: 3
Wait Between Tries (ms): 2000
# 타임아웃
Timeout: 10000 # 10초 (ms)
Code 노드
// 실행 모드 선택
// "Run Once for All Items" — 전체 아이템 배열을 한 번에 처리 (기본)
const items = $input.all();
const results = items.map(item => ({
json: {
...item.json,
upperName: item.json.name?.toUpperCase(),
processedAt: $now.toISO(),
}
}));
return results;
// "Run Once for Each Item" — 아이템마다 독립 실행
const { name, score } = $json;
return { json: { name, grade: score >= 90 ? 'A' : score >= 80 ? 'B' : 'C' } };
// Python 모드 (별도 설정)
# Python 표현식으로 데이터 처리
import json, datetime
items = _input.all()
result = []
for item in items:
data = item['json']
result.append({
'json': {
'name': data['name'].upper(),
'processed': datetime.datetime.now().isoformat()
}
})
return result
// 자주 쓰는 Code 노드 패턴
// 1. 외부 npm 모듈 사용 (NODE_FUNCTION_ALLOW_EXTERNAL 필요)
const _ = require('lodash');
const items = $input.all().map(i => i.json);
const grouped = _.groupBy(items, 'category');
return Object.entries(grouped).map(([key, val]) => ({
json: { category: key, count: val.length, items: val }
}));
// 2. CSV 파싱
const csv = $json.csvText;
const lines = csv.trim().split('\n');
const headers = lines[0].split(',');
return lines.slice(1).map(line => {
const vals = line.split(',');
return { json: Object.fromEntries(headers.map((h, i) => [h.trim(), vals[i]?.trim()])) };
});
// 3. 중복 제거
const seen = new Set();
return $input.all().filter(item => {
const key = item.json.email;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
Flow Control 노드 상세
## IF 노드
조건 결합: AND / OR
연산자: Equal, Not Equal, Contains, Starts With, Ends With,
Larger, Smaller, Regex Match, Is Empty, Is Not Empty
## Switch 노드
# 여러 조건 분기 (IF의 다중 버전)
Mode: Rules → 각 Rule에 조건 설정, 해당 출력으로 분기
Mode: Expression → {{ $json.type }} 값으로 자동 분기
Fallback: 매치 없는 경우 출력
## SplitInBatches 노드
# 대량 아이템을 N개씩 묶어 순차 처리
Batch Size: 10 # 한 번에 10개씩
Options:
Reset: false # 루프 초기화 여부
# Loop 상태 확인 (다음 노드에서)
{{ $node["SplitInBatches"].context.noItemsLeft }} # 마지막 배치?
{{ $node["SplitInBatches"].context.currentRunIndex }} # 현재 배치 번호
## Wait 노드
# 워크플로우 실행을 지정 시간 동안 일시 정지
Resume: After Time Interval
Amount: 5
Unit: Seconds / Minutes / Hours / Days
Resume: At Specified Time → {{ $json.retryAt }}
Resume: On Webhook Call → 외부 시스템이 Webhook 호출 시 재개
## Merge 노드
Mode: Append → 모든 아이템 단순 합치기
Mode: Merge by Field → 공통 필드 기준 병합 (SQL LEFT/INNER JOIN)
Mode: Multiplex → 카르테시안 곱 (모든 조합)
Mode: Choose Branch → IF True/False 분기 재결합
Mode: Wait for All Inputs → 두 입력 모두 도달 시 진행
## Aggregate 노드
# 여러 아이템을 하나로 집계
Aggregate: All Item Data (Into a List) → [{...}, {...}] → {list: [{...}, {...}]}
Field: items → 특정 필드만 배열로 수집
데이터 변환 노드
## Filter 노드
# 조건에 맞는 아이템만 통과
Condition: {{ $json.score }} Larger than 60
→ 60점 초과 아이템만 다음 노드로
## Sort 노드
Type: Simple
Sort Key: {{ $json.price }}
Order: Ascending / Descending
Type: Random → 무작위 섞기
## Limit 노드
Max Items: 10 → 앞에서 10개만 통과
## Remove Duplicates 노드
Compare: All Fields → 전체 필드가 같으면 중복
Compare: Selected Fields → 특정 필드 기준 중복 제거
Field: email
## Edit Fields (Set) 노드
Mode: Manual Mapping
fullName: {{ $json.firstName + ' ' + $json.lastName }}
timestamp: {{ $now.toISO() }}
Mode: JSON → 전체 json 객체 대체
Include Other Fields: true / false
## Rename Keys 노드
# 필드명 일괄 변경
user_name → userName
created_at → createdAt
유틸리티 노드
## Date & Time 노드
Action: Format a Date
Value: {{ $json.timestamp }}
Format: yyyy-MM-dd HH:mm:ss
Timezone: Asia/Seoul
Action: Add to a Date → {{ $json.date }}.plus({ days: 30 })
Action: Get Current Date → $now
## Crypto 노드
Action: Hash → MD5 / SHA256 / SHA512
Type: SHA256
Value: {{ $json.password + $json.salt }}
Encoding: hex
Action: HMAC → 서명 생성
Action: Generate → UUID, 랜덤 문자열
## HTML Extract 노드
Source Data: {{ $json.html }}
Extraction Values:
- Key: title
CSS Selector: h1
Return Value: Text
- Key: links
CSS Selector: a[href]
Return Value: Attribute (href)
Return Array: true
## RSS Read 노드
URL: https://feeds.example.com/rss.xml
Limit: 10 → 최신 10개 항목만
실전 워크플로우 예제
예제 1: 날씨 → Slack (기초)
# 매일 9시 서울 날씨 → Slack 전송
## Step 1: Schedule Trigger
Cron: "0 9 * * 1-5" # 평일 오전 9시
Timezone: Asia/Seoul
## Step 2: HTTP Request (날씨 API)
URL: https://api.open-meteo.com/v1/forecast
Query Parameters:
latitude: 37.5665
longitude: 126.9780
current_weather: true
timezone: Asia/Seoul
## Step 3: Code (메시지 포맷)
// Code 노드
const w = $input.first().json.current_weather;
const codeMap = {
0: '맑음 ☀️', 1: '대체로 맑음 🌤️', 2: '흐림 ⛅', 3: '흐림 ☁️',
45: '안개 🌫️', 61: '비 🌧️', 71: '눈 ❄️', 80: '소나기 🌦️',
95: '뇌우 ⛈️',
};
const condition = codeMap[w.weathercode] ?? '알 수 없음';
return [{ json: { message:
`*📅 ${$now.toFormat('yyyy년 MM월 dd일 (cccc)')} 서울 날씨*\n` +
`날씨: ${condition}\n기온: ${w.temperature}°C\n풍속: ${w.windspeed} km/h`
}}];
## Step 4: Slack
Channel: #daily-weather
Text: {{ $json.message }}
Parse Mode: mrkdwn
예제 2: Webhook API 서버 (중급)
# n8n을 간단한 REST API 서버로 활용
## POST /webhook/users/create → DB 저장 → 이메일 → 응답
Webhook Trigger
Method: POST
Path: users/create
Response Mode: Using 'Respond to Webhook' Node
↓
Code 노드 (입력 검증)
const { name, email, role } = $json.body;
if (!name || !email) throw new Error('name과 email은 필수입니다');
if (!email.includes('@')) throw new Error('유효하지 않은 이메일');
const allowedRoles = ['user', 'admin', 'viewer'];
if (role && !allowedRoles.includes(role)) throw new Error('허용되지 않은 role');
return [{ json: { name, email, role: role ?? 'user' } }];
↓
PostgreSQL 노드
Operation: Execute Query
Query: |
INSERT INTO users (name, email, role, created_at)
VALUES ('{{ $json.name }}', '{{ $json.email }}', '{{ $json.role }}', NOW())
RETURNING id, name, email, role, created_at
↓
Gmail 노드 (환영 이메일)
To: {{ $json.email }}
Subject: 가입을 환영합니다, {{ $json.name }}님!
Body: ...
↓
Respond to Webhook 노드
Status Code: 201
Response Body:
{
"success": true,
"user": {
"id": {{ $json.id }},
"name": "{{ $json.name }}",
"email": "{{ $json.email }}"
}
}
UI 가이드
캔버스 조작
| 단축키 | 동작 |
|---|---|
| Ctrl/Cmd + Enter | 워크플로우 실행 |
| Ctrl/Cmd + S | 저장 |
| Ctrl/Cmd + Z / Y | 실행 취소 / 다시 실행 |
| Ctrl/Cmd + C / V | 노드 복사 / 붙여넣기 |
| Ctrl/Cmd + D | 노드 복제 |
| Delete / Backspace | 선택 노드 삭제 |
| Ctrl/Cmd + A | 모든 노드 선택 |
| Ctrl/Cmd + Shift + F | 전체 워크플로우 화면 맞춤 |
| Tab | 다음 노드로 포커스 |
| 더블클릭 (캔버스) | 노드 추가 메뉴 |
| Space + 드래그 | 캔버스 이동 |
| 마우스 스크롤 | 줌 인/아웃 |
| Ctrl/Cmd + 0 | 100% 줌 리셋 |
디버깅 전략
# 1. 노드 클릭 → Input/Output 탭으로 데이터 확인
# 2. Execute Node → 해당 노드만 단독 실행
# 3. "Pinned Data" — 실행 결과를 고정하여 다운스트림 테스트
# 노드 우클릭 → Pin Data → 이후 실행 시 이 데이터 사용
# 4. Code 노드에서 console.log → "Execution Log" 탭에서 확인
console.log('디버그:', JSON.stringify($json, null, 2));
console.log('아이템 수:', $input.all().length);
# 5. 특정 노드부터 실행 — 노드 우클릭 → "Execute From Here"
# 6. 이전 실행 데이터로 테스트 — Executions → 특정 실행 → "Load"
워크플로우 설정
# 워크플로우 Settings 메뉴
Error Workflow: "에러 알림 워크플로우" 선택
→ 실패 시 해당 워크플로우 자동 실행
Timezone: Asia/Seoul
→ Schedule Trigger, Date 연산의 기준 시각
Caller Policy: Any Workflow
→ 서브워크플로우로 호출 허용 범위
Save Execution Progress: true
→ 각 노드 실행 후 중간 상태 저장 (장애 복구 가능)
Execution Order: v1 (latest)
→ 새 실행 순서 정책 (기본값 유지 권장)
워크플로우 패턴
패턴 1: 동기 Webhook API
Webhook Trigger (Response Mode: Using Respond Node)
↓
IF (입력 검증)
True → 처리 로직 → Respond to Webhook (200)
False → Respond to Webhook (400, 에러 메시지)
패턴 2: Fan-Out (병렬 처리)
# 하나의 이벤트 → 여러 작업 동시 실행
Webhook Trigger
├─→ Slack 알림 (동시 실행)
├─→ DB 저장
├─→ Email 발송
└─→ 외부 API 호출
↓ (Merge: Append)
Code (결과 집계)
패턴 3: Fan-In (결과 집계)
# 여러 소스에서 데이터 수집 → 통합
Schedule Trigger
├─→ PostgreSQL (매출 데이터)
├─→ HTTP (GA4 API)
└─→ HTTP (Slack 통계)
↓ (Merge: Append)
Code (전체 집계)
↓
Gmail (주간 리포트)
패턴 4: 배치 처리 (대용량)
Schedule Trigger
↓
PostgreSQL (미처리 레코드 SELECT)
↓
SplitInBatches (50개씩)
↓ (각 배치)
HTTP Request (외부 API)
↓
IF (성공/실패)
True → PostgreSQL UPDATE (processed = true)
False → PostgreSQL UPDATE (retry_count + 1)
↓ (루프 반복)
IF (noItemsLeft)
→ Slack (완료 알림)
패턴 5: 단계별 파이프라인 (Saga)
# 각 단계가 실패하면 보상(롤백) 트랜잭션 실행
Webhook Trigger (주문 생성)
↓
Code (재고 확인)
↓ (재고 있음)
HTTP Request (결제 처리)
↓ (성공)
PostgreSQL (주문 생성)
↓ (성공)
HTTP Request (배송 요청)
↓ (실패 시 → Error Workflow)
→ PostgreSQL (주문 취소)
→ HTTP Request (결제 환불)
→ Slack (관리자 알림)
↓ (성공)
Gmail (주문 확인 이메일)
패턴 6: 멱등성 보장 (중복 실행 방지)
// Code 노드 — 멱등성 키 기반 중복 처리 방지
const idempotencyKey = $json.eventId || $json.orderId;
// 이미 처리한 이벤트인지 확인 (Redis 또는 DB)
// 다음 노드: Redis GET → IF (존재하면 중단)
return [{ json: { ...($json), idempotencyKey } }];
Webhook Trigger
↓
Code (idempotencyKey 추출)
↓
Redis (GET idempotencyKey)
↓
IF (이미 처리됨)
True → Respond (409 Conflict)
False → 처리 로직
↓
Redis (SET idempotencyKey "done" EX 86400)
↓
Respond (200 OK)
다음 단계
- n8n 기초 (현재 페이지) — 설치·개념·표현식·노드·패턴
- AI 워크플로우 구축 — Claude/OpenAI 연동, AI Agent, RAG
- 고급 활용 — 커스텀 노드, 운영 배포, 보안, 성능
- 공식 문서: docs.n8n.io
- 워크플로우 템플릿: n8n.io/workflows (900개+)
- 커뮤니티 포럼: community.n8n.io
- GitHub: github.com/n8n-io/n8n