다중 파일의 드랍에 대한 안전한 처리

웹 API가 계속 발전하고 그에 맞춰 Javascript도 개선되는 과정에서 괴리가 발생하는데요. 그중 한가지 사례로 다중 파일에 대한 드랍처리입니다.

아래의 드랍 처리는 2개 이상의 파일을 드랍했을때 처리되어야할 파일이 누락됩니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dropArea.addEventListener('drop', async (e) => {
e.preventDefault();
dropArea.classList.remove('highlight');
fileListElement.innerHTML = '';
const items = e.dataTransfer.items;
if (items) {
for (let i = 0; i < items.length; i++) {
try {
const handle = await items[i].getAsFileSystemHandle();
if (handle.kind === 'file') {
const file = await handle.getFile();
displayFile(file);
} else if (handle.kind === 'directory') {
await traverseFileSystemHandle(handle);
}
} catch (error) {
console.error(error);
}
}
}
});
dropArea.addEventListener('drop', async (e) => { e.preventDefault(); dropArea.classList.remove('highlight'); fileListElement.innerHTML = ''; const items = e.dataTransfer.items; if (items) { for (let i = 0; i < items.length; i++) { try { const handle = await items[i].getAsFileSystemHandle(); if (handle.kind === 'file') { const file = await handle.getFile(); displayFile(file); } else if (handle.kind === 'directory') { await traverseFileSystemHandle(handle); } } catch (error) { console.error(error); } } } });
dropArea.addEventListener('drop', async (e) => {
  e.preventDefault();
  dropArea.classList.remove('highlight');
  fileListElement.innerHTML = '';

  const items = e.dataTransfer.items;
  if (items) {
    for (let i = 0; i < items.length; i++) {
      try {
        const handle = await items[i].getAsFileSystemHandle();

        if (handle.kind === 'file') {
          const file = await handle.getFile();
          displayFile(file);
        } else if (handle.kind === 'directory') {
          await traverseFileSystemHandle(handle);
        }
      } catch (error) {
        console.error(error);
      }
    }
  }
});

위 코드의 문제점에 대한 원인은 비동기 처리를 위해 js에서는 for await .. of 문이 도입되었으나 아직 drop 이벤트 객체의 dataTransfer.items 컬렉션은 이를 지원하지 않기 때문입니다. 차선책으로 이를 개선한 코드가 다음과 같습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
dropArea.addEventListener('drop', async (e) => {
e.preventDefault();
dropArea.classList.remove('highlight');
fileListElement.innerHTML = '';
const items = e.dataTransfer.items;
if (items) {
const processingPromises = [];
for (let i = 0; i < items.length; i++) {
processingPromises.push((async () => {
if (items[i].getAsFileSystemHandle) {
try {
const handle = await items[i].getAsFileSystemHandle();
if (handle.kind === 'file') {
const file = await handle.getFile();
displayFile(file);
} else if (handle.kind === 'directory') {
await traverseFileSystemHandle(handle);
}
} catch (error) {
console.warn(error);
}
}
})()); // 즉시 실행
}
await Promise.allSettled(processingPromises);
}
});
dropArea.addEventListener('drop', async (e) => { e.preventDefault(); dropArea.classList.remove('highlight'); fileListElement.innerHTML = ''; const items = e.dataTransfer.items; if (items) { const processingPromises = []; for (let i = 0; i < items.length; i++) { processingPromises.push((async () => { if (items[i].getAsFileSystemHandle) { try { const handle = await items[i].getAsFileSystemHandle(); if (handle.kind === 'file') { const file = await handle.getFile(); displayFile(file); } else if (handle.kind === 'directory') { await traverseFileSystemHandle(handle); } } catch (error) { console.warn(error); } } })()); // 즉시 실행 } await Promise.allSettled(processingPromises); } });
dropArea.addEventListener('drop', async (e) => {
  e.preventDefault();
  dropArea.classList.remove('highlight');

  fileListElement.innerHTML = '';

  const items = e.dataTransfer.items;
  if (items) {
    const processingPromises = [];

    for (let i = 0; i < items.length; i++) {
      processingPromises.push((async () => {
        if (items[i].getAsFileSystemHandle) {
          try {
            const handle = await items[i].getAsFileSystemHandle();
            if (handle.kind === 'file') {
              const file = await handle.getFile();
              displayFile(file);
            } else if (handle.kind === 'directory') {
              await traverseFileSystemHandle(handle);
            }
          } catch (error) {
            console.warn(error);
          }
        }
      })()); // 즉시 실행
    }
    await Promise.allSettled(processingPromises);
  }
});