[Swap(), 그 기록] 2. 이미지 업로드 1편 - FileReader

Soye Park·2023년 1월 11일
0
post-thumbnail

이 포스팅은 사이드 프로젝트를 진행하며 생겼던 여러 일들에 대해서 기록을 겸하여 작성한 글입니다. 주기적으로 작성하고자 노력은 하지만 블로그 포스팅 한번 하려고 하면 머릿속에서 나만의 언어로 정리도 하면서 작성하다가 몇시간은 사용하는 지라 주기적이지 않을 가능성이 농후합니다. 아자아자...

이미지 업로드 및 미리보기 구현

들어가면서...

거래 플롯폼인 이상 필연적으로 필요한 요소가 있다고 하면 바로 제품의 사진이라고 생각한다. 이미지 업로드 기능 구현을 담당하게 되면서 나는 이미지를 업로드 하는 것에도 몇가지 방법이 있다는 것을 알게 되었고 정말 정직하게 그 중 꽤 많은 정보가 나왔던 FileReader를 활용한 업로드 방법으로 구현해보고자 하였다. 아.. 내가 왜 그랬지... 멍청했다 어젠가 엊그젠가 나..ㅠ

하지만 이 선택은 firebase를 활용하는 우리 프로젝트에 어울리지 않는 방법이었다... 단점이 많아 이제는 잘사용하지 않는 방법인 것을 구현하고 난 이후에 알게 되었기 때문에 이왕 한 거 리마인드도 할 겸 블로깅을 해보고자 한다.

FileReader..? 그게 뭔데? 난 첨 들음

FileReader 객체는 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 읽을 파일을 가리키는File 혹은 Blob 객체를 이용해 파일의 내용을(혹은 raw data버퍼로) 읽고 사용자의 컴퓨터에 저장하는 것을 가능하게 해줍니다. 출처 MDN

난 FileReader 라는 객체를 이번 이미지 업로드를 구현하면서 처음 봤다. 이미지 업로드 기능을 구현해본 적이 없기도 했지만, 공부를 하면서도 FileReader를 보지 못했다. 공부를 덜했네...

MDN의 힘을 빌려 알아본 바 File API가 가진 다양한 속성, 메소드, 이벤트를 가진 객체였는데, 이를 활용하면 <input type="file">으로부터 change 이벤트가 발생할 때 FileList 객체에 추가됐었던 파일들을 URL화 할 수 있는 메소드까지 내장되어 있었다. 물론 한장도 여러장도 가능했다. (input이 multiple 설정일 때)

먼저 <input type="file">으로부터 받아진 파일이 FileList에 추가되는 지 확인부터 했다.

const onLoadFile = event => {
 const files = event.target.files;
}

이게 딱 어울리는 거네!! 바로 작성 ㄱ!

파일이 FileList에 추가되는 것까지 확인하고 나서는 아 이걸로 사용하면 되겠다 싶어서 바로 조금 더 공부를 했다.

FileList는 index number로 이루어진 key : 업로드된 파일의 정보를 valuelength : 추가된 데이터의 갯수로 이루어진 객체였는데, 덕분에 마치 배열인 것 마냥 코드를 풀어나갈 수 있었다.

자 그럼 이제 뭘하지? 계획 좀 세워볼가---

파일의 정보가 담긴 배열(이라고 하고 객체라고 한다 대충 배열이라고 하겠다)이 생긴 걸 확인했으니 이 배열들을 만들어둔 미리보기 component와 함께 정상적으로 렌더링되도록 코드를 짜야했다.

파일의 정보가 담긴 배열이 있으므로 해야할 일은 명확했다.

  1. URL이 하나씩 담길 빈 배열이 필요했다.
  2. 미리보기를 몇개까지 렌더링 할 지 (몇개의 업로드를 허용할 지)에 대한 조건이 필요했다.
  3. 여러개의 데이터를 받을 것이므로 반복문을 만들어줘야한다.
  4. 이 배열에 담긴 정보들을 URL로 변환 시켜줄 필요가 있었다.
  5. 업로드된 파일 모두를 하나씩 URL로 변환시켜서 배열에 담고 그 배열을 useState hook을 사용해 상태값으로 저장한다.


본격적인 FileReader 활용 시작

const onLoadFile = event => {
  const fileArr = event.target.files;
  const fileUrlArr = [];
  const fileLength = fileArr.length > 5 ? 5 : fileArr.length;

  for (let i = 0; i < fileLength; i++) {
    const reader = new FileReader();
    const file = fileArr[i];
   
    reader.onload = () => {
      fileUrlArr[i] = reader.result;
      setFileUrl([...fileUrlArr]);
    };
    reader.readAsDataURL(file);
  }
};

일단 완성된 건 코드는 이건데...!

하나씩 뜯어보자..! 리마인드 리마인드 기계적 리마인드!

  1. URL이 하나씩 담길 빈 배열이 필요했다. 완료

    const fileUrlArr = []를 통해 빈배열을 하나 만든다

  2. 미리보기를 몇개까지 렌더링 할 지 (몇개의 업로드를 허용할 지)에 대한 조건이 필요했다. 완료

    const fileLength = fileArr.length > 5 ? 5 : fileArr.length
    비록 배열이 아닌 객체이지만 length라는 훌륭한 요소를 가지고 있다. fileArr의 length를 기준으로 해서 최대 업로드 가능 갯수를 정해줄 수 있다.

  3. 여러개의 데이터를 받을 것이므로 반복문을 만들어줘야한다. 완료

    내가 만약 프로필 파일과 같이 단 1개의 파일만을 업로드하면 된다면 딱히 반복문을 사용할 필요없이 fileArr[0]FileReader.readAsDataUrl에 넘겨주면 됐겠지만, 애석하게도 여러 파일들을 업로드 해야만 했다.

    그런 이유로 미리 할당해둔 fileLength값만큼 반복해 업로드된 모든 파일들을 순회할 수 있도록 for문을 사용해 반복한다.

  4. 이 배열에 담긴 정보들을 URL로 변환 시켜줄 필요가 있었다. 완료

    이제 드디어 메소드를 사용하기 위해 const reader = new FileReader()로 새로운 FileReader 객체를 생성해준다.

    그 후 파일의 정보를 담을 수 있게 const file = fileArr[i]를 작성하고, file에 담긴 정보를 URL화하기 위해 가장 기다리던 메소드, reader.readAsDataURL(file) 메소드를 활용해 데이터를 URL화 시킨다.

  5. 업로드된 파일 모두를 하나씩 URL로 변환시켜서 배열에 담고 그 배열을 useState hook을 사용해 상태값으로 저장한다. 완료

    어..? 그런데 잠깐, 웬걸? reader.readAsDataURL(file)를 console.log 해봐도 undefined만 반환된다. 대체 뭐가 잘못된 걸까..?

    🚨문제해결🚨

    사실 문제랄 것도 없었다. reader.readAsDataURL(file)가 data를 URL로 변환시켜주는 것은 맞는데, 읽기 동작이 완료된 후 Result값을 받아와야 얘가 정상적으로 데이터를 받을 수 있었다.

    그렇기 때문에 reader.onload 라는 이벤트를 실행시켜줘야했는데, onload 이벤트는 읽기 동작이 성공적으로 실행되었을 때 실행된다.

    그렇게 성공한 결과값을 fileUrlArr[i] 번째 값으로 넣고, useState Hook을 이용해 state에 저장해준다.

    state에서 map메서드를 통해 데이터를 받아 렌더링 해주면 끝!

    reader.onload = () => {
      fileUrlArr[i] = reader.result;
      setFileUrl([...fileUrlArr]);
    };

조잡한 결과물


그런데 이렇게 잘 실행되는데 왜 쓰면 안돼?

  1. 변환된 URL의 길이가 길어도 너어어어어어무 길다. 말도 수천수만자? 암튼 말도 안되게 길기 떄문에 백엔드단에서 다루는 것이 쉽지가 않다. 이것은 유지보수에도 악영향을 미친다.

  2. 위 구현된 코드는 정말 단순히 업로드된 파일의 정보를 URL로 변환해줄 뿐, 사진용량을 줄이거나 삭제기능을 제공하지는 않는다. 결국 이런 기능을 구현하려고 하면 개발자원이 든다는 얘기다.

  3. 요즘에는 여러가지 이유로 서버리스 서비스를 활용하는 경우가 많은데 AWS, Firebase 등에서 정말 간편한 코드를 통해 업로드 기능을 제공해준다. 또한 2번에서 지적된 문제점을 완벽하게 간편한 사용법으로 제공하고 있다.

  4. 레거시 코드 (같)다.


마치며...

뭐.. 제대로 알아보지 않고 기능을 구현한 내 잘못이지만 그래도 이전에 자주 사용되었던 코드들로 기능을 구현해보았다. 이는 꽤 귀중한 경험이 되지 않을까?

firebase로 이미지 업로드 기능을 구현해야하므로 또 이미지 포스팅이 올라올 것
그리고 alt 관련해서 고민했던 지점이 있는데 그것에 대해서도 한번 포스팅을 해볼까 한다.

오늘도 열심히 블로깅했다. 다음에도 열심히 블로깅 해보자.

그리고 오늘 블로깅은 좀 기계적인 설명이 많은 듯? 꺄꺌

profile
응애FE개발자/ 블로그 이전 : https://soyeah-log.vercel.app/

0개의 댓글