자바스크립트로 파일 읽기

사용자의 로컬 기기에서 파일을 선택하고 상호작용하는 기능은 웹에서 가장 흔히 사용되는 기능 중 하나입니다. 이를 통해 사용자는 사진을 공유하거나 세금 서류를 제출할 때와 같이 파일을 선택하여 서버에 업로드할 수 있습니다. 또한 사이트에서 네트워크를 통해 데이터를 전송하지 않고도 데이터를 읽고 조작할 수 있습니다. 이 페이지에서는 JavaScript를 사용하여 파일과 상호작용하는 방법을 설명합니다.

최신 File System Access API

File System Access API는 사용자의 로컬 시스템에 있는 파일 및 디렉터리를 읽고 쓰는 방법을 제공합니다. Chrome 및 Edge와 같은 대부분의 Chromium 기반 브라우저에서 사용할 수 있습니다. 자세한 내용은 파일 시스템 액세스 API를 참고하세요.

File System Access API는 일부 브라우저와 호환되지 않으므로 새 API를 사용할 수 있는 곳에서는 새 API를 사용하고 사용할 수 없는 경우에는 기존 접근 방식으로 대체하는 도우미 라이브러리인 browser-fs-access를 사용하는 것이 좋습니다.

기존 방식으로 파일 작업하기

이 가이드에서는 기존 JavaScript 메서드를 사용하여 파일과 상호작용하는 방법을 보여줍니다.

파일 선택

파일을 선택하는 기본적인 방법에는 두 가지가 있습니다. HTML 입력 요소를 사용하거나 드래그 앤 드롭 영역을 사용하는 것입니다.

HTML 입력 요소

사용자가 파일을 선택하는 가장 쉬운 방법은 모든 주요 브라우저에서 지원되는 <input type="file"> 요소를 사용하는 것입니다. 클릭하면 사용자가 운영체제의 내장 파일 선택 UI를 사용하여 파일 또는 multiple 속성이 포함된 경우 여러 파일을 선택할 수 있습니다. 사용자가 파일 선택을 완료하면 요소의 change 이벤트가 실행됩니다. FileList 객체인 event.target.files에서 파일 목록에 액세스할 수 있습니다. FileList의 각 항목은 File 객체입니다.

<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
  const fileSelector = document.getElementById('file-selector');
  fileSelector.addEventListener('change', (event) => {
    const fileList = event.target.files;
    console.log(fileList);
  });
</script>

다음 예에서는 사용자가 운영체제의 내장 파일 선택 UI를 사용하여 여러 파일을 선택한 다음 선택한 각 파일을 콘솔에 기록할 수 있습니다.

사용자가 선택할 수 있는 파일 유형 제한

경우에 따라 사용자가 선택할 수 있는 파일 유형을 제한해야 할 수 있습니다. 예를 들어 이미지 편집 앱은 텍스트 파일이 아닌 이미지만 허용해야 합니다. 파일 유형 제한을 설정하려면 입력 요소에 accept 속성을 추가하여 허용되는 파일 유형을 지정합니다.

<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">

맞춤 드래그 앤 드롭

일부 브라우저에서는 <input type="file"> 요소가 드롭 타겟이기도 하므로 사용자가 파일을 앱으로 드래그 앤 드롭할 수 있습니다. 그러나 이 드롭 타겟은 작아서 사용하기 어려울 수 있습니다. 대신 <input type="file"> 요소를 사용하여 핵심 기능을 제공한 후 대규모 맞춤 드래그 앤 드롭 노출 영역을 제공할 수 있습니다.

배송 지역 선택

드롭 노출 영역은 애플리케이션 설계에 따라 다릅니다. 창의 일부만 드롭 노출 영역으로 설정할 수도 있지만 전체 창을 사용할 수도 있습니다.

이미지 압축 웹 앱인 Squoosh의 스크린샷
Squoosh를 사용하면 전체 창이 드롭 영역이 됩니다.

이미지 압축 앱 Squoosh를 사용하면 사용자가 이미지를 창의 아무 곳이나 드래그하고 이미지 선택을 클릭하여 <input type="file"> 요소를 호출할 수 있습니다. 드롭 영역으로 선택하는 항목이 무엇이든 사용자가 해당 노출 영역으로 파일을 드래그할 수 있음을 명확하게 알 수 있어야 합니다.

배치 영역 정의

요소를 드래그 앤 드롭 영역으로 사용 설정하려면 두 이벤트인 dragoverdrop의 리스너를 만듭니다. dragover 이벤트는 브라우저 UI를 업데이트하여 드래그 앤 드롭 작업으로 파일 사본이 생성되고 있음을 시각적으로 나타냅니다. drop 이벤트는 사용자가 노출 영역에 파일을 배치한 후에 실행됩니다. 입력 요소와 마찬가지로 FileList 객체인 event.dataTransfer.files에서 파일 목록에 액세스할 수 있습니다. FileList의 각 항목은 File 객체입니다.

const dropArea = document.getElementById('drop-area');

dropArea.addEventListener('dragover', (event) => {
  event.stopPropagation();
  event.preventDefault();
  // Style the drag-and-drop as a "copy file" operation.
  event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
  event.stopPropagation();
  event.preventDefault();
  const fileList = event.dataTransfer.files;
  console.log(fileList);
});

event.stopPropagation()event.preventDefault()는 브라우저의 기본 동작을 중지하고 대신 코드를 실행합니다. 이러한 속성이 없으면 브라우저가 페이지에서 벗어나 사용자가 브라우저 창에 드롭한 파일을 엽니다.

실시간 데모는 맞춤 드래그 앤 드롭을 참고하세요.

디렉터리는 어떨까요?

안타깝게도 JavaScript를 사용하여 디렉터리에 액세스하는 좋은 방법은 없습니다.

<input type="file"> 요소의 webkitdirectory 속성을 사용하면 사용자가 디렉터리를 선택할 수 있습니다. Android용 Firefox 및 iOS의 Safari를 제외한 대부분의 주요 브라우저에서 지원됩니다.

드래그 앤 드롭이 사용 설정된 경우 사용자가 디렉터리를 드롭 영역으로 드래그하려고 할 수 있습니다. 드롭 이벤트가 실행되면 디렉터리의 File 객체가 포함되지만 디렉터리의 파일에 대한 액세스 권한은 제공하지 않습니다.

파일 메타데이터 읽기

File 객체에는 파일에 관한 메타데이터가 포함됩니다. 대부분의 브라우저는 파일 이름, 파일 크기, MIME 유형을 제공하지만 플랫폼에 따라 브라우저마다 다른 정보 또는 추가 정보를 제공할 수 있습니다.

function getMetadataForFileList(fileList) {
  for (const file of fileList) {
    // Not supported in Safari for iOS.
    const name = file.name ? file.name : 'NOT SUPPORTED';
    // Not supported in Firefox for Android or Opera for Android.
    const type = file.type ? file.type : 'NOT SUPPORTED';
    // Unknown cross-browser support.
    const size = file.size ? file.size : 'NOT SUPPORTED';
    console.log({file, name, type, size});
  }
}

input-type-file 데모에서 이를 확인할 수 있습니다.

파일 콘텐츠 읽기

FileReader를 사용하여 File 객체의 콘텐츠를 메모리로 읽습니다. FileReader에 파일을 배열 버퍼, 데이터 URL 또는 텍스트로 읽도록 지시할 수 있습니다.

function readImage(file) {
  // Check if the file is an image.
  if (file.type && !file.type.startsWith('image/')) {
    console.log('File is not an image.', file.type, file);
    return;
  }

  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    img.src = event.target.result;
  });
  reader.readAsDataURL(file);
}

이 예에서는 사용자가 제공한 File를 읽은 다음 데이터 URL로 변환하고 이 데이터 URL을 사용하여 img 요소에 이미지를 표시합니다. 사용자가 이미지 파일을 선택했는지 확인하는 방법을 알아보려면 read-image-file 데모를 참고하세요.

파일 읽기 진행 상황 모니터링

대용량 파일을 읽을 때는 사용자에게 읽기가 얼마나 진행되었는지 알려주는 UX를 제공하는 것이 좋습니다. 이를 위해 FileReader에서 제공하는 progress 이벤트를 사용하세요. progress 이벤트에는 loaded (읽은 양) 및 total (읽을 양)의 두 가지 속성이 있습니다.

function readFile(file) {
  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    const result = event.target.result;
    // Do something with result
  });

  reader.addEventListener('progress', (event) => {
    if (event.loaded && event.total) {
      const percent = (event.loaded / event.total) * 100;
      console.log(`Progress: ${Math.round(percent)}`);
    }
  });
  reader.readAsDataURL(file);
}

Unsplash의 빈센트 보타님 제공 히어로 이미지