금. 8월 15th, 2025

G: 안녕하세요, 자동화와 효율성을 사랑하는 여러분! 🚀

n8n은 코딩 없이도 복잡한 워크플로우를 구축할 수 있게 해주는 강력한 자동화 도구입니다. 드래그 앤 드롭 방식으로 노드를 연결하기만 하면 되죠. 하지만 때로는 기본 노드만으로는 해결하기 어려운, 아주 특수한 데이터 처리나 커스텀 로직이 필요할 때가 있습니다.

이런 순간, n8n의 “비장의 무기”가 등장합니다. 바로 Code Node입니다! 🧙‍♂️ Code Node는 자바스크립트(JavaScript) 코드를 직접 작성하여 n8n 워크플로우 내에서 상상할 수 있는 거의 모든 데이터 조작과 로직 구현을 가능하게 해줍니다.

이 글에서는 n8n Code Node가 무엇인지부터 시작하여, 언제 사용해야 하는지, 핵심 문법과 유용한 함수들, 그리고 실제 활용 가능한 풍부한 예제들을 통해 여러분이 Code Node의 진정한 잠재력을 깨울 수 있도록 도와드리겠습니다.

자, 이제 n8n 워크플로우에 코딩의 마법을 불어넣어 볼 준비가 되셨나요? ✨


📚 1. n8n Code Node란 무엇인가요?

n8n Code Node는 워크플로우 내에서 JavaScript 코드를 실행할 수 있는 특별한 노드입니다. 일종의 작은 프로그래밍 샌드박스라고 생각하시면 됩니다. 다른 노드들이 제공하지 못하는 섬세한 제어나 복잡한 계산, 조건부 로직 등을 이 노드를 통해 구현할 수 있습니다.

핵심 특징:

  • 유연성: JavaScript의 모든 기능을 활용하여 데이터를 변환, 필터링, 조합, 생성할 수 있습니다.
  • 확장성: n8n 워크플로우의 한계를 뛰어넘어, 거의 모든 커스텀 로직을 추가할 수 있습니다.
  • 데이터 접근: 이전 노드에서 넘어온 모든 데이터에 쉽게 접근하고 조작할 수 있습니다.
  • 출력 제어: 가공된 데이터를 원하는 형식으로 다음 노드에 전달할 수 있습니다.

마치 여러분의 n8n 워크플로우에 “맞춤 제작된 만능칼” 하나를 추가하는 것과 같습니다. 🔪


🎯 2. Code Node, 언제 사용해야 할까요?

Code Node는 n8n의 기본 노드로는 해결하기 어렵거나 비효율적인 상황에서 빛을 발합니다. 다음은 Code Node를 사용하면 좋은 대표적인 시나리오들입니다.

2.1. 복잡한 데이터 변환 및 가공 🔄

  • 시나리오: 여러 필드의 데이터를 조합하여 새로운 필드를 만들거나, 특정 규칙에 따라 문자열을 변환해야 할 때 (예: 이름 포맷 변경, 주소 파싱).
  • 예시: first_namelast_name 필드를 합쳐 full_name 필드를 생성하거나, 숫자를 특정 통화 형식으로 변환하는 경우.
    // 입력 데이터: { "firstName": "John", "lastName": "Doe" }
    // 출력 데이터: { "fullName": "John Doe", "email": "john.doe@example.com" }
    return items.map(item => {
      const firstName = item.json.firstName;
      const lastName = item.json.lastName;
      return {
        json: {
          fullName: `${firstName} ${lastName}`,
          email: `${firstName.toLowerCase()}.${lastName.toLowerCase()}@example.com`
        }
      };
    });

2.2. 커스텀 로직 및 조건부 처리 🚦

  • 시나리오: 특정 조건(예: 값의 범위, 특정 문자열 포함 여부)에 따라 데이터의 흐름을 변경하거나, 다른 값을 할당해야 할 때.
  • 예시: 주문 금액이 100달러 이상이면 ‘VIP’ 고객으로 분류하고 할인율을 적용하는 로직.
    // 입력 데이터: { "orderAmount": 120 }
    // 출력 데이터: { "customerType": "VIP", "discountRate": 0.1 }
    return items.map(item => {
      const orderAmount = item.json.orderAmount;
      if (orderAmount >= 100) {
        return { json: { customerType: 'VIP', discountRate: 0.1 } };
      } else {
        return { json: { customerType: 'Regular', discountRate: 0 } };
      }
    });

2.3. 외부 API 호출 (고급 시나리오) 🔗

  • 시나리오: n8n의 기본 HTTP Request 노드로 처리하기 어려운, 아주 복잡한 인증 방식이나 동적인 요청 본문/헤더 구성이 필요할 때. (⚠️ 일반적인 API 호출은 HTTP Request 노드가 훨씬 편리하고 권장됩니다!)
  • 예시: HMAC 서명과 같은 복잡한 인증 방식을 사용하여 특정 API를 호출해야 하는 경우. (이 경우 crypto 모듈 등을 활용할 수 있습니다.)

2.4. 데이터 집계 및 요약 📊

  • 시나리오: 여러 개의 입력 아이템(예: 주문 목록)에서 총합, 평균, 개수 등을 계산하여 하나의 요약 보고서를 만들 때.
  • 예시: 여러 판매 트랜잭션의 총 판매액과 평균 판매액을 계산하는 경우.

    // 입력 데이터: [{ "amount": 10 }, { "amount": 20 }, { "amount": 30 }]
    // 출력 데이터: { "totalAmount": 60, "averageAmount": 20 }
    const totalAmount = items.reduce((sum, item) => sum + item.json.amount, 0);
    const averageAmount = totalAmount / items.length;
    
    return [{ json: { totalAmount, averageAmount } }];

2.5. 특정 에러 처리 또는 로깅 🚨

  • 시나리오: 특정 조건에서만 에러를 발생시키거나, 디버깅을 위해 중간 데이터를 정교하게 로깅해야 할 때.
  • 예시: 특정 필드가 누락되었을 경우 워크플로우를 중단하고 에러 메시지를 생성하는 경우.

📖 3. Code Node 핵심 문법 및 활용법

Code Node는 JavaScript를 사용하지만, n8n 워크플로우 환경에 맞게 몇 가지 특별한 전역 변수와 함수를 제공합니다.

3.1. 기본 구조: 입력과 출력 📦

Code Node의 핵심은 items 배열을 입력받아, 새로운 items 배열을 반환하는 것입니다. 각 아이템은 보통 json 속성을 포함합니다.

// 기본 Code Node 구조

// 'items'는 이전 노드에서 전달받은 데이터의 배열입니다.
// 각 배열 요소는 { json: { ... }, binary: { ... } } 와 같은 형태입니다.

const outputItems = []; // 새로운 출력 아이템들을 담을 배열

for (const item of items) {
  // item.json: 현재 아이템의 JSON 데이터
  // item.binary: 현재 아이템의 바이너리 데이터 (파일 등)

  // 🔽 여기에 데이터 처리 로직 작성 🔽
  const originalData = item.json;
  const processedData = {
    // 예시: 원본 데이터에 새로운 필드 추가
    ...originalData,
    newField: '새로운 값'
  };
  // 🔼 여기에 데이터 처리 로직 작성 🔼

  outputItems.push({
    json: processedData // 처리된 JSON 데이터를 출력 아이템에 추가
    // binary: item.binary // 필요하다면 바이너리 데이터도 함께 전달
  });
}

return outputItems; // 처리된 아이템 배열을 반환

가장 일반적인 형태는 입력 itemsmap 함수로 처리하여 새로운 items 배열을 반환하는 것입니다.

return items.map(item => {
  // item.json에 접근하여 데이터 처리
  const originalValue = item.json.someField;
  const newValue = originalValue.toUpperCase();

  return {
    json: {
      ...item.json, // 원본 데이터를 유지하면서
      processedField: newValue // 새로운 필드 추가 또는 기존 필드 수정
    }
  };
});

3.2. 입력 데이터 접근 💡

  • items: 이전 노드에서 전달받은 모든 아이템의 배열입니다. Code Node의 메인 입력입니다.
  • item: (주로 items.map(item => ...) 내부에서) 현재 처리 중인 단일 아이템을 나타냅니다.
    • item.json: 현재 아이템의 JSON 데이터입니다. (가장 많이 사용)
    • item.binary: 현재 아이템의 바이너리 데이터입니다. (예: 파일)
  • $json: 현재 아이템의 JSON 데이터에 바로 접근하는 약식 문법입니다. (item.json과 동일)
  • $input.first().json: 여러 아이템 중 첫 번째 아이템의 JSON 데이터에 접근합니다.

3.3. 출력 데이터 구성 📝

Code Node는 반드시 아이템들의 배열을 반환해야 합니다. 각 아이템은 최소한 json 속성을 가져야 합니다.

  • 단일 아이템 출력:
    return [{ json: { message: 'Hello, World!' } }];
  • 여러 아이템 출력:
    return [
      { json: { id: 1, name: 'Alice' } },
      { json: { id: 2, name: 'Bob' } }
    ];
  • 입력 아이템을 변환하여 출력:
    return items.map(item => {
      return { json: { ...item.json, status: 'processed' } };
    });

3.4. 유용한 내장 함수 🛠️

n8n Code Node 환경에는 워크플로우와 상호작용할 수 있는 몇 가지 전역 함수가 있습니다.

  • getIndex(): 현재 처리 중인 아이템의 인덱스를 반환합니다. (0부터 시작)
    return items.map((item, index) => { // map의 두 번째 인자로 index를 받는 것이 더 일반적입니다.
      return { json: { ...item.json, originalIndex: index } };
    });
    // 또는 Code Node 내부에서
    // const index = getIndex();
  • getBinaryData(nodeName, itemIndex, binaryPropertyName): 특정 노드의 특정 아이템에서 바이너리 데이터를 가져옵니다.
  • getWorkflowStaticData() / getWorkflowData(): 워크플로우 수준의 고정 데이터 또는 실행 중인 데이터를 가져옵니다. (고급)
  • executeCommand(command, args): n8n 서버에서 쉘 명령어를 실행합니다. (🚨 경고: 보안상 매우 위험하므로 사용에 극도의 주의가 필요합니다!)
  • console.log(): 디버깅을 위해 Execution Log에 메시지를 출력할 수 있습니다.
    console.log('현재 아이템:', JSON.stringify(item.json));

🚀 4. Code Node 실전 예제

이제 몇 가지 실제 시나리오를 통해 Code Node의 활용법을 익혀봅시다.

4.1. 예제 1: JSON 데이터 필터링 및 변환 🧹

시나리오: 고객 주문 목록에서 ‘주문 완료’ 상태의 주문만 필터링하고, 각 주문의 총액을 계산하여 새로운 필드 totalPrice를 추가합니다.

입력 데이터 (JSON):

[
  { "orderId": "ORD001", "items": [{ "name": "A", "price": 10, "qty": 1 }], "status": "pending" },
  { "orderId": "ORD002", "items": [{ "name": "B", "price": 20, "qty": 2 }, { "name": "C", "price": 5, "qty": 1 }], "status": "completed" },
  { "orderId": "ORD003", "items": [{ "name": "D", "price": 15, "qty": 3 }], "status": "completed" }
]

Code Node 코드:

const completedOrders = items.filter(item => item.json.status === 'completed');

const processedOrders = completedOrders.map(item => {
  const originalOrder = item.json;
  let totalPrice = 0;

  if (originalOrder.items && Array.isArray(originalOrder.items)) {
    totalPrice = originalOrder.items.reduce((sum, product) => {
      return sum + (product.price * product.qty);
    }, 0);
  }

  return {
    json: {
      orderId: originalOrder.orderId,
      status: originalOrder.status,
      items: originalOrder.items, // 원본 아이템 목록 유지
      totalPrice: totalPrice, // 계산된 총액 추가
      currency: 'USD' // 추가 정보
    }
  };
});

return processedOrders;

설명:

  1. filter 함수를 사용하여 status가 ‘completed’인 주문만 걸러냅니다.
  2. 필터링된 주문들에 대해 map 함수를 사용하여 각 주문을 순회합니다.
  3. reduce 함수를 사용하여 각 주문의 items 배열을 순회하며 price * qty를 합산하여 totalPrice를 계산합니다.
  4. 새로운 json 객체를 구성하여 orderId, status, items, totalPrice, currency 필드를 포함하도록 반환합니다.

출력 데이터 (JSON):

[
  { "orderId": "ORD002", "items": [{ "name": "B", "price": 20, "qty": 2 }, { "name": "C", "price": 5, "qty": 1 }], "status": "completed", "totalPrice": 45, "currency": "USD" },
  { "orderId": "ORD003", "items": [{ "name": "D", "price": 15, "qty": 3 }], "status": "completed", "totalPrice": 45, "currency": "USD" }
]

4.2. 예제 2: 여러 아이템 합치기 (집계) 📈

시나리오: 월별 매출 데이터가 여러 아이템으로 들어왔을 때, 모든 월의 총 매출과 평균 매출을 계산하여 하나의 요약 아이템으로 만듭니다.

입력 데이터 (JSON):

[
  { "month": "Jan", "revenue": 10000 },
  { "month": "Feb", "revenue": 12000 },
  { "month": "Mar", "revenue": 9500 },
  { "month": "Apr", "revenue": 11000 }
]

Code Node 코드:

let totalRevenue = 0;
for (const item of items) {
  totalRevenue += item.json.revenue;
}

const averageRevenue = totalRevenue / items.length;

return [{
  json: {
    reportTitle: "월별 매출 요약 보고서",
    totalRevenue: totalRevenue,
    averageRevenue: averageRevenue,
    numberOfMonths: items.length,
    reportDate: new Date().toISOString().split('T')[0] // 오늘 날짜 추가
  }
}];

설명:

  1. totalRevenue 변수를 초기화합니다.
  2. items 배열을 순회하며 각 아이템의 revenue 값을 totalRevenue에 더합니다.
  3. totalRevenueitems.length로 나누어 averageRevenue를 계산합니다.
  4. 이 모든 요약 정보를 담은 하나의 새로운 JSON 아이템을 반환합니다.

출력 데이터 (JSON):

[
  { "reportTitle": "월별 매출 요약 보고서", "totalRevenue": 42500, "averageRevenue": 10625, "numberOfMonths": 4, "reportDate": "2023-10-27" }
]

(날짜는 실행 시점에 따라 달라집니다)

4.3. 예제 3: 동적 조건부 로직 구현 (출력 분기) 🚦

시나리오: 사용자 이메일 주소를 분석하여, 특정 도메인(예: @example.com)은 ‘내부 사용자’, 그 외에는 ‘외부 사용자’로 분류하고, 서로 다른 출력으로 보냅니다. (이후 노드에서 이 출력에 따라 다른 작업을 수행할 수 있도록)

입력 데이터 (JSON):

[
  { "name": "Alice", "email": "alice@example.com" },
  { "name": "Bob", "email": "bob@gmail.com" },
  { "name": "Charlie", "email": "charlie@example.com" }
]

Code Node 코드:

const internalUsers = [];
const externalUsers = [];

for (const item of items) {
  const email = item.json.email;
  if (email && email.endsWith('@example.com')) {
    internalUsers.push({
      json: { ...item.json, userType: 'internal' }
    });
  } else {
    externalUsers.push({
      json: { ...item.json, userType: 'external' }
    });
  }
}

// Code Node는 여러 개의 출력을 가질 수 있습니다.
// [output 0]: internalUsers
// [output 1]: externalUsers
return [internalUsers, externalUsers];

설명:

  1. internalUsersexternalUsers라는 두 개의 빈 배열을 준비합니다.
  2. 각 사용자의 이메일을 확인하여 @example.com으로 끝나는지 검사합니다.
  3. 조건에 따라 해당 사용자를 적절한 배열에 추가하고, userType 필드를 추가합니다.
  4. 마지막으로 [internalUsers, externalUsers]와 같이 배열의 배열을 반환하여 n8n의 “Output 0”, “Output 1” 등으로 데이터를 분기합니다.

출력 데이터 (JSON):

  • Output 0 (내부 사용자):
    [
      { "name": "Alice", "email": "alice@example.com", "userType": "internal" },
      { "name": "Charlie", "email": "charlie@example.com", "userType": "internal" }
    ]
  • Output 1 (외부 사용자):
    [
      { "name": "Bob", "email": "bob@gmail.com", "userType": "external" }
    ]

4.4. 예제 4: 간단한 외부 API 호출 (⚠️ 주의) 🌐

시나리오: (교육용 예제) Code Node 내에서 외부 API를 호출하고 응답을 처리합니다. 주의: 일반적인 API 호출은 n8n의 HTTP Request 노드를 사용하는 것이 훨씬 간편하고 권장됩니다! Code Node는 HTTP Request 노드로 불가능한 복잡한 인증/헤더/본문 로직이 필요할 때만 고려하세요.

Code Node 코드:

// n8n Code Node 환경에는 기본적으로 fetch API가 내장되어 있지 않을 수 있습니다.
// 일반적으로는 n8n에서 제공하는 'httpRequest' 유틸리티 함수나 'axios' 같은 라이브러리를 사용합니다.
// 여기서는 개념 설명을 위해 fetch를 사용하지만, 실제 n8n에서는 아래 주석 처리된 부분을 참고하세요.

// const axios = require('axios'); // Node.js 환경이므로 axios를 require할 수 있습니다.
// const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');

// n8n 내장 유틸리티 (실제 사용 예시)
// const httpRequest = require('n8n-nodes-base/dist/nodes/HttpRequest');
// const response = await httpRequest.request({ url: 'https://jsonplaceholder.typicode.com/posts/1', method: 'GET' });

// 일반적인 Node.js 환경처럼 fetch를 사용한다고 가정 (실제 n8n 환경에서는 제한적일 수 있음)
try {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos/1'); // 더미 API
  const data = await response.json();

  return [{
    json: {
      apiResponse: data,
      status: 'success',
      fetchedAt: new Date().toISOString()
    }
  }];
} catch (error) {
  return [{
    json: {
      error: error.message,
      status: 'failed',
      fetchedAt: new Date().toISOString()
    }
  }];
}

설명:

  1. fetch API를 사용하여 외부 API (여기서는 JSONPlaceholder)에 GET 요청을 보냅니다.
  2. await 키워드를 사용하여 응답을 기다리고 JSON으로 파싱합니다.
  3. 성공적으로 데이터를 가져오면 해당 데이터를 포함한 json 객체를 반환합니다.
  4. 에러 발생 시 try...catch 블록을 통해 에러 메시지를 반환합니다.

출력 데이터 (JSON):

[
  { "apiResponse": { "userId": 1, "id": 1, "title": "delectus aut autem", "completed": false }, "status": "success", "fetchedAt": "2023-10-27T10:30:00.000Z" }
]

(데이터와 시간은 달라질 수 있습니다)


⚠️ 5. Code Node 사용 시 주의사항 및 모범 사례

Code Node는 강력하지만, 오용하면 워크플로우를 복잡하게 만들거나 디버깅을 어렵게 할 수 있습니다.

  • JavaScript 지식 필수: 기본적인 JavaScript 문법, 배열 및 객체 조작, 비동기 프로그래밍(Promise, async/await)에 대한 이해가 필요합니다. 🧑‍💻
  • 디버깅의 어려움: Code Node 내부의 코드를 디버깅하는 것은 시각적인 노드보다 어렵습니다. console.log()를 적극적으로 활용하여 변수 값이나 중간 처리 결과를 Execution Log에서 확인하세요.
  • 성능 고려: 대량의 데이터를 처리할 때는 Code Node 내에서 복잡한 반복문이나 계산을 많이 수행하면 워크플로우 속도가 느려질 수 있습니다. 가능한 한 효율적인 알고리즘을 사용하세요.
  • 재사용성: 복잡한 로직이라면, 함수로 분리하여 코드를 더 읽기 쉽고 관리하기 쉽게 만드세요.
  • 에러 핸들링: 예상치 못한 상황(예: 데이터 누락, API 호출 실패)에 대비하여 try...catch 블록을 사용하여 에러를 안전하게 처리하는 습관을 들이세요.
  • 보안: executeCommand()와 같은 함수는 시스템 명령을 직접 실행하므로, 절대 신뢰할 수 없는 외부 입력값을 이 함수에 직접 전달하지 마세요. 보안 취약점으로 이어질 수 있습니다. 🔒
  • 가독성: 주석을 충분히 달아 다른 사람(또는 미래의 자신)이 코드를 이해하기 쉽게 만드세요.

🎉 마무리하며

n8n Code Node는 여러분의 자동화 워크플로우를 한 차원 높게 끌어올릴 수 있는 강력한 도구입니다. 기본 노드로는 구현하기 어려운 복잡한 데이터 처리, 커스텀 로직, 섬세한 조건부 제어 등을 Code Node를 통해 구현할 수 있습니다.

처음에는 자바스크립트 코드를 작성하는 것이 어렵게 느껴질 수 있지만, 몇 가지 예제를 통해 연습하다 보면 금방 익숙해질 것입니다. 두려워하지 말고, 여러분의 아이디어를 코드로 자유롭게 표현해보세요! 🚀

이 가이드가 여러분이 n8n Code Node를 마스터하는 데 큰 도움이 되기를 바랍니다. 궁금한 점이나 더 복잡한 활용 시나리오가 있다면 언제든지 댓글로 남겨주세요!

다음 글에서 또 만나요! Happy Automating! 😊

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다