프로그래밍 언어/자바스크립트

자바스크립트로 Image Upload 및 Preview 구현하기

초보군붕이 2022. 1. 8. 14:11
반응형

중고나라 클론 프로젝트를 하면서 처음 해보는 이미지 업로드 기능에 부딪혔다.

 

이미지 업로드

 

여기저기 정보를 찾아보면서 공부한 내용을 정리해보려고 한다.

그리고 단순 input에 받는것이 아닌 다른 요소(div 등..)를 눌렀을 때 이미지 업로드와 동시에 preview 화면도 구현한다.

 

해당 포스트에 있는 코드는 이해를 돕기위해 코드를 모두 html 파일 내부에 작성했다.

실제 프로젝트에서는 html, css, js를 모두 나눠서 작업했다.

 

 

● HTML 에서 이미지를 업로드 받는방법

<input type="file" class="real-upload" accept="image/*" required multiple>

input type="text"

input type속성을 file로 정의하면 파일 업로드가 가능해진다.

또한 accept 속성을 img/*으로 지정할 경우 이미지 파일만 업로드가 가능해진다.

 

 

● 다른 요소(div 등..) 클릭시 input 이벤트가 발생하게 하는방법

내가 구현하고자 하는것은 div 를 눌렀을 때 input 의 파일선택창이 뜨는것이다.

이를 위해 input 은 display: none; 속성으로 숨기고 div에 대해서 input을 클릭하는 이벤트를 추가했다.

<body>
  <input type="file" class="real-upload" accept="image/*" required multiple style="display: none;">
  <div class="upload"></div>
  <script>
    const realUpload = document.querySelector('.real-upload');
    const upload = document.querySelector('.upload');

    upload.addEventListener('click', () => realUpload.click());
  </script>
</body>

파일 업로드 창이 뜨는 모습

위 사진처럼 input 태그의 "파일 선택" 버튼을 누르지 않아도 잘 출력된다.

 

 

 

● input 태그에서 업로드한 파일을 다루는 방법

input 에 파일이 업로드되면 change event 가 발생하게 된다.

DOM.addEventListener 를 통해서 change event 를 지정하고 getImageFiles 함수를 등록했다.

<head>
  <meta charset="UTF-8">
  <style>
    .real-upload {
      display: none;
    }

    .upload {
      width: 200px;
      height: 200px;
      background-color: antiquewhite;
    }
  </style>
</head>

<body>
  <input type="file" class="real-upload" accept="image/*" required multiple>
  <div class="upload"></div>
  <script>
    function getImageFiles(e) {
      const files = e.currentTarget.files;
      console.log(typeof files, files);
    }

    const realUpload = document.querySelector('.real-upload');
    const upload = document.querySelector('.upload');

    upload.addEventListener('click', () => realUpload.click());
    realUpload.addEventListener('change', getImageFiles);
  </script>
</body>

이미지 업로드 결과

object 형식의 FileList 가 생성되었으며 출력시 위 처럼 출력된다.

 

 

 

● input 태그에서 업로드된 파일의 유효성검사 방법

우선 업로드된 이미지 파일의 갯수를 체크하는 방법이다.

유사배열인 FileList 를 ES6 Spread 문법을 통해 Array로 변환하여 length 메서드로 길이를 체크하는 방식이다.

function getImageFiles(e) {
  const files = e.currentTarget.files;

  if ([...files].length >= 7) {
    alert('이미지는 최대 6개까지 업로드가 가능합니다.');
    return;
  }
}

이미지를 6개 초과하여 입력하면 나오는 메세지

 

 

다음은 업로드된 파일이 이미지 파일인지 체크하는 방법이다.

동일하게 유사배열인 FileList를 스프레드 문법을 통해 Array 로 변환해서 forEach 함수로 값을 체크한다.

function getImageFiles(e) {
  const files = e.currentTarget.files;
  [...files].forEach(file => {
    if (!file.type.match("image/.*")) {
      alert('이미지 파일만 업로드가 가능합니다.');
      return;
    }
  })
}

이미지 파일 외 다른 파일 업로드시 발생하는 메세지

!file.type.match("image/.*") 를 통해서 file 이 이미지인지 체크했다. 만약 이미지가 아닐경우 alert 를 띄우는 형식이다.

 

 

 

 

● 업로드된 이미지 파일 preview 표현하기

위에서 작성한 코드를 바탕으로 최종 코드를 완성했다. 로직은 아래와 같다

  1. div 를 눌렀을때 <input type="file"> 이 클릭되게 이벤트를 추가
  2. 업로드된 파일이 6개 이상인지 검사한다. 7개부터는 업로드가 불가능하다.
  3. 업로드된 파일이 모두 이미지 형식인지 검사한다.
  4. 이미지 파일을 li 태그내에 넣어서 ul 태그에 추가한다.
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    li {
      list-style: none;
    }

    img {
      width: 200px;
      height: 200px;
    }

    .real-upload {
      display: none;
    }

    .upload {
      width: 200px;
      height: 200px;
      background-color: antiquewhite;
    }

    .image-preview {
      width: 1300px;
      height: 200px;
      background-color: aquamarine;
      display: flex;
      gap: 20px;
    }
  </style>
</head>

<body>
  <input type="file" class="real-upload" accept="image/*" required multiple>
  <div class="upload"></div>
  <ul class="image-preview"></ul>
  <script>
    function getImageFiles(e) {
      const uploadFiles = [];
      const files = e.currentTarget.files;
      const imagePreview = document.querySelector('.image-preview');
      const docFrag = new DocumentFragment();

      if ([...files].length >= 7) {
        alert('이미지는 최대 6개 까지 업로드가 가능합니다.');
        return;
      }

      // 파일 타입 검사
      [...files].forEach(file => {
        if (!file.type.match("image/.*")) {
          alert('이미지 파일만 업로드가 가능합니다.');
          return
        }

        // 파일 갯수 검사
        if ([...files].length < 7) {
          uploadFiles.push(file);
          const reader = new FileReader();
          reader.onload = (e) => {
            const preview = createElement(e, file);
            imagePreview.appendChild(preview);
          };
          reader.readAsDataURL(file);
        }
      });
    }

    function createElement(e, file) {
      const li = document.createElement('li');
      const img = document.createElement('img');
      img.setAttribute('src', e.target.result);
      img.setAttribute('data-file', file.name);
      li.appendChild(img);

      return li;
    }

    const realUpload = document.querySelector('.real-upload');
    const upload = document.querySelector('.upload');

    upload.addEventListener('click', () => realUpload.click());

    realUpload.addEventListener('change', getImageFiles);
  </script>
</body>

 

위 코드를 기준으로 현재 진행중인 프로젝트에 적용했다. 이미지 업로드시 아래와 같이 표현된다.

이미지 업로드 완료사진

반응형