Frontend/javaScript

์ฒจ๋ถ€ํŒŒ์ผ ๊ด€๋ฆฌ (DataTransfer)

dddzr 2025. 11. 8. 18:35

๐Ÿ“Œ 1.type="file" 

HTML์—์„œ <input type="file"> ์š”์†Œ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋กœ์ปฌ ์ปดํ“จํ„ฐ์—์„œ ํŒŒ์ผ์„ ์„ ํƒํ•ด ์„œ๋ฒ„๋กœ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋Š” ์ž…๋ ฅ ํ•„๋“œ๋‹ค.

file ์ž…๋ ฅ ํ•„๋“œ์— ๋“ค์–ด๊ฐ€๋Š” ๋ฐ์ดํ„ฐ๋Š” FileList ๊ฐ์ฒด๋กœ input[type="file"].files๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด FileList ๊ฐ์ฒด๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.

 

 

โœ… ๋™์ž‘ ์›๋ฆฌ

1๏ธโƒฃ html์˜ ๊ธฐ๋ณธ ์š”์†Œ ์ค‘ ํ•˜๋‚˜์ธ file ์š”์†Œ๋Š” ํด๋ฆญ ์‹œ ๋กœ์ปฌ ํŒŒ์ผ ํƒ์ƒ‰๊ธฐ ์ฐฝ์„ ์—ด๊ณ ,

2๏ธโƒฃ ํŒŒ์ผ์„ ์„ ํƒํ•˜๋ฉด, <input> ์š”์†Œ์˜ ๊ฐ’์ด ํŒŒ์ผ ๊ฐ์ฒด๋กœ ์ฑ„์›Œ์ง„๋‹ค.
3๏ธโƒฃ ํผ ์ œ์ถœ ์‹œ, ํŒŒ์ผ์ด multipart/form-data ํ˜•์‹์œผ๋กœ ์„œ๋ฒ„์— ์ „์†ก๋œ๋‹ค.

<input type="file" id="fileInput" multiple>

 

โš ๏ธ ์ฃผ์˜:
<form> ํƒœ๊ทธ์˜ enctype์ด ๋ฐ˜๋“œ์‹œ multipart/form-data๋กœ ์ง€์ •๋˜์–ด์•ผ ํŒŒ์ผ์ด ์ „์†ก๋œ๋‹ค.

<form enctype="multipart/form-data">

 

๐Ÿ“Œ 2. DataTransfer์„ ์ด์šฉํ•œ ์ปค์Šคํ…€

์ปค์Šคํ…€ํ•˜๋ ค๋ฉด DataTransfer ๊ฐ์ฒด๋ฅผ ํ™œ์šฉํ•ด ํŒŒ์ผ ๋ชฉ๋ก์„ ๊ด€๋ฆฌํ•˜๊ณ , file ํผ์€ ํŒŒ์ผ ์„ ํƒ์ฐฝ๋งŒ ์—ด ๋•Œ๋งŒ ์‚ฌ์šฉํ•˜๋ฉด๋œ๋‹ค.

 

๐Ÿ‘‰ ์ปค์Šคํ…€ ๊ธฐ๋Šฅ ์˜ˆ์‹œ

๐Ÿ”น ์ปค์Šคํ…€ UI: ์„ ํƒํ•œ ํŒŒ์ผ์„ ๋ฆฌ์ŠคํŠธ๋กœ ๋ณด์—ฌ์ฃผ๊ณ , ์‚ญ์ œ ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ.
๐Ÿ”น ์„œ๋ฒ„ ์—ฐ๋™: ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•œ ํ›„, ์„œ๋ฒ„์—์„œ ์‚ญ์ œ/์ˆ˜์ • API๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Œ.
๐Ÿ”น ํŒŒ์ผ ์„ ํƒ์ฐฝ ์‚ฌ์šฉ: file ์ž…๋ ฅ ํ•„๋“œ๋Š” ๋‹จ์ˆœํžˆ ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜๋Š” ์—ญํ• ๋กœ๋งŒ ์‚ฌ์šฉ.

 

๐Ÿ“– ์˜ˆ์ œ

const dt = new DataTransfer();
dt.items.add(new File(["content"], "example.txt")); // ํŒŒ์ผ ์ถ”๊ฐ€
document.querySelector("input[type=file]").files = dt.files; // ํŒŒ์ผ ํ•„๋“œ์— ์ ์šฉ

โœ” UI์—์„œ ํŒŒ์ผ ์‚ญ์ œ ์‹œ DataTransfer.items.remove()๋ฅผ ํ™œ์šฉ!

 

๐Ÿ“– ์˜ˆ์ œ (์ปค์Šคํ…€ UI + DataTransfer ํ™œ์šฉ)

<input type="file" id="fileInput" multiple hidden>
<button onclick="document.getElementById('fileInput').click()">ํŒŒ์ผ ์„ ํƒ</button>
<ul id="fileList"></ul>

const fileInput = document.getElementById("fileInput");
const fileList = document.getElementById("fileList");
let dt = new DataTransfer();

fileInput.addEventListener("change", (e) => {
  for (const file of e.target.files) {
    dt.items.add(file);
    const li = document.createElement("li");
    li.textContent = file.name;
    const btn = document.createElement("button");
    btn.textContent = "์‚ญ์ œ";
    btn.onclick = () => {
      dt.items.remove([...dt.files].indexOf(file));
      fileInput.files = dt.files;
      li.remove();
    };
    li.appendChild(btn);
    fileList.appendChild(li);
  }
  fileInput.files = dt.files;
});

 

๐Ÿ“Œ 3. ํŒŒ์ผ ์ˆ˜์ •, ์‚ญ์ œ ๊ธฐ๋Šฅ

๋ณดํ†ต ์ฒจ๋ถ€ํŒŒ์ผ ๊ธฐ๋Šฅ์—์„œ ๊ธฐ์กด ํŒŒ์ผ๋ฆฌ์ŠคํŠธ๋ฅผ ๊ฐ€์งœ ๊ฐ์ฒด ํ˜•ํƒœ (์ด๋ฆ„, ์šฉ๋Ÿ‰๋งŒ ํ‘œ์‹œ)๋กœ ๋“ค๊ณ ์™€์„œ ui์— ๋ณด์—ฌ์ค€๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์ด์— ๋Œ€ํ•œ ์ˆ˜์ •, ์‚ญ์ œ ๊ธฐ๋Šฅ๋„ ์ œ๊ณตํ•˜๊ธฐ์œ„ํ•ด์„œ ํ”„๋ก ํŠธ์—์„œ ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋”ฐ๋กœ ๊ด€๋ฆฌํ•ด์•ผํ•œ๋‹ค. (๋ฐฑ์—”๋“œ์—์„œ ์ฒ˜๋ฆฌํ•  ์ˆ˜๋„ ์žˆ๊ธดํ•˜๋‹ค.)

 

๐Ÿš€ ํŒŒ์ผ ์ˆ˜์ • ๊ธฐ๋Šฅ์—์„œ ํ•„์š”ํ•œ ๋ชฉ๋ก 3๊ฐ€์ง€
1๏ธโƒฃ newFileDT (์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ)DataTransfer ๊ฐ์ฒด๋กœ ๊ด€๋ฆฌ
2๏ธโƒฃ deleteList (์‚ญ์ œํ•  ๊ธฐ์กด ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ) → ๋ฐฐ์—ด([])๋กœ ๊ด€๋ฆฌ
3๏ธโƒฃ keepList (์œ ์ง€ํ•  ๊ธฐ์กด ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ) → ๊ธฐ์กด ํŒŒ์ผ์—์„œ deleteList๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€

 

๐Ÿ“Œ ์ „์ฒด ํ๋ฆ„

  • ๊ธฐ์กด ํŒŒ์ผ(existingFiles)์„ UI์— ํ‘œ์‹œ.
  • ์‚ฌ์šฉ์ž๊ฐ€ ์ƒˆ ํŒŒ์ผ์„ ์ถ”๊ฐ€ํ•˜๋ฉด newFileDT์— ์ €์žฅ.
  • ๊ธฐ์กด ํŒŒ์ผ์„ ์‚ญ์ œํ•˜๋ฉด deleteList์— ์ถ”๊ฐ€.
  • ์ „์†ก ์‹œ keepList๋Š” ๊ธฐ์กด ํŒŒ์ผ์—์„œ deleteList๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€.

 

๐Ÿ“– HTML ๊ตฌ์กฐ

<input type="file" id="fileInput" multiple hidden>
<button onclick="document.getElementById('fileInput').click()">ํŒŒ์ผ ์„ ํƒ</button>
<ul id="fileList"></ul>
<button onclick="submitFiles()">์—…๋กœ๋“œ</button>

 

๐Ÿ“– JavaScript ์ฝ”๋“œ

const fileInput = document.getElementById("fileInput");
const fileList = document.getElementById("fileList");

// ๊ธฐ์กด ํŒŒ์ผ ๋ชฉ๋ก (DB์—์„œ ๋ถˆ๋Ÿฌ์˜จ ํŒŒ์ผ)
let existingFiles = ["old1.pdf", "old2.jpg"];
let deleteList = [];  // ์‚ญ์ œํ•  ํŒŒ์ผ ๋ชฉ๋ก
let newFileDT = new DataTransfer(); // ์ƒˆ๋กœ ์ถ”๊ฐ€ํ•œ ํŒŒ์ผ ๋ชฉ๋ก
let keepList = [...existingFiles]; // ์œ ์ง€ํ•  ๊ธฐ์กด ํŒŒ์ผ ๋ชฉ๋ก (์ดˆ๊ธฐ๊ฐ’ = ๊ธฐ์กด ํŒŒ์ผ)

// ๊ธฐ์กด ํŒŒ์ผ UI ํ‘œ์‹œ
existingFiles.forEach(fileName => addFileToUI(fileName, false));

// ํŒŒ์ผ ์ถ”๊ฐ€ ์ด๋ฒคํŠธ
fileInput.addEventListener("change", (e) => {
  for (const file of e.target.files) {
    newFileDT.items.add(file);
    addFileToUI(file.name, true);
  }
  fileInput.files = newFileDT.files;
});

// ํŒŒ์ผ์„ UI์— ์ถ”๊ฐ€ํ•˜๋Š” ํ•จ์ˆ˜
function addFileToUI(fileName, isNew) {
  const li = document.createElement("li");
  li.textContent = fileName;
  const btn = document.createElement("button");
  btn.textContent = "์‚ญ์ œ";

  btn.onclick = () => {
    if (isNew) {
      // ์ƒˆ ํŒŒ์ผ ์‚ญ์ œ: DataTransfer์—์„œ ์ œ๊ฑฐ
      const index = [...newFileDT.files].findIndex(f => f.name === fileName);
      
      if (index > -1) {
        newFileDT.items.remove(index);
        fileInput.files = newFileDT.files;
      }
    } else {
      // ๊ธฐ์กด ํŒŒ์ผ ์‚ญ์ œ: ์‚ญ์ œ ๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€ํ•˜๊ณ  ์œ ์ง€ ๋ฆฌ์ŠคํŠธ์—์„œ ์ œ๊ฑฐ
      deleteList.push(fileName);
      keepList = keepList.filter(f => f !== fileName);
    }
    li.remove();
  };

  li.appendChild(btn);
  fileList.appendChild(li);
}

// ํŒŒ์ผ ์ „์†ก ํ•จ์ˆ˜
function submitFiles() {
  console.log("โœ… ์œ ์ง€ํ•  ๊ธฐ์กด ํŒŒ์ผ ๋ชฉ๋ก:", keepList);
  console.log("โŒ ์‚ญ์ œํ•  ํŒŒ์ผ ๋ชฉ๋ก:", deleteList);
  console.log("โž• ์ƒˆ๋กœ ์ถ”๊ฐ€ํ•œ ํŒŒ์ผ ๋ชฉ๋ก:", newFileDT.files);

  // ๐Ÿ”น ์„œ๋ฒ„๋กœ ์ „์†กํ•˜๋Š” ๋กœ์ง ์ถ”๊ฐ€ ๊ฐ€๋Šฅ (AJAX ๋˜๋Š” FormData ํ™œ์šฉ)

}

 

๐Ÿš€ ์ •๋ฆฌ

โœ” ์œ ์ง€ํ•  ๊ธฐ์กด ํŒŒ์ผ(keepList) = existingFiles - deleteList
โœ” ์‚ญ์ œํ•  ํŒŒ์ผ(deleteList) = ์‚ฌ์šฉ์ž๊ฐ€ ์‚ญ์ œํ•œ ๊ธฐ์กด ํŒŒ์ผ ๋ชฉ๋ก
โœ” ์ƒˆ๋กœ ์ถ”๊ฐ€๋œ ํŒŒ์ผ(newFileDT) = file ์ž…๋ ฅ ํ•„๋“œ์—์„œ ์ถ”๊ฐ€ํ•œ ํŒŒ์ผ ๋ชฉ๋ก
โœ” ์„œ๋ฒ„ ์ „์†ก ์‹œ 3๊ฐ€์ง€ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋ณ„๋„๋กœ ๊ด€๋ฆฌํ•˜๋ฉด ๊น”๋”ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ!