{
  "name": "\uc218\uc5c5 \ubcf5\uc2b5 \uc6cc\ud06c\ud50c\ub85c\uc6b0 Upstage Studio X n8n",
  "nodes": [
    {
      "parameters": {
        "content": "\ud83d\udcda \uc218\uc5c5 \ubcf5\uc2b5 \uc6cc\ud06c\ud50c\ub85c\uc6b0(Upstage Studio)\n\n\uac15\uc758 PDF\ub97c \uc9c0\uc815 \ud3f4\ub354\uc5d0 \ub4dc\ub86d\ud558\uba74 Upstage Studio Agent\uac00\nParse \u2192 Classify \u2192 Extract \u2192 Instruct 4\ub2e8\uacc4\ub85c \uc790\ub3d9 \ucc98\ub9ac\ud558\uace0,\n\ubcf5\uc2b5 \uc9c8\ubb38 + \ud575\uc2ec \uac1c\ub150 \uc815\ub9ac\uac00 \ub2f4\uae34 \ud559\uc2b5 \ucee8\ud14d\uc2a4\ud2b8\ub97c \uc774\uba54\uc77c\ub85c \uc804\uc1a1\ud569\ub2c8\ub2e4.\n\n---\n\n \ud83d\udd04 \uc6cc\ud06c\ud50c\ub85c\uc6b0 \ud750\ub984 (10\ub2e8\uacc4)\n\n01. \uac15\uc758 \ud3f4\ub354 \uac10\uc2dc\n    \uc9c0\uc815 \ud3f4\ub354\uc5d0 PDF\uac00 \ucd94\uac00\ub418\uba74 \uc790\ub3d9 \uc2e4\ud589 (\ud30c\uc77c \ub4dc\ub86d\ub9cc\uc73c\ub85c \ud2b8\ub9ac\uac70)\n02. \ud30c\uc77c \uc77d\uae30\n    \uac10\uc9c0\ub41c PDF\ub97c \ubc14\uc774\ub108\ub9ac \ub370\uc774\ud130\ub85c \uc77d\uae30\n03. Upstage \ud30c\uc77c \uc5c5\ub85c\ub4dc\n    Files API\ub85c \uc5c5\ub85c\ub4dc \u2192 file_id \ud68d\ub4dd\n04. Agent Job \uc0dd\uc131\n    Studio Agent \uc2e4\ud589 \uc694\uccad \u2192 job_id \ud68d\ub4dd\n05. \ucd08\uae30 \ub300\uae30 (15\ucd08)\n    Agent \ucc98\ub9ac \uc2dc\uc791 \ub300\uae30\n06. Job \uc0c1\ud0dc \ud3f4\ub9c1\n    GET /v2/responses/{job_id} \ub85c \uc0c1\ud0dc \uc870\ud68c\n07. \uc644\ub8cc \ud655\uc778 (IF)\n    status === completed?\n    \u251c\u2500\u2500 Yes \u2192 09\ub85c \uc9c4\ud589\n    \u2514\u2500\u2500 No  \u2192 3\ucd08 \ub300\uae30 \ud6c4 06\uc73c\ub85c \ub8e8\ud504 (\uacf5\uc2dd \ubb38\uc11c \uad8c\uc7a5 \ud328\ud134)\n08. \uc7ac\uc2dc\ub3c4 \ub300\uae30 (3\ucd08)\n    \ud3f4\ub9c1 \uac04\uaca9 (Upstage \uacf5\uc2dd \ubb38\uc11c \uad8c\uc7a5: 2~3\ucd08)\n09. \uacb0\uacfc \ud30c\uc2f1 \ubc0f \uc774\uba54\uc77c \ud3ec\ub9f7\n    Instruct \ucd9c\ub825 \ud30c\uc2f1 + definitions JSON \u2192 bullet \ubcc0\ud658\n10. \uc774\uba54\uc77c \ubc1c\uc1a1\n    Gmail SMTP\ub85c \ubcf5\uc2b5 \uc790\ub8cc \uc804\uc1a1\n\n",
        "height": 850,
        "width": 750
      },
      "id": "node-sticky1",
      "name": "\ud83d\udccb PrepDrop \uc2dc\uc791 \uac00\uc774\ub4dc1",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        0,
        -600
      ]
    },
    {
      "parameters": {
        "content": "\u2705 \uc2dc\uc791 \uc804 \ud544\uc218 \uc124\uc815\n\n**1. Upstage Studio\uc5d0\uc11c Agent \ub9cc\ub4e4\uae30**\n- studio.upstage.ai \uc5d0\uc11c Agent\ub97c \uc0dd\uc131\ud558\uc138\uc694\n(\uc608\uc2dc: https://studio.upstage.ai/workflows/agt_hLUyZCKUSRShjkVxEHXsiZ)\n- Classify: \uacfc\ubaa9\ubcc4 \uce74\ud14c\uace0\ub9ac JSON \ud30c\uc77c\uc744 import \ub610\ub294 \uc791\uc131\ud558\uc138\uc694\n- Extract: \uacfc\ubaa9\ubcc4 \ud544\ub4dc \uc2a4\ud0a4\ub9c8 JSON \ud30c\uc77c\uc744 import \ub610\ub294 \uc791\uc131\ud558\uc138\uc694\n- Instruct: \ubcf5\uc2b5 \uc774\uba54\uc77c \uc0dd\uc131 \ud504\ub86c\ud504\ud2b8\ub97c \uc791\uc131\ud558\uc138\uc694\n- Agent ID\ub97c \ubcf5\uc0ac\ud558\uc138\uc694 (agt_xxxxx \ud615\uc2dd)\n\n**2. \uc544\ub798 \ud56d\ubaa9\uc744 \uc2e4\uc81c \uac12\uc73c\ub85c \uad50\uccb4\ud558\uc138\uc694**\n- `YOUR_UPSTAGE_API_KEY` \u2192 Upstage API \ud0a4 (console.upstage.ai)\n- `YOUR_STUDIO_AGENT_ID` \u2192 Studio Agent ID\n- `YOUR/LECTURE/FOLDER/PATH` \u2192 \uac10\uc2dc\ud560 \ub85c\uceec \ud3f4\ub354 \uacbd\ub85c\n- `YOUR_EMAIL@gmail.com` \u2192 \uc774\uba54\uc77c \uc8fc\uc18c\n- SMTP \uc790\uaca9\uc99d\uba85: n8n \uc124\uc815\uc5d0\uc11c Gmail SMTP \uba3c\uc800 \ucd94\uac00\n  \ucd94\uac00 \ubc29\ubc95\n  1. https://n8n-docs.infograb.net/integrations/builtin/credentials/sendemail/\n  2. https://n8n-docs.infograb.net/integrations/builtin/credentials/sendemail/gmail/#_2\n\n**3. n8n \ub85c\uceec \uc2e4\ud589 \uba85\ub839\uc5b4**\n```\nNODES_EXCLUDE='[]' \\\nN8N_RESTRICT_FILE_ACCESS_TO='/your/path' \\\nNODE_FUNCTION_ALLOW_BUILTIN='fs,path' \\\nnpx n8n start\n```\n\uc608\uc2dc:\n```\nNODES_EXCLUDE='[]' \\\nN8N_RESTRICT_FILE_ACCESS_TO='/Users/chawj' \\\nNODE_FUNCTION_ALLOW_BUILTIN='fs,path' \\\nnpx n8n start\n```\n\n---\n\n### \n*by Upstage AI Ambassador \ucc28\uc6d0\uc900*",
        "height": 850,
        "width": 750
      },
      "id": "node-sticky2",
      "name": "\ud83d\udccb PrepDrop \uc2dc\uc791 \uac00\uc774\ub4dc2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        800,
        -600
      ]
    },
    {
      "parameters": {
        "triggerOn": "folder",
        "path": "/YOUR/LECTURE/FOLDER/PATH",
        "events": [
          "add"
        ],
        "options": {}
      },
      "id": "node-trigger",
      "name": "01. \uac15\uc758 \ud3f4\ub354 \uac10\uc2dc",
      "type": "n8n-nodes-base.localFileTrigger",
      "typeVersion": 1,
      "position": [
        0,
        304
      ]
    },
    {
      "parameters": {
        "filePath": "={{ $('01. \uac15\uc758 \ud3f4\ub354 \uac10\uc2dc').item.json.path }}"
      },
      "id": "node-read",
      "name": "02. \ud30c\uc77c \uc77d\uae30",
      "type": "n8n-nodes-base.readBinaryFile",
      "typeVersion": 1,
      "position": [
        240,
        304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.upstage.ai/v2/files",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_UPSTAGE_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "file",
              "inputDataFieldName": "data"
            },
            {
              "name": "purpose",
              "value": "user_data"
            }
          ]
        },
        "options": {}
      },
      "id": "node-upload",
      "name": "03. Upstage \ud30c\uc77c \uc5c5\ub85c\ub4dc",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        480,
        304
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 2000
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.upstage.ai/v2/responses",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_UPSTAGE_API_KEY"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"model\": \"YOUR_STUDIO_AGENT_ID\",\n  \"include\": [\"last\"],\n  \"input\": [\n    {\n      \"role\": \"user\",\n      \"content\": [\n        {\n          \"type\": \"input_file\",\n          \"file_id\": \"{{ $json.id }}\"\n        }\n      ]\n    }\n  ]\n}",
        "options": {}
      },
      "id": "node-create-job",
      "name": "04. Agent Job \uc0dd\uc131",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        720,
        304
      ],
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 2000
    },
    {
      "parameters": {
        "amount": 15,
        "unit": "seconds"
      },
      "id": "node-wait",
      "name": "05. \ucd08\uae30 \ub300\uae30 (15\ucd08)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        960,
        304
      ]
    },
    {
      "parameters": {
        "url": "=https://api.upstage.ai/v2/responses/{{ $('04. Agent Job \uc0dd\uc131').item.json.id }}?include[]=last",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer YOUR_UPSTAGE_API_KEY"
            }
          ]
        },
        "options": {}
      },
      "id": "node-poll",
      "name": "06. Job \uc0c1\ud0dc \ud3f4\ub9c1",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        1200,
        304
      ],
      "retryOnFail": true,
      "maxTries": 5,
      "waitBetweenTries": 5000
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "check-completed",
              "leftValue": "={{ $json.status }}",
              "rightValue": "completed",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "node-if-check",
      "name": "07. \uc644\ub8cc \ud655\uc778",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1440,
        304
      ]
    },
    {
      "parameters": {
        "amount": 3,
        "unit": "seconds"
      },
      "id": "node-retry-wait",
      "name": "08. \uc7ac\uc2dc\ub3c4 \ub300\uae30 (3\ucd08)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1440,
        480
      ]
    },
    {
      "parameters": {
        "jsCode": "const job = $input.first().json;\n\n// \uc644\ub8cc \uc0c1\ud0dc \ud655\uc778\nif (job.status !== 'completed') {\n  throw new Error(`Job status: ${job.status}. Not completed yet.`);\n}\n\n// \ud30c\uc77c \uba54\ud0c0 \ucd94\ucd9c\nconst filePath = $('01. \uac15\uc758 \ud3f4\ub354 \uac10\uc2dc').first().json.path;\nconst fileName = filePath.split('/').pop();\nconst lectureName = fileName.replace(/\\.[^/.]+$/, '');\nconst today = new Date().toISOString().split('T')[0];\n\n// Agent \uacb0\uacfc \ud14d\uc2a4\ud2b8 \ucd94\ucd9c\nlet rawText = '';\ntry {\n  rawText = job.output_text || '';\n  if (!rawText) {\n    const lastOutput = job.output?.[job.output.length - 1];\n    rawText = lastOutput?.content?.[0]?.text || '';\n  }\n} catch (e) {\n  rawText = '';\n}\n\n// Instruct\uac00 JSON \ubb38\uc790\uc5f4\ub85c \uac10\uc2f8\uc11c \ubc18\ud658\ud558\ub294 \uacbd\uc6b0 \ubc97\uaca8\ub0b4\uae30\nif (rawText.startsWith('\"') && rawText.endsWith('\"')) {\n  try {\n    rawText = JSON.parse(rawText);\n  } catch (e) {}\n}\n\n// @definitions JSON \ubc30\uc5f4\uc744 bullet \ud615\uc2dd\uc73c\ub85c \ubcc0\ud658\nfunction formatDefinitions(text) {\n  const jsonArrayPattern = /\\[\\s*\\{[\\s\\S]*?\\}\\s*\\]/g;\n  return text.replace(jsonArrayPattern, (match) => {\n    try {\n      const items = JSON.parse(match);\n      if (Array.isArray(items) && items[0]?.term) {\n        return items.map(item => `\u2022 ${item.term}: ${item.definition}`).join('\\n');\n      }\n    } catch (e) {}\n    return match;\n  });\n}\n\n// (summary) \ud0dc\uadf8 \uc81c\uac70\nfunction cleanSummary(text) {\n  return text.replace(/\\s*\\(summary\\)\\s*/g, '');\n}\n\n// \uc5f0\uc18d \uacf5\ubc31/\uc904\ubc14\uafc8 \uc815\ub9ac\nfunction cleanSpacing(text) {\n  return text\n    .replace(/[ \\t]{2,}/g, ' ')\n    .replace(/\\n{3,}/g, '\\n\\n')\n    .trim();\n}\n\nlet emailBody = rawText;\nemailBody = formatDefinitions(emailBody);\nemailBody = cleanSummary(emailBody);\nemailBody = cleanSpacing(emailBody);\n\nconst emailSubject = `\ud83d\udcda [${lectureName}] \ubcf5\uc2b5 \uc790\ub8cc\uac00 \uc900\ube44\ub410\uc5b4\uc694! (${today})`;\n\nreturn [{ json: { job_id: job.id, lectureName, fileName, date: today, emailSubject, emailBody } }];"
      },
      "id": "node-parse-email",
      "name": "09. \uacb0\uacfc \ud30c\uc2f1 \ubc0f \uc774\uba54\uc77c \ud3ec\ub9f7",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1680,
        304
      ]
    },
    {
      "parameters": {
        "fromEmail": "YOUR_EMAIL@gmail.com",
        "toEmail": "YOUR_EMAIL@gmail.com",
        "subject": "={{ $json.emailSubject }}",
        "emailFormat": "text",
        "text": "={{ $json.emailBody }}",
        "options": {}
      },
      "id": "node-email",
      "name": "10. \uc774\uba54\uc77c \ubc1c\uc1a1",
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [
        1920,
        304
      ],
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      }
    }
  ],
  "connections": {
    "01. \uac15\uc758 \ud3f4\ub354 \uac10\uc2dc": {
      "main": [
        [
          {
            "node": "02. \ud30c\uc77c \uc77d\uae30",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "02. \ud30c\uc77c \uc77d\uae30": {
      "main": [
        [
          {
            "node": "03. Upstage \ud30c\uc77c \uc5c5\ub85c\ub4dc",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "03. Upstage \ud30c\uc77c \uc5c5\ub85c\ub4dc": {
      "main": [
        [
          {
            "node": "04. Agent Job \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "04. Agent Job \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "05. \ucd08\uae30 \ub300\uae30 (15\ucd08)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "05. \ucd08\uae30 \ub300\uae30 (15\ucd08)": {
      "main": [
        [
          {
            "node": "06. Job \uc0c1\ud0dc \ud3f4\ub9c1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "06. Job \uc0c1\ud0dc \ud3f4\ub9c1": {
      "main": [
        [
          {
            "node": "07. \uc644\ub8cc \ud655\uc778",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "07. \uc644\ub8cc \ud655\uc778": {
      "main": [
        [
          {
            "node": "09. \uacb0\uacfc \ud30c\uc2f1 \ubc0f \uc774\uba54\uc77c \ud3ec\ub9f7",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "08. \uc7ac\uc2dc\ub3c4 \ub300\uae30 (3\ucd08)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "08. \uc7ac\uc2dc\ub3c4 \ub300\uae30 (3\ucd08)": {
      "main": [
        [
          {
            "node": "06. Job \uc0c1\ud0dc \ud3f4\ub9c1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "09. \uacb0\uacfc \ud30c\uc2f1 \ubc0f \uc774\uba54\uc77c \ud3ec\ub9f7": {
      "main": [
        [
          {
            "node": "10. \uc774\uba54\uc77c \ubc1c\uc1a1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  }
}