본문 바로가기
n8n

n8n 커스텀 노드 Authorization 실패 트러블슈팅 — Worker 분산 환경의 함정

by simplify-len 2026. 5. 5.

n8n을 큐 모드로 운영하면서, 단일 인스턴스 환경에서는 멀쩡하던 커스텀 노드가 운영에서만 401 에러를 뿜기 시작했다. 원인은 n8n 자체가 아니라 "프로세스 로컬 메모리 캐시"라는 무심한 설계 가정이었다. 이번 글에서는 그 디버깅 과정과 해결책(Redis 기반 분산 토큰 캐시)을 정리한다.


1. 증상

  • 자체 개발한 커스텀 노드(외부 API 연동)에서 드롭다운(예: 프로젝트 목록)이 처음에는 정상 로드됨
  • Execute Step을 수동으로 누른 이후부터 Error fetching options 발생
  • HTTP 401 Unauthorized + Authorization failed - please check your credentials
  • 로컬 단일 인스턴스 환경에서는 재현되지 않고, 운영(메인 1대 + Worker 3대 큐 모드)에서만 발생

처음에는 "토큰 만료 처리 버그"로 의심했지만, 단일 환경에서는 같은 코드가 멀쩡히 동작했다. 환경 차이가 답이라는 뜻이었다.


2. 원인 분석

운영 환경 구성:

  • n8n Queue Mode (Main 1대 + Worker 3대)
  • OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true

문제의 커스텀 credential은 외부 API 인증 토큰을 프로세스 로컬 메모리(Map)에 캐싱하는 단순한 구조였다. 이 가정이 큐 모드에서 다음과 같이 무너진다.

2.1 프로세스별로 캐시가 따로 논다

Main과 Worker 4개 프로세스가 각자 별도의 메모리 공간을 갖기 때문에, 한쪽에서 발급한 토큰이 다른 쪽으로 공유되지 않는다.

2.2 실행 주체가 분리되어 있다

작업 실행 위치
loadOptions (드롭다운 채우기) Main 프로세스
Execute Step (수동 실행) Worker 프로세스로 오프로드

2.3 토큰 중복 발급 → 서버 측 무효화

  1. Main이 드롭다운을 채우기 위해 토큰 A를 발급해서 캐싱
  2. 사용자가 Execute Step을 누르면 Worker가 실행 → Worker는 자기 캐시에 토큰이 없으므로 토큰 B를 새로 발급
  3. 외부 API 서버가 동일 계정에 대해 유효 토큰을 1개만 유지하는 정책이라, 토큰 B 발급과 동시에 토큰 A가 무효화됨
  4. 다음 드롭다운 호출 → Main은 (자기 입장에서는) 만료되지 않은 토큰 A를 그대로 재사용
  5. 서버는 이미 무효화된 토큰 A를 거절 → 401

요약: *"각자 캐시"* + *"동일 계정 단일 세션 정책"* 의 조합이 시한폭탄이었다.


3. 해결: Redis 기반 분산 토큰 캐시

토큰 캐시를 Redis(Cluster) 공유 스토리지로 옮겨, Main과 Worker가 같은 토큰을 보도록 변경했다.

핵심 구현

  • 공유 캐시 키: n8n:auth:{username}:token
    TTL은 외부 서버가 부여하는 토큰 수명보다 약간 짧게 설정해 만료 race를 방지
  • 분산 락 키: n8n:auth:{username}:lock
    SET NX EX 로 락을 획득해 동시 발급을 방지
  • 폴링 대기: 락 획득에 실패한 프로세스는 일정 간격으로 토큰 키를 폴링하며 다른 프로세스의 발급 결과를 기다림
  • 해시 태그 {username}: Redis Cluster에서 토큰 키와 락 키를 같은 슬롯에 묶기 위한 키 디자인. 슬롯이 갈리면 트랜잭션/원자성 보장이 깨진다
  • Redis 인프라 재활용: 이미 n8n이 사용하는 QUEUE_BULL_REDIS_CLUSTER_NODES 설정을 그대로 활용

인증 흐름

1. Redis에서 토큰 조회
   └─ 있으면 → 사용

2. 없으면 → 분산 락 획득 시도
   ├─ 획득 성공 → 토큰 발급 → Redis 저장 → 락 해제
   └─ 획득 실패 → 폴링으로 다른 프로세스가 저장한 토큰 대기
        ┌──────────┐  ┌──────────┐  ┌──────────┐
        │   Main   │  │ Worker A │  │ Worker B │
        └────┬─────┘  └────┬─────┘  └────┬─────┘
             │             │             │
             └─────────────┼─────────────┘
                           ▼
                  ┌─────────────────┐
                  │     Redis       │
                  │  token + lock   │
                  └────────┬────────┘
                           ▼
                    External API
                   (단일 세션 정책)

4. 교훈

이번 사고의 본질은 *"분산 환경에서 프로세스 로컬 상태를 신뢰했다"* 는 점이다. 정리하면:

  • n8n Queue Mode + OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true 환경에서는 프로세스 로컬 메모리를 전제로 한 캐싱/상태 관리가 작동하지 않는다.
  • 커스텀 노드/credential에서 상태를 유지해야 한다면 반드시 공유 스토리지(Redis 등) 를 사용해야 한다.
  • 특히 "동일 계정의 세션이 1개만 유효한" 외부 API를 호출할 때는 토큰 중복 발급이 곧장 장애로 이어진다. 단순 "캐시 안 됨" 수준의 문제가 아니라 인증 자체가 불가능한 상태가 된다.

n8n이 큐 모드로 전환되는 순간, 우리가 작성한 커스텀 코드도 *"하나의 프로세스가 아니라 여러 프로세스에서 동시에 돈다"* 는 전제로 다시 검토해야 한다는 교훈을 비싸게 배웠다.

댓글