HTTP Multipart/form-data ์ง์ ‘ ํŒŒ์„œ ๋งŒ๋“ค๋ฉฐ ์›๋ฆฌ ์ดํ•ดํ•˜๊ธฐ

2025. 10. 1. 10:43ยท๐ŸŒ WEB
728x90

๐Ÿ“ฆ Multipart/form-data ์ง์ ‘ ํŒŒ์„œ ๋งŒ๋“ค๋ฉฐ ์›๋ฆฌ ์ดํ•ดํ•˜๊ธฐ

์›น ๊ฐœ๋ฐœ์„ ํ•˜๋‹ค ๋ณด๋ฉด ํŒŒ์ผ ์—…๋กœ๋“œ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ผ์ด ๋งŽ์Šต๋‹ˆ๋‹ค. ๋ณดํ†ต์€ Express์—์„œ multer ๊ฐ™์€ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์“ฐ๋ฉด ์†์‰ฝ๊ฒŒ ๋๋‚˜๋Š”๋ฐ์š”. ํ•˜์ง€๋งŒ ์ด๋ฒˆ์—๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ์ง€ ์•Š๊ณ , ์ง์ ‘ multipart/form-data๋ฅผ ํŒŒ์‹ฑํ•ด๋ณด๋ฉด์„œ ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•ด๊ณ ์ž ํ–ˆ์Šต๋‹ˆ๋‹ค.

 

1. ์™œ multipart/form-data์ธ๊ฐ€?

์ผ๋ฐ˜์ ์ธ HTML <form>์€ ๊ธฐ๋ณธ์ ์œผ๋กœ application/x-www-form-urlencoded ๋ฐฉ์‹์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ ํŒŒ์ผ ์—…๋กœ๋“œ์ฒ˜๋Ÿผ ๋Œ€์šฉ๋Ÿ‰ ์ด์ง„ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜๋ฉด ๋‹จ์ˆœ URL ์ธ์ฝ”๋”ฉ์œผ๋กœ๋Š” ์ฒ˜๋ฆฌ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค.
์ด๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ๋ฐ”๋กœ multipart/form-data์ž…๋‹ˆ๋‹ค.
์—ฌ๋Ÿฌ ๊ฐœ์˜ ํŒŒํŠธ๋ฅผ ํ•˜๋‚˜์˜ ์š”์ฒญ์— ๋‹ด๊ณ , ๊ฐ ํŒŒํŠธ๋ฅผ boundary(๊ฒฝ๊ณ„ ๋ฌธ์ž์—ด) ๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.

 

2. ์š”์ฒญ ์˜ˆ์‹œ ์‚ดํŽด๋ณด๊ธฐ

์˜ˆ๋ฅผ ๋“ค์–ด, ์‚ฌ์šฉ์ž๊ฐ€ username๊ณผ profile ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ–ˆ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค. ์š”์ฒญ์€ ๋Œ€๋žต ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ „์†ก๋ฉ๋‹ˆ๋‹ค.
 

const formData = new FormData();
formData.append("title", "์˜ค๋Š˜์˜ ์ ์‹ฌ");
formData.append("content", "๊น€๋ฐฅ์ฒœ๊ตญ์—์„œ ๋ผ๋ฉด ๋จน์—ˆ๋‹ค ๐Ÿœ");
formData.append("date", "2025-10-01");
formData.append("photo", document.querySelector("input[type=file]").files[0]);

fetch("/upload", {
  method: "POST",
  body: formData
});
POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryABC123
Content-Length: 51234

------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="title"

์˜ค๋Š˜์˜ ์ ์‹ฌ
------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="content"

๊น€๋ฐฅ์ฒœ๊ตญ์—์„œ ๋ผ๋ฉด ๋จน์—ˆ๋‹ค ๐Ÿœ
------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="date"

2025-10-01
------WebKitFormBoundaryABC123
Content-Disposition: form-data; name="photo"; filename="lunch.jpg"
Content-Type: image/jpeg

(์—ฌ๊ธฐ์— lunch.jpg ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ)
------WebKitFormBoundaryABC123--

 

  • boundary ๋Š” ๊ฐ ๋ฐ์ดํ„ฐ ํŒŒํŠธ๋ฅผ ๊ตฌ๋ถ„ํ•˜๋Š” ๋ฌธ์ž์—ด ์ด๊ณ 
  • ๊ฐ ํŒŒํŠธ ๋Š” ํ—ค๋” + ๊ณต๋ฐฑ์ค„ + ์‹ค์ œ ๋ฐ์ดํ„ฐ ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

3. ํŒŒ์‹ฑ ์ ˆ์ฐจ ์ •๋ฆฌ

  1. Content-Type ํ—ค๋”์—์„œ boundary ์ถ”์ถœ→ boundary = ----WebKitFormBoundaryX
  2. multipart/form-data; boundary=----WebKitFormBoundaryX
  3. body๋ฅผ boundary ๊ธฐ์ค€์œผ๋กœ split
  4. ๊ฐ ํŒŒํŠธ์—์„œ ํ—ค๋”/๋ณธ๋ฌธ ๋ถ„๋ฆฌ
    • \r\n\r\n ๊ธฐ์ค€์œผ๋กœ ํ—ค๋”์™€ ๋ฐ์ดํ„ฐ ๊ตฌ๋ถ„
  5. ํ—ค๋” ํ•ด์„
    • Content-Disposition์—์„œ name, filename ์ถ”์ถœ
    • Content-Type์ด ์žˆ์œผ๋ฉด ํŒŒ์ผ ๋ฐ์ดํ„ฐ
  6. ๋ฐ์ดํ„ฐ ์ €์žฅ
    • ์ผ๋ฐ˜ ํ•„๋“œ → ๋ฌธ์ž์—ด
    • ํŒŒ์ผ → filename, contentType, ๋‚ด์šฉ(Buffer)

 

4. ๊ฐ„๋‹จ ํŒŒ์„œ ๊ตฌํ˜„ (string split ๋ฒ„์ „)

์•„๋ž˜๋Š” ์ดํ•ด๋ฅผ ์œ„ํ•ด AI ์—๊ฒŒ ๋ถ€ํƒํ•œ ๊ฐ„๋‹จ ๋ฌธ์ž์—ด ๊ธฐ๋ฐ˜ ๊ฐ„๋‹จ ํŒŒ์„œ์ž…๋‹ˆ๋‹ค.  ( ์‹ค์ œ๋กœ๋Š” Buffer ๊ธฐ๋ฐ˜์œผ๋กœ ํ™•์žฅ์ด ํ•„์š” )
 
์ฒซ ํ—ค๋” ๊ธฐ์ค€์œผ๋กœ ๋ฐ”์šด๋”๋ฆฌ ๋ฌธ์ž์—ด์„ ํŒŒ์•…ํ•˜๊ณ ,
boundary ๊ธฐ์ค€์œผ๋กœ ๋ฐ”๋”” ํŒŒํŠธ๋ฅผ ์ชผ๊ฐœ๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

function getBoundary(contentType) {
  const match = contentType.match(/boundary=(.+)$/);
  return match && match[1];
}

function parseMultipart(body, contentType) {
  const boundary = getBoundary(contentType);
  const rawParts = body.split(`--${boundary}`);
  const result = {};

  for (const rawPart of rawParts) {
    if (rawPart.trim() === '' || rawPart === '--') continue;

    const [rawHeaders, rawBody] = rawPart.split('\r\n\r\n');
    if (!rawHeaders) continue;

    const nameMatch = rawHeaders.match(/name="([^"]+)"/);
    const filenameMatch = rawHeaders.match(/filename="([^"]+)"/);
    const contentTypeMatch = rawHeaders.match(/Content-Type: (.+)/);

    const name = nameMatch?.[1];
    const filename = filenameMatch?.[1];
    const contentType = contentTypeMatch?.[1];
    let value = rawBody.trim();

    if (filename) {
      result[name] = { filename, contentType, content: value };
    } else {
      result[name] = value;
    }
  }
  return result;
}

 

์‹คํ–‰ ์˜ˆ์‹œ

const body = `
------WebKitFormBoundaryX
Content-Disposition: form-data; name="username"

seoyeon
------WebKitFormBoundaryX
Content-Disposition: form-data; name="profile"; filename="me.png"
Content-Type: image/png

(binary-data-here)
------WebKitFormBoundaryX--
`;

const contentType = "multipart/form-data; boundary=----WebKitFormBoundaryX";
console.log(parseMultipart(body, contentType));

 
์ถœ๋ ฅ:

{
  username: "seoyeon",
  profile: {
    filename: "me.png",
    contentType: "image/png",
    content: "(binary-data-here)"
  }
}

 

'๐ŸŒ WEB' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

RAG ์—ฐ๋™ ์›น ์•ฑ  (0) 2026.01.23
์›น ์‚ฌ์ดํŠธ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•  (0) 2026.01.19
Node.js๋กœ ๊ณ ์„ฑ๋Šฅ ์›น์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ: Cluster์™€ Worker Threads  (0) 2025.09.28
HTTP ํŒจํ‚ท ๋ถ„์„ํ•˜๊ธฐ  (0) 2025.09.18
HTTP ํŒจํ‚ท ๊ตฌ์กฐ, ์š”์ฒญ ํ—ค๋”/๋ฐ”๋””  (0) 2025.09.17
'๐ŸŒ WEB' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • RAG ์—ฐ๋™ ์›น ์•ฑ
  • ์›น ์‚ฌ์ดํŠธ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•
  • Node.js๋กœ ๊ณ ์„ฑ๋Šฅ ์›น์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ: Cluster์™€ Worker Threads
  • HTTP ํŒจํ‚ท ๋ถ„์„ํ•˜๊ธฐ
์—ฐ์žŽ(lotus leaf)
์—ฐ์žŽ(lotus leaf)
  • ์—ฐ์žŽ(lotus leaf)
    lotus' s develog ๐Ÿƒ
    ์—ฐ์žŽ(lotus leaf)
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (79)
      • โœ๏ธ ๊ฐœ๋ฐœํšŒ๊ณ ๋ก (5)
      • ๐Ÿงฎ ์•Œ๊ณ ๋ฆฌ์ฆ˜ (3)
      • ๐Ÿ’™ ํ”„๋ก ํŠธ์—”๋“œ(FE) (19)
        • HTML (0)
        • CSS (0)
        • Javascript (0)
        • React (0)
        • Next.js (0)
        • webpack & babel (0)
      • ๐Ÿ’ป ๋ฐฑ์—”๋“œ(BE) (2)
        • Nest.js (0)
        • Express.js (0)
        • MySQL (1)
      • โš™๏ธ ์ธํ”„๋ผ(Devops) (2)
      • ๐Ÿค– AI (1)
      • ๐ŸŒ WEB (8)
      • ๐Ÿ’ป CS (16)
        • ์ž๋ฃŒ๊ตฌ์กฐ (0)
        • ์ปดํ“จํ„ฐ ๋„คํŠธ์›Œํฌ (1)
        • ์šด์˜์ฒด์ œ (0)
        • ์ธ๊ณต์ง€๋Šฅ (8)
        • ์›น ๋ณด์•ˆ (1)
        • ํด๋ผ์šฐ๋“œ ์ปดํ“จํŒ… (6)
      • ๐Ÿ–‹๏ธ DevLog (1)
      • ๐Ÿฆพ ๋กœ๋ณดํ‹ฑ์Šค (4)
      • ๐Ÿ“— ๋„ค์ด๋ฒ„๋ถ€์ŠคํŠธ์บ ํ”„ ์›น ๋ชจ๋ฐ”์ผ (0)
      • ๐ŸŽฎ Unity(C#) (10)
      • ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด (4)
        • C (4)
        • C++ (0)
        • Java (0)
        • Python (0)
      • MSA (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    turtlebot
    deploy-aws
    C#
    isaac automator
    AWS
    client-streaming
    ๊ธฐ์ดˆ์•Œ๊ณ ๋ฆฌ์ฆ˜
    gaussian rbf svm
    hard margin svm
    soft margin svm
    ์ˆœ์—ด
    ์ŠคํŒธ๋ถ„๋ฅ˜๊ธฐ
    ์ŠคํŒธ๋ฉ”์ผ๋ถ„๋ฅ˜๊ธฐ
    C
    Devops #๋Œ€๊ทœ๋ชจํŠธ๋ž˜ํ”ฝ์ฒ˜๋ฆฌ
    ๋ฐฑ์ค€
    nav2
    ros bridge
    c++
    auto-scaling
    ๋ฆฌ์•กํŠธ
    next.js12
    ์กฐํ•ฉ
    advaned detail
    c์–ธ์–ด
    ์ดํ™”์—ฌ์ž๋Œ€ํ•™๊ต #๋„์ „ํ•™๊ธฐ์ œ
    ์•Œ๊ณ ๋ฆฌ์ฆ˜
    ํŒŒ์ผํŠธ๋ฆฌ
    ros workspace
    ์ฝ”๋”ํŒจ๋“œ
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.6
์—ฐ์žŽ(lotus leaf)
HTTP Multipart/form-data ์ง์ ‘ ํŒŒ์„œ ๋งŒ๋“ค๋ฉฐ ์›๋ฆฌ ์ดํ•ดํ•˜๊ธฐ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”