8
n8n 한국어amn8n.com

n8n 책 스캐너

고급

이것은Content Creation, Multimodal AI분야의자동화 워크플로우로, 16개의 노드를 포함합니다.주로 Set, Code, Webhook, HttpRequest, OpenAi 등의 노드를 사용하며. GPT-4o와 Google Books를 사용하여 책셀 사진에서 책 제목 추출 및 검증

사전 요구사항
  • HTTP Webhook 엔드포인트(n8n이 자동으로 생성)
  • 대상 API의 인증 정보가 필요할 수 있음
  • OpenAI API Key
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
  "id": "n1UbcJnl7I5rMDIe",
  "meta": {
    "instanceId": "7d302cb44fba0420b6a4deb04edff9d7c47e83ef1f3f66f89fe519337b882186",
    "templateCredsSetupCompleted": true
  },
  "name": "n8n submission book scanner",
  "tags": [],
  "nodes": [
    {
      "id": "8fc53cb4-d7bc-4fcb-8c6d-1ee90b4ad5b4",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -896,
        1376
      ],
      "webhookId": "365ea003-fe66-4211-ae03-69f1456d768e",
      "parameters": {
        "path": "365ea003-fe66-4211-ae03-69f1456d768e",
        "options": {},
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "a140d496-9098-43b2-97df-089a811a909d",
      "name": "Webhook에 응답",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        592,
        1376
      ],
      "parameters": {
        "options": {
          "responseCode": 200,
          "responseHeaders": {
            "entries": [
              {
                "name": "Content-Type",
                "value": "application/json"
              }
            ]
          }
        },
        "respondWith": "json",
        "responseBody": "={{$json}}"
      },
      "typeVersion": 1.4
    },
    {
      "id": "050a5432-4d7e-4855-b09f-5ca40a9e0999",
      "name": "이미지 분석",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -448,
        1376
      ],
      "parameters": {
        "text": "=You are a STRICT transformer. Analyze the image of book spines and return only clearly readable titles and authors. \nDo NOT guess. If the author isn't clearly visible, set \"author\": null.\nNormalize capitalization. Deduplicate by title. \nOutput STRICT JSON only:\n{\"books\":[{\"title\":\"string\",\"author\":\"string|null\"}]}\n",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "GPT-4O-MINI"
        },
        "options": {},
        "resource": "image",
        "imageUrls": "={{$json.image}}\n",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "id": "iJ4uczBur5RBMvV4",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "50c4d4b8-b164-47fb-b9c6-2234f3cb5952",
      "name": "스티키 노트",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -992,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Webhook connects to front end that passes on JSON with imageURL (string)"
      },
      "typeVersion": 1
    },
    {
      "id": "d245e1df-a5a2-487c-bd8f-41601be2f05c",
      "name": "스티키 노트1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -736,
        1520
      ],
      "parameters": {
        "height": 80,
        "content": "Input is normalized"
      },
      "typeVersion": 1
    },
    {
      "id": "2c44a9af-f395-45ff-bf4f-91a55c829d3e",
      "name": "스티키 노트2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -512,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Image is analyzed and transformed."
      },
      "typeVersion": 1
    },
    {
      "id": "1ec82294-d70b-4ff6-ab34-c7ef156a41b2",
      "name": "스티키 노트3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -288,
        1520
      ],
      "parameters": {
        "content": "Splits the output (in this case, books) into individual items in preparation for the next step which is to verify the book against a known source to confirm the title and author."
      },
      "typeVersion": 1
    },
    {
      "id": "44f65fae-f829-4b6b-976b-001d071b82ee",
      "name": "스티키 노트4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -96,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Confirms each title against Google Books"
      },
      "typeVersion": 1
    },
    {
      "id": "917a2da3-3691-4904-8cbc-4c00ee4ed72e",
      "name": "스티키 노트5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        112,
        1536
      ],
      "parameters": {
        "height": 80,
        "content": "Normalizes data"
      },
      "typeVersion": 1
    },
    {
      "id": "62b507e0-5b7d-48a3-9e44-aa4ccf339331",
      "name": "스티키 노트6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        304,
        1280
      ],
      "parameters": {
        "height": 80,
        "content": "Reaggregates book list and dedupes"
      },
      "typeVersion": 1
    },
    {
      "id": "94b96cbc-3bd4-4db8-83ff-eb57b4e516c3",
      "name": "스티키 노트7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        544,
        1520
      ],
      "parameters": {
        "height": 80,
        "content": "Returns list back to frontend."
      },
      "typeVersion": 1
    },
    {
      "id": "c4796062-4c64-474a-85b9-8306585c18b1",
      "name": "정규화된 입력",
      "type": "n8n-nodes-base.set",
      "position": [
        -672,
        1376
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "94b88376-fed9-46cf-882f-d4c0d7670350",
              "name": "image",
              "type": "string",
              "value": "={{ ($json.body?.imageUrl || $json.body?.image || $json.imageUrl || $json.image || '').trim() }}\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "dbe25007-44da-403b-89bf-bcbf57591d58",
      "name": "항목 목록 분할",
      "type": "n8n-nodes-base.code",
      "position": [
        -240,
        1376
      ],
      "parameters": {
        "jsCode": "const items = await $input.all();\nconst out = [];\n\nfunction stripCodeFence(s) {\n  return String(s || '')\n    .replace(/^```json\\s*/i, '')\n    .replace(/^```\\s*/i, '')\n    .replace(/```$/, '')\n    .trim();\n}\n\nfunction firstAuthor(a) {\n  if (!a) return null;\n  // keep a single name for better \"inauthor:\" matching\n  const s = String(a);\n  const parts = s.split(/\\s*(?:,| and |&)\\s*/i);\n  return (parts[0] || '').trim() || null;\n}\n\nfor (const item of items) {\n  let books = null;\n\n  // Case 1: you already have an object with books[]\n  if (Array.isArray(item.json?.books)) {\n    books = item.json.books;\n  }\n\n  // Case 2: you have a string in `content` with ```json ... ```\n  if (!books && typeof item.json?.content === 'string') {\n    const cleaned = stripCodeFence(item.json.content);\n    try {\n      const parsed = JSON.parse(cleaned);\n      if (Array.isArray(parsed.books)) books = parsed.books;\n    } catch (e) {\n      // ignore; we'll fall back\n    }\n  }\n\n  if (!books) continue;\n\n  for (const b of books) {\n    out.push({\n      json: {\n        title: b.title,\n        author: b.author ?? null,\n        // helper field only for the search query:\n        searchAuthor: firstAuthor(b.author)\n      }\n    });\n  }\n}\n\nreturn out;\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2017d011-6d85-4844-a08a-0a7e88ae052d",
      "name": "제목 검증",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -32,
        1376
      ],
      "parameters": {
        "url": "https://www.googleapis.com/books/v1/volumes",
        "options": {
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "=q",
              "value": "={{ \n  'intitle:\"' + $json.title.replace(/\"/g,'') + '\"' +\n  ($json.searchAuthor ? ' inauthor:\"' + $json.searchAuthor.replace(/\"/g,'') + '\"' : '')\n}}\n"
            },
            {
              "name": "maxResults",
              "value": "5"
            },
            {
              "name": "printType",
              "value": "books"
            },
            {
              "name": "orderBy",
              "value": "relevance"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "7d94ce36-fc7c-44cb-8df6-6f13293160a4",
      "name": "정규화된 데이터",
      "type": "n8n-nodes-base.set",
      "position": [
        176,
        1376
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "67464bb0-8615-41a5-8408-11a95708d200",
              "name": "=title",
              "type": "string",
              "value": "={{ $json.items?.[0]?.volumeInfo?.title || $prevNode('Code').json.title }}\n"
            },
            {
              "id": "8db9c2f0-3193-428e-adab-2745f397233c",
              "name": "author",
              "type": "string",
              "value": "={{ $json.items?.[0]?.volumeInfo?.authors?.[0] || $prevNode('Code').json.author || null }}\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "99d74571-f35d-4086-93ce-f7f67768a8f0",
      "name": "목록 재집계",
      "type": "n8n-nodes-base.code",
      "position": [
        384,
        1376
      ],
      "parameters": {
        "jsCode": "const items = await $input.all();\nconst seen = new Set();\nconst books = [];\n\nfor (const it of items) {\n  const t = (it.json.title || '').toLowerCase().trim();\n  if (t && !seen.has(t)) {\n    seen.add(t);\n    books.push({ title: it.json.title, author: it.json.author ?? null });\n  }\n}\n\nreturn [{ json: { books } }];\n"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "5f454fe8-f8b7-4302-820e-ec24a00f13bd",
  "connections": {
    "8fc53cb4-d7bc-4fcb-8c6d-1ee90b4ad5b4": {
      "main": [
        [
          {
            "node": "c4796062-4c64-474a-85b9-8306585c18b1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "050a5432-4d7e-4855-b09f-5ca40a9e0999": {
      "main": [
        [
          {
            "node": "dbe25007-44da-403b-89bf-bcbf57591d58",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "7d94ce36-fc7c-44cb-8df6-6f13293160a4": {
      "main": [
        [
          {
            "node": "99d74571-f35d-4086-93ce-f7f67768a8f0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "dbe25007-44da-403b-89bf-bcbf57591d58": {
      "main": [
        [
          {
            "node": "2017d011-6d85-4844-a08a-0a7e88ae052d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "c4796062-4c64-474a-85b9-8306585c18b1": {
      "main": [
        [
          {
            "node": "050a5432-4d7e-4855-b09f-5ca40a9e0999",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2017d011-6d85-4844-a08a-0a7e88ae052d": {
      "main": [
        [
          {
            "node": "7d94ce36-fc7c-44cb-8df6-6f13293160a4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "99d74571-f35d-4086-93ce-f7f67768a8f0": {
      "main": [
        [
          {
            "node": "a140d496-9098-43b2-97df-089a811a909d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
자주 묻는 질문

이 워크플로우를 어떻게 사용하나요?

위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.

이 워크플로우는 어떤 시나리오에 적합한가요?

고급 - 콘텐츠 제작, 멀티모달 AI

유료인가요?

이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.

워크플로우 정보
난이도
고급
노드 수16
카테고리2
노드 유형7
난이도 설명

고급 사용자를 위한 16+개 노드의 복잡한 워크플로우

외부 링크
n8n.io에서 보기

이 워크플로우 공유

카테고리

카테고리: 34