발행일: 2026-10-06(화) 11:44
홈›도서›하루 30분 n8n ④›부록 D — 커스텀 노드 TypeScript 보일러플레이트
부록 D★★☆

부록 D — 커스텀 노드 TypeScript 보일러플레이트

📖 이론10분

"좋은 보일러플레이트 하나가 수백 시간을 아껴준다."

— 실리콘밸리 개발자 격언
🎯 오늘의 학습 목표
  • Execute 노드 완성 패턴을 이해하고 새로운 노드 개발의 출발점으로 활용할 수 있다.
Execute 노드 완성 패턴 — MyApiNode.node.ts
typescript
import {
  IExecuteFunctions,
  INodeExecutionData,
  INodeType,
  INodeTypeDescription,
  IHttpRequestOptions,
  NodeOperationError,
} from 'n8n-workflow';

export class MyApiNode implements INodeType {
  description: INodeTypeDescription = {
    displayName: '내 API 노드',
    name: 'myApiNode',
    group: ['transform'],
    version: 1,
    description: 'REST API를 호출하는 커스텀 노드',
    defaults: { name: '내 API 노드' },
    inputs: ['main'],
    outputs: ['main'],
    credentials: [{ name: 'myApiCredentials', required: true }],
    properties: [
      {
        displayName: '작업',
        name: 'operation',
        type: 'options',
        noDataExpression: true,
        options: [
          { name: '데이터 가져오기', value: 'get' },
          { name: '데이터 만들기', value: 'create' },
        ],
        default: 'get',
      },
      {
        displayName: 'ID',
        name: 'id',
        type: 'string',
        displayOptions: { show: { operation: ['get'] } },
        default: '',
        required: true,
      },
      {
        displayName: '이름',
        name: 'name',
        type: 'string',
        displayOptions: { show: { operation: ['create'] } },
        default: '',
        required: true,
      },
    ],
  };

  async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
    const items = this.getInputData();
    const returnData: INodeExecutionData[] = [];
    const credentials = await this.getCredentials('myApiCredentials');

    for (let i = 0; i < items.length; i++) {
      try {
        const operation = this.getNodeParameter('operation', i) as string;
        let responseData;

        if (operation === 'get') {
          const id = this.getNodeParameter('id', i) as string;
          const options: IHttpRequestOptions = {
            method: 'GET',
            url: `${credentials.baseUrl}/items/${id}`,
            headers: { Authorization: `Bearer ${credentials.apiKey}` },
          };
          responseData = await this.helpers.httpRequest(options);
        } else if (operation === 'create') {
          const name = this.getNodeParameter('name', i) as string;
          const options: IHttpRequestOptions = {
            method: 'POST',
            url: `${credentials.baseUrl}/items`,
            headers: {
              Authorization: `Bearer ${credentials.apiKey}`,
              'Content-Type': 'application/json',
            },
            body: { name },
            json: true,
          };
          responseData = await this.helpers.httpRequest(options);
        }

        returnData.push({
          json: responseData as Record<string, unknown>,
          pairedItem: { item: i },
        });
      } catch (error) {
        if (this.continueOnFail()) {
          returnData.push({
            json: { error: (error as Error).message },
            pairedItem: { item: i },
          });
          continue;
        }
        throw new NodeOperationError(this.getNode(), error as Error, { itemIndex: i });
      }
    }
    return [returnData];
  }
}
← 이전
부록 C — MCP 클라이언트별 설정 치트시트
다음 →
부록 E — 30일 완성 운영 플레이북 템플릿
← 목차로 돌아가기