JS | 파일 업로드, 이벤트 등록 과정

Autumn·2021년 2월 7일
1

JavaScript

목록 보기
14/18
post-thumbnail
post-custom-banner

HTML input 태그 만들기

MDN 바로가기

input 태그의 속성 중 type="file"을 이용하면 파일을 업로드 할 수 있다. (참고: type에는 file 말고도 체크박스, 이메일, 이미지, 비밀번호 등등등 많은 유형이 있다.)


JS로 이벤트 등록하기

File 객체를 어떻게 얻나요?

  1. <input> 태그를 이용해 유저가 파일을 선택하면 FileList 객체가 반환된다.

  2. drag & drop 에서 DataTransfer 객체가 반환된다.

  3. HTMLCanvasElementmozGetAsFile() API에서도 얻을 수도 있다. MDN

    • <canvas>가 포함하고 있는 이미지를 나타내는 File 객체를 반환합니다. 이 파일은 메모리 기반의 파일이며, name의 이름을 갖습니다. 만약 type이 지정되지 않는다면, 이미지는 기본적으로 image/png의 타입을 갖게 될 것입니다.

input 태그를 이용해 파일 업로드

input 태그의 type을 file로 주면 위와 같이 버튼이 생기고, 파일을 선택할 수 있다. JS 코드를 쓰지 않아도 업로드는 되지만, 이렇게만 두면 파일을 업로드하는 의미가 없다. 😅 파일 업로드 후에 다양한 일을 할 수 있도록 이벤트리스너를 달아서 핸들링해야 한다. 파일에 관한 이벤트 핸들러가 FileReader 라는 클래스에 있기 때문에, 우리는 FileReader의 인스턴스를 만들어서 이벤트를 핸들링 해야 한다.

파일 업로드 이벤트 등록하기

FileReader MDN 바로가기

const fileEvent = (e) => {
  const reader = new FileReader();
  reader.onload = () => {
    console.log('파일 업로드 완료.');
  };
  reader.readAsText(e.target.files[0]);
}

$fileItem.addEventListener('change', fileEvent);
// $fileItem : querySelector로 불러온 input 버튼
  1. FileReader의 인스턴스 reader를 만든다.

  2. load 이벤트 핸들러인 reader.onload를 정의해준다. load 이벤트읽기 동작이 성공적으로 완료 되었을 때마다 발생한다.

  3. load 이벤트를 발생시키기 위해서는 '읽기 동작'이 필요하므로 readAsText를 실행한다. 업로드 된 파일은 e.target.files에 저장이 되는데, 파일을 하나만 업로드했기 때문에 첫 번째 요소를 넣은 것이다.



참고 - FileReader.readyState

MDN 바로가기

FileReader의 상태는 세 가지가 있다.

  1. EMPTY : FileReader가 생성됐지만 아직 readAs 메서드가 불려지지 않은 상태

  2. LOADING : readAs 메서드가 실행된 상태. File 또는 Blob이 읽어지고 있고, 에러가 아직 발생하지 않은 상태

  3. DONE : read operation이 완료된 상태. 그 말인 즉슨, 다음 세 가지 상황 중 하나를 뜻한다.

    3-1. File이나 Blob이 모두 읽어져서 메모리에 들어갔을 때

    3-2. 발생한 에러를 읽었을 때

    3-3. abort()가 호출되어 읽기 작업이 취소됐을 때


다시 본문으로 돌아와서 코드를 보자.

const fileEvent = (e) => {
  const reader = new FileReader();
  reader.onload = () => { // 1)
    console.log('파일 업로드 완료.');
  };
  reader.readAsText(e.target.files[0]); // 2)
}

$fileItem.addEventListener('change', fileEvent);
// $fileItem : querySelector로 불러온 input 버튼

위 코드에서 1)과 2)의 순서를 바꿔도, 즉 readAsTextonload 위로 올려도 정상적으로 동작한다. 오잉? 이벤트 핸들러가 등록되지 않았는데 어떻게 가능한 일이지? 일단 순서를 바꾼 코드는 아래와 같다.

const fileEvent = (e) => {
  const reader = new FileReader();
  reader.readAsText(e.target.files[0]);
  reader.onload = () => {
    console.log('파일 업로드 완료.');
  };
}

$fileItem.addEventListener('change', fileEvent);
// $fileItem : querySelector로 불러온 input 버튼

readAsText가 실행되면서 상태가 LOADING으로 바뀌었고, 바로 다음 줄의 onload 코드가 실행되면서 이벤트 핸들러가 등록이 되었고, 그 후에 상태가 DONE으로 바뀌어 콘솔 창에 '파일 업로드 완료' 라고 찍히는 것으로 보인다.

위 코드에서 onloadsetTimeout으로 지연을 시키니, 콘솔 창에 메시지가 나오지 않았다.

const fileEvent = (e) => {
  const reader = new FileReader();
  console.dir(e);
  reader.readAsText(e.target.files[0]);
  window.setTimeout(() => {
    reader.onload = () => {
      console.log('파일 업로드 완료.');
    };
  }, 2000);
}

$fileItem.addEventListener('change', fileEvent);

reader의 상태가 DONE으로 바뀌는 그 순간에 등록되어 있는 onload가 없기 때문이다.

즉, readAsText비동기적으로 실행되는 메서드이고, 다른 코드의 실행을 방해하지 않는 non-block이기 때문에 코드 순서를 바꿔도 동작하는 것이었다. 하지만 setTimeout을 넣었을 때처럼 의도치 않은 비동기 함수가 있을 수 있기 때문에, 코드의 순서가 굉장히 중요해 보인다.

profile
한 발짝씩 나아가는 중 〰 🍁 / 자잘한 기록은 아래 🏠 아이콘에 연결된 노션 페이지에 남기고 있어요 😎
post-custom-banner

2개의 댓글

comment-user-thumbnail
2021년 2월 9일

어텀도 엄청 자세하게 공부하시네요... 좀 보고, 잘 보고 갈게용 ㅎㅎㅎ

1개의 답글