n8n 워크플로우가 느릴 때 바로 병렬 실행을 늘리면 해결되는 것은 아닙니다. 외부 API의 제한, 데이터베이스 연결 수, 작업 순서, 재시도 정책이 함께 바뀌기 때문입니다. 이 글은 특정 배수의 성능 향상을 약속하지 않고, 동시성·배치·rate limit·오류 처리를 분리해 설계하고 동일한 조건으로 전후를 측정하는 방법을 정리합니다.
핵심은 다음 세 가지입니다.
- 한 번에 처리할 작업 수와 한 요청에 묶을 항목 수는 서로 다른 설정입니다.
- 외부 API를 호출한다면 속도보다 먼저 rate limit과 재시도·실패 처리를 정해야 합니다.
- 변경 효과는 TPS 하나가 아니라 처리 시간, 오류율, 재시도 수, P95/P99 지연을 같은 조건에서 비교해야 합니다.
이 원고에는 n8n 운영 인스턴스나 외부 API를 연결한 실측 결과가 없습니다. 따라서 수치 결과는 측정 필요로 남기고, 공식 문서에 나온 기능과 로컬에서 실행할 수 있는 측정 기록 형식만 제시합니다.
이 글의 범위는 동시성·배치 변경의 측정 설계와 기록표입니다. 오류 복구·재시도 예산·대체 경로 계약은 오류 처리와 대체 경로 설계에서, LLM 호출 비용·쿼터 회계는 비용 한도와 실제 사용량 정산에서 별도로 다룹니다.
1. 먼저 병목 유형을 나누기
다음 질문에 답하면 설정을 잘못 바꾸는 일을 줄일 수 있습니다.
| 관찰 | 먼저 확인할 것 | 가능한 대응 |
|---|---|---|
| 요청이 순서대로 오래 대기 | 작업을 직렬로 처리하는지 | 작은 배치, 독립 단계 분리 |
| 외부 API가 429를 반환 | 서비스별 rate limit과 Retry-After | 배치·대기·재시도 조정 |
| 실행이 자주 실패 | 마지막 실행 노드와 오류 유형 | Error Workflow, 오류 분기 |
| 데이터베이스가 병목 | 연결 풀·쿼리·트랜잭션 | 연결 수와 쿼리 계획 점검 |
| 작업 수가 늘면 전체가 불안정 | n8n 실행 동시성·worker 자원 | 제한을 낮춘 기준선부터 측정 |
비동기라는 단어만으로 응답 시간이 줄어드는 것은 아닙니다. 병렬 작업이 공유 자원에서 대기하거나 재시도가 겹치면 오히려 전체 지연과 실패가 늘 수 있습니다.
2. n8n의 동시성·배치 기능을 구분하기
실행 동시성
먼저 배포 환경을 고정합니다. 아래는 n8n 공식 문서의 범위를 요약한 것이며, Cloud 계정에 self-hosted 설정을 그대로 적용한다는 뜻이 아닙니다.
- self-hosted regular mode: 기본적으로 production 실행 수를 제한하지 않으며,
N8N_CONCURRENCY_PRODUCTION_LIMIT으로 제한을 둘 수 있습니다. - self-hosted queue mode: worker가 동시에 처리할 작업 수를
--concurrency로 제어하며,N8N_CONCURRENCY_PRODUCTION_LIMIT과의 우선순위·적용 범위를 운영 버전의 문서에서 확인해야 합니다. - n8n Cloud: 이 글의 self-hosted 환경변수·worker 옵션을 적용하지 않고, 계정별 Cloud 동시성 문서를 확인합니다.
Queue mode를 선택한다면 Redis와 데이터베이스 연결, 그리고 main·worker·webhook processor에 동일한 N8N_ENCRYPTION_KEY를 설정하는 조건을 확인합니다. n8n 공식 문서는 queue 실행에 SQLite를 사용하는 구성을 권장하지 않습니다. 이 글에서는 해당 인프라를 실제로 구성하거나 측정하지 않습니다(동시성 제어 공식 문서, Queue mode 공식 문서).
동시성을 올리기 전에 다음 값을 기록합니다.
- n8n 배포 방식과 queue mode 사용 여부
- worker 수와 각 worker의 동시성 제한
- 워크플로우 1회가 소비하는 외부 API 요청 수
- 데이터베이스 연결 풀과 타임아웃
- 기준선의 처리 시간·오류율·재시도 수
n8n의 실제 설정 이름과 지원 범위는 배포 방식과 버전에 따라 확인해야 합니다. 화면이나 환경변수를 추측해서 쓰지 말고, 운영 중인 버전의 문서를 확인합니다.
Loop Over Items와 배치
Loop Over Items (Split in Batches)는 입력 항목을 묶어 처리할 때 사용할 수 있는 노드입니다(Loop Over Items 공식 문서). 배치 크기만 바꾼다고 외부 요청 수가 자동으로 줄어드는 것은 아닙니다. downstream 노드와 대상 API가 여러 항목을 하나의 payload로 받는 계약일 때만 요청 수 변화가 생길 수 있습니다. 한 번의 실패가 영향을 주는 범위와 메모리 사용량도 함께 기록합니다.
배치 크기를 정할 때 기록할 항목:
- 입력 항목 수
- 배치 크기
- 배치 사이 대기 시간
- 요청당 payload 크기
- 실패한 배치의 재처리 방법
- API 응답의 페이지네이션·중복 처리 규칙
배치와 동시성은 함께 올리지 않습니다. 먼저 배치 크기 하나를 바꾸고, 같은 입력 fixture로 실행 결과를 비교합니다.
Rate limit과 재시도
n8n 공식 안내는 Retry On Fail 또는 Loop Over Items와 Wait 조합으로 요청 사이에 대기를 둘 수 있다고 설명합니다(rate limit 공식 안내). HTTP Request 노드의 Max Tries와 Wait Between Tries (ms)도 공식 문서에 명시되어 있습니다(HTTP Request 공통 문제).
Retry-After 해석, 지수형 대기, jitter, 상태 코드별 재시도 여부는 n8n이 모든 API에 자동 보장하는 기능이 아닙니다. 제공자 문서와 HTTP 명세를 먼저 확인하고 애플리케이션에서 별도로 구현·측정할 일반 운영 권고입니다(HTTP Retry-After 명세).
재시도는 무한한 성공 장치가 아닙니다.
- 400 계열 입력 오류는 먼저 입력을 수정합니다.
- 401·403은 인증·권한을 확인하고 반복 재시도를 막습니다.
- 429는 서비스 문서의 제한과 대기 지시를 따릅니다.
- 500·502·503·504는 제한된 횟수와 지수형 대기를 검토합니다.
- 같은 요청을 다시 보내도 안전한지 idempotency를 확인합니다.
실패한 항목을 성공한 것처럼 표시하지 않도록 status, attempt, error_type, source_id 같은 필드를 남깁니다.
3. 안전한 변경 순서
1단계: 기준선 고정
운영 데이터 대신 합성 fixture 또는 재처리 가능한 비민감 데이터로 먼저 측정합니다.
- 입력 항목 수를 고정합니다.
- 같은 API 응답 fixture를 사용하거나 테스트 엔드포인트를 사용합니다.
- worker 수와 동시성 설정을 기록합니다.
- 캐시, 네트워크 지역, 실행 시간대처럼 결과에 영향을 주는 조건을 기록합니다.
- warm-up 실행을 별도로 기록하고, 동일 조건에서 최소 5회 이상 반복해 중앙값과 꼬리 지연을 봅니다. 반복 횟수·timestamp·이상치 처리 규칙을 함께 기록합니다.
2단계: 한 변수만 변경
다음 중 하나만 바꿉니다.
- 배치 크기
- 배치 사이 대기 시간
- 워크플로우 동시성
- worker 수
- retry 횟수·대기 방식
여러 값을 동시에 바꾸면 개선 원인을 설명할 수 없습니다.
3단계: 실패 예산 확인
속도가 빨라져도 오류율이나 재시도 수가 증가하면 변경을 채택하지 않습니다. 특히 외부 서비스의 제한을 초과해 다른 사용자의 요청까지 지연시키지 않는지 확인합니다.
4단계: 되돌림 조건 설정
다음 중 하나라도 발생하면 이전 설정으로 되돌립니다.
- 오류율이 기준선보다 높아짐
- 중복 처리 또는 누락이 발견됨
- P95/P99 지연이 허용 범위를 넘음
- 외부 API의 429 비율이 증가함
- 데이터베이스 연결 고갈·메모리 압박이 발생함
4. 측정표를 먼저 만들기
| 항목 | 기록 방법 |
|---|---|
| 측정일시 | 시간대와 timezone 포함 |
| 코드/워크플로우 버전 | export 해시 또는 변경 식별자 |
| 입력 수 | 고정된 fixture의 항목 수 |
| 동시성·배치 | 실제 설정값 |
| 총 처리 시간 | 시작·종료 timestamp 차이 |
| 처리량 | 성공 항목 수 / 경과 시간 |
| 오류율 | 실패 항목 수 / 전체 항목 수 |
| 재시도 수 | attempt 합계와 분포 |
| 지연 | P50·P95·P99 또는 측정 가능한 대체값 |
| 자원 | CPU·메모리·DB 연결 관찰값 |
| 한계 | fixture·네트워크·외부 API 차이 |
특정 배수나 백분율 같은 성과 표현은 위의 기준선, 입력, 환경, 계산식이 공개될 때만 결과로 쓸 수 있습니다. 그런 자료가 없으면 제목과 본문 모두 측정 방법으로 표현하고 결과는 측정 필요로 남깁니다.
5. 로컬에서 측정 기록 구조 검증하기
아래 코드는 외부 서비스에 연결하지 않고, 합성 실행 기록의 처리량·오류율을 계산합니다. 실제 n8n 성능 결과를 만들어 내는 코드가 아니며, 기록 형식과 계산식을 점검하는 예제입니다.
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class Run:
input_count: int
success_count: int
elapsed_seconds: float
retry_count: int
def summarize(run: Run) -> dict[str, float]:
if run.input_count <= 0:
raise ValueError("input_count must be positive")
if run.elapsed_seconds <= 0:
raise ValueError("elapsed_seconds must be positive")
if not 0 <= run.success_count <= run.input_count:
raise ValueError("success_count must be within input_count")
if run.retry_count < 0:
raise ValueError("retry_count must not be negative")
return {
"successful_items_per_second": run.success_count / run.elapsed_seconds,
"error_rate": (run.input_count - run.success_count) / run.input_count,
"retry_count": float(run.retry_count),
}
if __name__ == "__main__":
result = summarize(Run(input_count=100, success_count=98, elapsed_seconds=20, retry_count=3))
assert round(result["successful_items_per_second"], 2) == 4.90
assert round(result["error_rate"], 2) == 0.02
assert result["retry_count"] == 3.0
print(result)
이 검증은 success_count / elapsed_seconds 산술과 입력 경계만 확인하며, 성공하지 않은 항목을 모두 실패로 간주하는 단순한 합성 예제입니다. successful_items_per_second는 n8n의 일반 TPS나 외부 API 성능을 의미하지 않습니다. P95/P99를 계산하거나 n8n 실행 데이터를 수집하려면 실행별 timestamp와 동일한 측정 조건을 추가해야 합니다.
이 예제의 합성 출력은 successful_items_per_second=4.9, error_rate=0.02, retry_count=3.0입니다. 이 값은 코드에 넣은 fixture의 산술 결과이며 운영 성능 결과가 아닙니다.
6. 오류 처리와 관찰성
n8n 공식 문서에 따르면 Error Workflow는 Workflow Settings에서 지정하고, 별도 workflow의 첫 노드는 Error Trigger여야 합니다(오류 처리 공식 문서). 이 글에서는 오류 복구 구현 자체가 아니라, 성능 측정에서 오류를 어떻게 기록할지만 다룹니다.
Error Trigger가 실행 컨텍스트에 제공할 수 있는 값과 저장 설정에 따라 달라지는 값은 n8n 버전·실행 유형별로 확인합니다. 다음 중 workflow·실패 노드·실행 식별자·원인 요약은 받을 수 있는 값으로 확인하고, retry_count, source_id, 중복 방지 키와 재처리 기준은 애플리케이션이 별도로 기록할 권장 필드로 구분합니다. API 키·개인정보·전체 payload는 보내지 않습니다.
성공과 실패를 같은 결과 테이블에 남기고, 재처리 시 원래 source_id를 기준으로 중복을 막습니다. 오류 복구·재시도 예산의 상세 설계는 entry/27을 사용하고, 이 글에서는 해당 상태의 처리 시간·재시도 수·오류율을 측정합니다.
7. 체크리스트
- 동시성, 배치, 재시도, worker 수를 서로 다른 항목으로 기록했는가?
- API별 rate limit과
Retry-After의미를 확인했는가? - 합성 fixture로 기준선을 고정했는가?
- 한 번에 하나의 변수만 바꿨는가?
- 처리량뿐 아니라 오류율·재시도·꼬리 지연을 확인했는가?
- 재시도해도 안전한 작업인지 확인했는가?
- 개인정보·API 키가 로그와 알림에 포함되지 않는가?
- 되돌림 조건과 재처리 기준이 있는가?
- 결과가 없으면
측정 필요로 표시했는가?
공식 자료
- n8n Control concurrency: https://docs.n8n.io/deploy/host-n8n/configure-n8n/scaling/control-concurrency (확인일: 2026-08-12)
- n8n Queue mode: https://docs.n8n.io/deploy/host-n8n/configure-n8n/scaling/enable-queue-mode (확인일: 2026-08-12)
- n8n Loop Over Items: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.splitinbatches (확인일: 2026-08-12)
- n8n Handle rate limits: https://docs.n8n.io/integrations/builtin/handle-rate-limits (확인일: 2026-08-12)
- n8n Error handling: https://docs.n8n.io/build/flow-logic/handle-errors-gracefully (확인일: 2026-08-12)
- Google 사용자 중심 콘텐츠: https://developers.google.com/search/docs/fundamentals/creating-helpful-content?hl=ko (확인일: 2026-08-12)
'🤖 1인 에이전트 구축기' 카테고리의 다른 글
| n8n Switch 노드, 복잡한 조건문 깔끔하게 정리하기 (0) | 2026.06.12 |
|---|---|
| UUID 생성과 DB 중복 방지 설계에서 점검할 3가지 기준 (0) | 2026.06.12 |
| 대화 요약·압축 알고리즘 구현에서 점검할 3가지 기준 (0) | 2026.06.12 |
| LLM API 토큰 계산과 사용자별 비용 한도 설계 가이드 (0) | 2026.06.12 |
| Gemini·Claude 교차검증 가이드: LLM 합의보다 근거를 비교하는 방법 (0) | 2026.05.27 |