n8n vs Make.com 徹底比較:最適な自動化ツールの選び方

はじめに

業務自動化ツールの選択は、組織の生産性と効率性に大きな影響を与えます。本記事では、人気の高い2つの自動化プラットフォーム「n8n」と「Make.com(旧Integromat)」を徹底比較し、それぞれの特徴と最適な使用シーンを解説します。

機能比較表

機能 n8n Make.com
デプロイメント セルフホスト / クラウド クラウドのみ
料金体系 オープンソース(無料)/ 有料版あり 従量課金制
ノード/モジュール数 400+ 1000+
カスタムコード JavaScript/Python 対応 限定的
ビジュアルエディタ
エラーハンドリング 高度 標準
バージョン管理 Git 連携可能 なし
エンタープライズ機能

n8n の詳細解説

1. セルフホスティングによる完全制御

# docker-compose.yml for n8n
version: '3'
services:
  n8n:
    image: n8nio/n8n
    restart: always
    ports:
      - "5678:5678"
    environment:
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
      - N8N_HOST=n8n.company.com
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=https://n8n.company.com/
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
    volumes:
      - n8n_data:/home/node/.n8n
      - ./custom-nodes:/home/node/.n8n/custom
    networks:
      - n8n-network

  postgres:
    image: postgres:13
    restart: always
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - n8n-network

volumes:
  n8n_data:
  postgres_data:

networks:
  n8n-network:

2. カスタムノード開発

// カスタム n8n ノードの実装例
import {
    IExecuteFunctions,
    INodeExecutionData,
    INodeType,
    INodeTypeDescription,
    NodeOperationError,
} from 'n8n-workflow';

export class CustomBusinessLogic implements INodeType {
    description: INodeTypeDescription = {
        displayName: 'Custom Business Logic',
        name: 'customBusinessLogic',
        icon: 'file:custom.svg',
        group: ['transform'],
        version: 1,
        description: '独自のビジネスロジックを実行',
        defaults: {
            name: 'Custom Business Logic',
        },
        inputs: ['main'],
        outputs: ['main'],
        properties: [
            {
                displayName: 'Operation',
                name: 'operation',
                type: 'options',
                options: [
                    {
                        name: 'Process Order',
                        value: 'processOrder',
                    },
                    {
                        name: 'Calculate Commission',
                        value: 'calculateCommission',
                    },
                    {
                        name: 'Validate Data',
                        value: 'validateData',
                    },
                ],
                default: 'processOrder',
            },
            {
                displayName: 'Business Rules',
                name: 'businessRules',
                type: 'json',
                default: '{}',
                description: 'ビジネスルールをJSON形式で定義',
            },
        ],
    };

    async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
        const items = this.getInputData();
        const operation = this.getNodeParameter('operation', 0) as string;
        const businessRules = this.getNodeParameter('businessRules', 0) as object;
        
        const returnData: INodeExecutionData[] = [];

        for (let i = 0; i < items.length; i++) {
            try {
                let result: any;

                switch (operation) {
                    case 'processOrder':
                        result = await this.processOrder(items[i].json, businessRules);
                        break;
                    case 'calculateCommission':
                        result = await this.calculateCommission(items[i].json, businessRules);
                        break;
                    case 'validateData':
                        result = await this.validateData(items[i].json, businessRules);
                        break;
                    default:
                        throw new NodeOperationError(this.getNode(), `Unknown operation: ${operation}`);
                }

                returnData.push({
                    json: result,
                    pairedItem: { item: i },
                });
            } catch (error) {
                if (this.continueOnFail()) {
                    returnData.push({
                        json: {
                            error: error.message,
                        },
                        pairedItem: { item: i },
                    });
                    continue;
                }
                throw error;
            }
        }

        return [returnData];
    }

    private async processOrder(data: any, rules: any): Promise<any> {
        // 注文処理ロジック
        const order = {
            ...data,
            processedAt: new Date().toISOString(),
            status: 'processed',
        };

        // ビジネスルールの適用
        if (rules.minimumOrderValue && data.totalAmount < rules.minimumOrderValue) {
            order.status = 'rejected';
            order.reason = 'Below minimum order value';
        }

        if (rules.requireApproval && data.totalAmount > rules.approvalThreshold) {
            order.status = 'pending_approval';
        }

        return order;
    }

    private async calculateCommission(data: any, rules: any): Promise<any> {
        const baseCommission = data.salesAmount * (rules.commissionRate || 0.1);
        let finalCommission = baseCommission;

        // パフォーマンスボーナス
        if (data.salesAmount > rules.bonusThreshold) {
            finalCommission += baseCommission * (rules.bonusRate || 0.2);
        }

        return {
            salesPerson: data.salesPerson,
            salesAmount: data.salesAmount,
            baseCommission,
            bonus: finalCommission - baseCommission,
            totalCommission: finalCommission,
            calculatedAt: new Date().toISOString(),
        };
    }

    private async validateData(data: any, rules: any): Promise<any> {
        const errors: string[] = [];
        const requiredFields = rules.requiredFields || [];

        // 必須フィールドチェック
        for (const field of requiredFields) {
            if (!data[field]) {
                errors.push(`Missing required field: ${field}`);
            }
        }

        // データ型検証
        if (rules.dataTypes) {
            for (const [field, expectedType] of Object.entries(rules.dataTypes)) {
                if (data[field] && typeof data[field] !== expectedType) {
                    errors.push(`Invalid type for ${field}: expected ${expectedType}`);
                }
            }
        }

        return {
            isValid: errors.length === 0,
            errors,
            validatedData: data,
            validatedAt: new Date().toISOString(),
        };
    }
}

3. 高度なワークフロー例

{
  "name": "注文処理自動化ワークフロー",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "webhook/order",
        "responseMode": "onReceived",
        "options": {}
      },
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [250, 300]
    },
    {
      "parameters": {
        "functionCode": "// 注文データの検証と整形\nconst orderData = items[0].json;\n\n// 在庫確認API呼び出し用データ準備\nconst inventoryCheckData = {\n  items: orderData.items.map(item => ({\n    sku: item.sku,\n    quantity: item.quantity\n  }))\n};\n\n// 顧客情報の検証\nif (!orderData.customerId || !orderData.email) {\n  throw new Error('顧客情報が不完全です');\n}\n\nreturn [{\n  json: {\n    order: orderData,\n    inventoryCheck: inventoryCheckData,\n    timestamp: new Date().toISOString()\n  }\n}];"
      },
      "name": "データ検証",
      "type": "n8n-nodes-base.function",
      "position": [450, 300]
    },
    {
      "parameters": {
        "url": "https://api.inventory.com/check",
        "method": "POST",
        "jsonParameters": true,
        "options": {},
        "bodyParametersJson": "={{ $json.inventoryCheck }}"
      },
      "name": "在庫確認",
      "type": "n8n-nodes-base.httpRequest",
      "position": [650, 300]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.allItemsInStock }}",
              "value2": true
            }
          ]
        }
      },
      "name": "在庫有無判定",
      "type": "n8n-nodes-base.if",
      "position": [850, 300]
    },
    {
      "parameters": {
        "operation": "create",
        "resource": "order",
        "additionalFields": {
          "status": "confirmed",
          "paymentMethod": "={{ $node['Webhook'].json.paymentMethod }}",
          "shippingAddress": "={{ $node['Webhook'].json.shippingAddress }}"
        }
      },
      "name": "注文作成",
      "type": "n8n-nodes-base.salesforce",
      "position": [1050, 200]
    },
    {
      "parameters": {
        "fromEmail": "orders@company.com",
        "toEmail": "={{ $node['Webhook'].json.email }}",
        "subject": "ご注文確認 - 注文番号: {{ $json.orderId }}",
        "html": "<h2>ご注文ありがとうございます</h2><p>注文番号: {{ $json.orderId }}</p><p>お届け予定日: {{ $json.estimatedDelivery }}</p>"
      },
      "name": "確認メール送信",
      "type": "n8n-nodes-base.emailSend",
      "position": [1250, 200]
    }
  ]
}

Make.com の詳細解説

1. ビジュアルシナリオ構築

Make.com の最大の特徴は、直感的なビジュアルインターフェースです。ドラッグ&ドロップで複雑なワークフローを構築できます。

2. 豊富なテンプレート

// Make.com シナリオの例(JSON表現)
{
  "name": "ECサイト注文処理",
  "flow": [
    {
      "module": "shopify:watchOrders",
      "parameters": {
        "store": "mystore.myshopify.com",
        "status": "paid"
      }
    },
    {
      "module": "googlesheets:addRow",
      "parameters": {
        "spreadsheetId": "1234567890",
        "sheetName": "Orders",
        "values": {
          "A": "{{1.order_number}}",
          "B": "{{1.email}}",
          "C": "{{1.total_price}}",
          "D": "{{formatDate(1.created_at, 'YYYY-MM-DD')}}"
        }
      }
    },
    {
      "module": "slack:createMessage",
      "parameters": {
        "channel": "#sales",
        "text": "新規注文: {{1.order_number}}\n金額: ¥{{1.total_price}}\n顧客: {{1.email}}"
      }
    },
    {
      "module": "router",
      "routes": [
        {
          "filter": "{{1.total_price > 50000}}",
          "modules": [
            {
              "module": "email:send",
              "parameters": {
                "to": "manager@company.com",
                "subject": "高額注文アラート",
                "content": "注文番号 {{1.order_number}} (¥{{1.total_price}}) の確認をお願いします。"
              }
            }
          ]
        }
      ]
    }
  ]
}

3. データ変換機能

// Make.com のデータ変換関数例
{
  "transformations": [
    {
      "name": "顧客データ整形",
      "function": "{{
        setVariable('formattedCustomer', {
          'fullName': join(' ', [1.firstName, 1.lastName]),
          'email': lower(1.email),
          'phone': replace(1.phone, '-', ''),
          'createdDate': formatDate(now, 'YYYY-MM-DD'),
          'customerId': uuid()
        })
      }}"
    },
    {
      "name": "金額計算",
      "function": "{{
        setVariable('calculations', {
          'subtotal': sum(map(1.items, 'price')),
          'tax': sum(map(1.items, 'price')) * 0.1,
          'total': sum(map(1.items, 'price')) * 1.1,
          'discount': if(sum(map(1.items, 'price')) > 10000, 
                        sum(map(1.items, 'price')) * 0.05, 0)
        })
      }}"
    }
  ]
}

使用シーン別の推奨

n8n が適している場合

  1. データプライバシーが重要

    • 金融、医療、政府関連
    • GDPR/HIPAA 準拠が必要
  2. 高度なカスタマイズが必要

    • 独自のビジネスロジック
    • 既存システムとの深い統合
  3. 開発者リソースがある

    • 技術チームが存在
    • DevOps 体制が整っている

Make.com が適している場合

  1. 素早い導入が必要

    • 即座に使い始めたい
    • インフラ管理を避けたい
  2. 非技術者が主に使用

    • マーケティングチーム
    • 営業チーム
  3. 標準的な統合で十分

    • 一般的なSaaS連携
    • 定型的な業務フロー

コスト比較

n8n のコスト構造

# セルフホスト版(月額)
インフラコスト:
  - サーバー: ¥5,000〜¥20,000
  - データベース: ¥2,000〜¥10,000
  - バックアップ: ¥1,000〜¥5,000
  
人件費:
  - 初期セットアップ: 40時間
  - 月間メンテナンス: 10時間

# クラウド版
- Starter: $20/月
- Pro: $50/月
- Enterprise: カスタム価格

Make.com のコスト構造

# 従量課金制(月額)
Free:
  - 1,000 operations/月
  - 無料

Core:
  - 10,000 operations/月
  - $9/月

Pro:
  - 100,000 operations/月
  - $16/月

Teams:
  - 500,000 operations/月
  - $29/月

Enterprise:
  - カスタム

移行戦略

n8n から Make.com への移行

  1. ワークフローの棚卸し
  2. API エンドポイントのマッピング
  3. 段階的移行計画

Make.com から n8n への移行

// 移行スクリプトの例
const migrateMakeToN8n = async (makeScenario) => {
  const n8nWorkflow = {
    name: makeScenario.name,
    nodes: [],
    connections: {}
  };

  // モジュールをノードに変換
  makeScenario.modules.forEach((module, index) => {
    const node = convertModuleToNode(module);
    node.position = [250 + (index * 200), 300];
    n8nWorkflow.nodes.push(node);
  });

  // 接続情報の構築
  n8nWorkflow.connections = buildConnections(n8nWorkflow.nodes);

  return n8nWorkflow;
};

const convertModuleToNode = (module) => {
  const nodeTypeMap = {
    'webhook': 'n8n-nodes-base.webhook',
    'http': 'n8n-nodes-base.httpRequest',
    'googlesheets': 'n8n-nodes-base.googleSheets',
    'slack': 'n8n-nodes-base.slack'
  };

  return {
    name: module.name,
    type: nodeTypeMap[module.type] || 'n8n-nodes-base.function',
    parameters: convertParameters(module.parameters)
  };
};

ベストプラクティス

1. エラーハンドリング

// n8n でのエラーハンドリング
{
  "nodes": [
    {
      "type": "n8n-nodes-base.errorTrigger",
      "name": "Error Handler",
      "parameters": {
        "errorMessage": "={{$json.error.message}}",
        "errorWorkflow": "={{$json.error.workflow}}"
      }
    }
  ]
}

// Make.com でのエラーハンドリング
{
  "errorHandler": {
    "type": "ignore",
    "settings": {
      "maxRetries": 3,
      "retryInterval": 60,
      "notification": {
        "email": "admin@company.com",
        "slack": "#errors"
      }
    }
  }
}

2. パフォーマンス最適化

  • バッチ処理: 大量データは分割処理
  • 並列実行: 独立したタスクは並列化
  • キャッシング: 頻繁にアクセスするデータはキャッシュ

まとめ

n8n と Make.com は、それぞれ異なる強みを持つ優れた自動化ツールです。選択にあたっては、以下の要素を考慮してください:

  1. 技術要件: カスタマイズの必要性
  2. 組織体制: 技術リソースの有無
  3. セキュリティ: データの機密性
  4. 予算: 初期投資と運用コスト
  5. 拡張性: 将来的な成長への対応

エンハンスド株式会社では、両ツールの導入支援から運用まで、包括的なサポートを提供しています。お客様のニーズに最適なソリューションをご提案いたします。