금. 8월 15th, 2025

G: 안녕하세요! 자동화와 워크플로우를 사랑하는 여러분! 🙋‍♂️ n8n은 강력한 오픈소스 자동화 도구로, 다양한 서비스와 연동하여 복잡한 워크플로우를 쉽고 빠르게 구축할 수 있게 해줍니다. 하지만 때로는 내장 노드나 커뮤니티 노드만으로는 충족되지 않는 특별한 요구사항이 생기곤 합니다. 바로 이럴 때, 커스텀 노드의 힘이 발휘되죠! 💪

“커스텀 노드? 왠지 어려워 보여요… 삽질 많이 해야 할 것 같아서 엄두가 안 나요 😩” 라고 생각하셨다면, 이 글이 바로 여러분을 위한 비법서입니다! 오늘은 제가 직접 n8n 커스텀 노드를 만들면서 겪었던 삽질을 줄이고, 효율적으로 개발할 수 있었던 노하우를 아낌없이 공개합니다. 자, 그럼 시작해볼까요? 🎉


1. 왜 커스텀 노드가 필요한가요? 🤔

n8n은 이미 수많은 노드를 제공하지만, 다음과 같은 경우에는 커스텀 노드가 빛을 발합니다.

  • 특정 API 연동: 사내 시스템의 내부 API, 아직 n8n에 노드가 없는 신규 서비스의 API 등 아주 특수한 API를 연동해야 할 때 유용합니다.
    • 예시: “우리 회사의 재고 관리 시스템 API에 직접 접속해서 데이터를 가져와야 해요!”
  • 복잡한 비즈니스 로직: 여러 데이터를 조합하거나, 복잡한 조건에 따라 데이터를 가공해야 할 때, Expression이나 Function 노드만으로는 한계가 있을 수 있습니다. 이때 Node.js의 강력한 기능들을 활용하여 노드 내부에 로직을 구현할 수 있습니다.
    • 예시: “들어온 고객 데이터에서 특정 패턴을 분석하고, 여러 변수를 조합해서 최종 점수를 산출해야 해요!”
  • 재사용 가능한 모듈화: 특정 작업을 여러 워크플로우에서 반복적으로 사용해야 할 때, 커스텀 노드로 만들어두면 매우 효율적입니다.
    • 예시: “모든 워크플로우에서 공통적으로 사용되는 데이터 정제 과정이 있는데, 이걸 한 번에 처리하는 노드가 있었으면 좋겠어요!”
  • 외부 라이브러리 활용: n8n 환경에서 특정 외부 Node.js 라이브러리를 사용해야 할 때, 커스텀 노드 내부에 해당 라이브러리를 포함시켜 기능을 확장할 수 있습니다.
    • 예시: “특정 이미지 처리 라이브러리나 암호화 라이브러리를 n8n 워크플로우에서 사용하고 싶어요!”

2. 커스텀 노드 개발, 시작 전에 알아야 할 것들 💡

삽질을 줄이려면 올바른 도구와 환경 설정이 필수입니다.

  • Node.js & npm (또는 yarn): n8n 커스텀 노드는 Node.js 환경에서 개발됩니다. 최신 LTS 버전을 사용하는 것을 권장합니다.

    • 설치 확인: node -vnpm -v 또는 yarn -v
  • TypeScript (강력 추천): n8n 공식 문서에서도 TypeScript 사용을 권장합니다. 타입 안정성을 제공하여 개발 중 발생할 수 있는 오류를 미리 방지하고, 코드 자동 완성 기능을 통해 생산성을 높여줍니다. (JavaScript로도 가능하지만, 삽질을 줄이는 비법은 역시 TypeScript입니다! 😉)

  • 로컬 n8n 개발 환경: 커스텀 노드를 개발하면서 실시간으로 테스트할 로컬 n8n 인스턴스가 필요합니다.

    • Docker를 사용하거나, npm install -g n8n으로 전역 설치 후 n8n 명령어로 실행할 수 있습니다.
  • n8n-node-dev 툴: 이게 바로 삽질을 줄이는 핵심 도구 중 하나입니다! 🤩 n8n-node-dev는 커스텀 노드 개발 시 hot-reloading (코드 변경 시 자동으로 n8n에 반영) 및 디버깅 환경 설정을 도와주는 유틸리티입니다.

    npm install -g n8n-node-dev
  • n8n의 데이터 구조 이해: n8n은 데이터를 “아이템(Item)” 단위로 처리합니다. 각 아이템은 JSON 객체 형태로 전달되며, 배열로 여러 아이템이 들어올 수 있습니다. 입력과 출력 모두 이 구조를 따릅니다.

    [
      {
        "json": {
          "field1": "value1",
          "field2": "value2"
        },
        "binary": {} // 바이너리 데이터가 있다면 여기에
      },
      {
        "json": {
          "field1": "value3",
          "field2": "value4"
        },
        "binary": {}
      }
    ]

3. 삽질 줄이는 핵심 비법 🔑: 공식 보일러플레이트와 n8n-node-dev 활용!

자, 이제 진짜 비법입니다! 🤫

비법 1: n8n-node-starter 보일러플레이트 활용하기

맨땅에 헤딩하지 마세요! n8n은 커스텀 노드 개발을 위한 공식 보일러플레이트 프로젝트인 n8n-node-starter를 제공합니다. 이걸 사용하면 기본적인 파일 구조, TypeScript 설정, 테스트 환경 설정 등 귀찮은 초기 세팅을 한 번에 끝낼 수 있습니다.

# 새로운 노드 프로젝트 생성 (my-custom-node는 원하는 프로젝트 이름)
npx n8n-node-dev new my-custom-node

cd my-custom-node

이 명령어를 실행하면 my-custom-node 폴더가 생성되고, 그 안에 다음과 같은 핵심 파일들이 자동으로 생성됩니다.

  • nodes/MyCustomNode.node.ts: 노드의 실제 로직이 들어가는 파일
  • nodes/MyCustomNode.description.ts: 노드의 메타데이터(이름, 입력 필드, 설명 등)가 정의되는 파일
  • package.json: 프로젝트 설정 파일 (의존성, 스크립트 등)
  • tsconfig.json: TypeScript 컴파일러 설정 파일

비법 2: n8n-node-dev start로 실시간 개발 환경 구축

위에서 설치한 n8n-node-dev 툴의 start 명령어를 사용하면 로컬 n8n 인스턴스에 커스텀 노드를 “링크”하여, 코드를 수정할 때마다 자동으로 n8n에 반영되도록 할 수 있습니다. 덕분에 매번 n8n을 재시작하거나 노드를 수동으로 복사할 필요 없이, 웹팩의 hot-reloading처럼 편하게 개발할 수 있습니다. 🔥

# 노드 개발 환경 시작 (프로젝트 루트 폴더에서 실행)
npx n8n-node-dev start

이 명령어를 실행하면 다음과 같은 일이 일어납니다.

  1. 현재 폴더의 노드 코드를 n8n이 인식할 수 있는 형태로 컴파일합니다.
  2. 로컬 n8n 인스턴스의 ~/.n8n/nodes 경로에 컴파일된 노드를 심볼릭 링크(Symbolic Link)합니다.
  3. 코드 변경을 감지하고, 변경 시 자동으로 재컴파일하여 n8n에 반영합니다.

이제 n8n을 실행하고 워크플로우를 만들 때, 여러분이 개발 중인 커스텀 노드를 검색해서 사용할 수 있습니다!


4. 실전! 나만의 “Hello World” 노드 만들기 🚀

npx n8n-node-dev new 명령어로 생성된 기본 구조를 바탕으로 간단한 “Hello World” 노드를 만들어봅시다. 이 노드는 입력받은 이름에 “Hello, “를 붙여서 출력할 것입니다.

Step 1: Description.ts 파일 수정하기

nodes/MyCustomNode.description.ts 파일을 열어 다음과 같이 수정합니다. (기본 생성된 파일명에서 MyCustomNode를 여러분이 만든 이름으로 변경하세요. 저는 GreetingNode로 가정합니다.)

// nodes/GreetingNode.description.ts

import { INodeDescription, INodeProperties } from 'n8n-workflow';

export const greetingNodeDescription: INodeDescription = {
  // 노드 UI에 표시될 이름
  displayName: 'My Awesome Greeting Node',
  // 노드를 식별하는 내부 이름 (코드에서 사용)
  name: 'myAwesomeGreetingNode',
  // 아이콘 (폰트 어썸 아이콘 이름)
  icon: 'fa:hand-spock',
  // 노드 팔레트에서 어떤 그룹에 속할지
  group: ['transform'],
  // 노드 버전 (향후 업데이트 시 유용)
  version: 1,
  // 노드에 대한 간단한 설명
  description: '사용자 이름을 입력받아 인사말을 생성합니다.',
  // 기본 설정 (입력/출력 유형)
  defaults: {
    name: 'Greeting',
  },
  // 입력 유형 (앞 노드에서 데이터를 받는지)
  inputs: ['main'],
  // 출력 유형 (다음 노드로 데이터를 내보내는지)
  outputs: ['main'],
  // 노드의 속성 (사용자가 설정할 수 있는 필드들)
  properties: [
    {
      // 필드 유형: 텍스트 입력
      displayName: '이름',
      name: 'userName',
      type: 'string',
      default: 'World', // 기본값
      placeholder: '인사할 이름을 입력하세요',
      description: '인사말을 생성할 대상의 이름을 입력합니다.',
    },
  ],
};
  • displayName: n8n UI에서 노드 팔레트에 표시될 이름입니다.
  • name: 노드를 코드 상에서 식별하는 고유한 이름입니다. (소문자, 하이픈 권장)
  • group: n8n 노드 팔레트의 카테고리입니다.
  • properties: 사용자가 노드를 설정할 때 볼 수 있는 입력 필드들을 정의합니다. 여기서는 userName이라는 텍스트 필드를 추가했습니다.

Step 2: Node.ts 파일 수정하기

nodes/GreetingNode.node.ts 파일을 열어 다음과 같이 수정합니다.

// nodes/GreetingNode.node.ts

import { IExecuteFunctions } from 'n8n-core';
import {
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
} from 'n8n-workflow';

// 이전에 정의한 노드 설명을 가져옵니다.
import { greetingNodeDescription } from './GreetingNode.description';

export class GreetingNode implements INodeType {
  // 노드 설명을 연결합니다.
  description: INodeTypeDescription = greetingNodeDescription;

  // 노드의 핵심 로직이 들어가는 execute 메서드
  async execute(this: IExecuteFunctions): Promise {
    // 입력 데이터(아이템)들을 가져옵니다.
    // n8n은 여러 아이템을 동시에 처리할 수 있으므로, 루프를 돌려야 합니다.
    const items = this.getInputData();

    // 처리된 결과를 저장할 배열
    const returnData: INodeExecutionData[] = [];

    // 각 입력 아이템에 대해 반복 처리합니다.
    for (let i = 0; i < items.length; i++) {
      // 사용자로부터 설정된 'userName' 값을 가져옵니다.
      // this.getNodeParameter()를 사용하여 노드 속성 값을 가져올 수 있습니다.
      // 두 번째 인자로 인덱스 (i)를 넘겨주면, 각 아이템에 대한 고유한 값을 가져올 수 있습니다.
      const userName = this.getNodeParameter('userName', i) as string;

      // 인사말 메시지를 생성합니다.
      const message = `Hello, ${userName}! Welcome to n8n custom nodes. 👋`;

      // 처리된 데이터를 returnData 배열에 추가합니다.
      // json 객체 안에 원하는 데이터를 넣습니다.
      returnData.push({
        json: {
          greetingMessage: message,
        },
      });
    }

    // 처리된 데이터를 다음 노드로 전달하기 위해 반환합니다.
    // 각 배열 요소는 하나의 출력(output)을 의미하며, 대부분 하나의 출력만 가집니다.
    return [returnData];
  }
}
  • execute 메서드: 이 메서드가 노드가 실행될 때 호출되는 핵심 부분입니다.
  • this.getInputData(): 이전 노드에서 넘어온 데이터를 가져옵니다.
  • this.getNodeParameter('userName', i): Description.ts에서 정의한 userName 속성 값을 가져옵니다. i는 현재 처리 중인 아이템의 인덱스입니다.
  • returnData.push({ json: { greetingMessage: message } }): 처리된 결과를 json 객체 형태로 반환 데이터에 추가합니다.

Step 3: 테스트 및 디버깅

  1. 개발 서버 시작: 프로젝트 루트 폴더에서 터미널을 열고 다음 명령어를 실행합니다.
    npx n8n-node-dev start

    이 명령어가 실행된 터미널은 계속 켜 두어야 합니다.

  2. n8n 실행: 다른 터미널에서 로컬 n8n 인스턴스를 실행합니다.
    n8n
  3. 워크플로우 생성: n8n UI에 접속하여 새 워크플로우를 생성합니다.
  4. 노드 추가: + 버튼을 클릭하고 My Awesome Greeting Node를 검색하여 추가합니다.
  5. 설정: 노드를 클릭하고 userName 필드에 여러분의 이름을 입력해 보세요. (예: Jane Doe)
  6. 실행: 워크플로우를 저장하고 실행해 보세요.
  7. 결과 확인: 다음 노드(예: Set 노드나 Log 노드)를 연결하여 greetingMessage가 잘 출력되는지 확인해 보세요.

    • Set 노드의 “Keep Only Set” 옵션을 끄고 greetingMessage 필드를 추가하면 이전 데이터와 함께 출력됩니다.
    {
      "json": {
        "greetingMessage": "Hello, Jane Doe! Welcome to n8n custom nodes. 👋"
      }
    }

    오류가 발생하면 n8n-node-dev start가 실행 중인 터미널에 에러 메시지가 출력될 것입니다. console.log를 활용하여 디버깅하는 것도 좋은 방법입니다.


5. 더 나아가기 💪

“Hello World”를 넘어 더 복잡하고 유용한 커스텀 노드를 만들기 위한 팁입니다.

  • 크리덴셜 (Credentials) 사용: API 키나 비밀번호와 같은 민감한 정보는 노드 코드에 직접 하드코딩하지 말고, n8n의 크리덴셜 기능을 사용하세요. INodeDescriptioncredentials 속성 및 this.getCredentials() 메서드를 활용합니다.
    • 예시: 외부 API 호출 시 API 키를 안전하게 관리할 수 있습니다.
  • HTTP 요청: this.helpers.httpRequest()를 사용하여 손쉽게 HTTP 요청을 보낼 수 있습니다. 이는 axios 기반으로 되어 있어 익숙하게 사용할 수 있습니다.
    • 예시: 외부 웹훅 호출, REST API 연동 등.
  • 다양한 데이터 유형 처리: 텍스트뿐만 아니라 바이너리 데이터(파일), 배열, 객체 등 n8n의 다양한 데이터 유형을 INodeExecutionData 인터페이스를 통해 처리하는 방법을 익히세요.
    • 예시: 이미지 파일을 다운로드하여 리사이즈하거나, CSV 파일을 파싱하여 JSON으로 변환하는 노드.
  • 에러 핸들링: try...catch 블록을 사용하여 예상치 못한 오류를 처리하고, 사용자에게 의미 있는 에러 메시지를 제공하세요. this.add ]Error()를 사용하여 특정 아이템에 대한 오류를 기록할 수 있습니다.
  • 입출력 옵션: inputsoutputs 속성을 배열로 설정하여 여러 개의 입력/출력 포트를 가질 수 있습니다. execute 메서드에서 this.getInputData(index)return [[output1], [output2]]와 같이 처리합니다.
  • 커뮤니티 활용: n8n 공식 문서, Discord 채널, 포럼 등 커뮤니티에는 수많은 정보와 사례가 공유되어 있습니다. 막히는 부분이 있다면 주저하지 말고 찾아보고 질문하세요!

마무리하며 🎉

n8n 커스텀 노드 개발은 처음에는 다소 복잡해 보일 수 있지만, n8n-node-starter 보일러플레이트와 n8n-node-dev 같은 유용한 도구들을 활용하면 삽질을 최소화하고 생산성을 극대화할 수 있습니다. 여러분만의 특별한 워크플로우를 위한 맞춤형 노드를 직접 만들어보면서, n8n의 무궁무진한 가능성을 경험해 보시길 바랍니다! 🥳

이제 여러분도 나만의 비법으로 n8n 커스텀 노드의 마스터가 될 준비가 되었습니다! 궁금한 점이 있다면 언제든지 댓글로 남겨주세요. 다음에는 더 유익한 정보로 찾아오겠습니다! Happy Automating! ✨

답글 남기기

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