[2차 프로젝트][숙박플랫폼] 어드민 페이지 - 이미지 업로더 만들기fileReader API, base64인코딩, formData

GY·2022년 1월 16일
0

리액트

목록 보기
40/54
post-thumbnail

💎 이미지 전달하기

로컬파일에서 업로드한 파일의 정보를 백엔드로 보내주면 백엔드에서 데이터베이스의 이미지를 해당 이미지로 바꿔주어야 했다.
우리가 생각했던 방법은 크게 2가지였다.

  1. input type: file로 받은 파일 객체를 그대로 body에 전달해주는 방법
  2. base64로 이미지 URL을 인코딩해 보내는 방법

그리고 이 이미지를 전달해주는 방법에 대해서도 고민 포인트가 있었다.

  1. base64로 인코딩한 URL은 JSON으로 형변환해주어야 하는지? 혹은 그대로 전달해주면 되는지
  2. formData에 담아 보내주는 것이 좋을지

💎 이미지는 base64로 인코딩해 전달해야 할까?

이미지 전달 방법을 백엔드 팀원분과 함께 맞추려고 하다가, base64에 대해 알게 되었다.

🔹 base64란?

  • Binary데이터를 아스키 코드 일부와 일대일로 매칭되는 문자열로 단순 치환되는 인코딩 방식
  • Binary 데이터를 문자로 변환하는데 영향을 받지 않는 공통 ASCII 코드 영역의 문자로만 이루어진 문자열로 바꾸는 인코딩 방식이다.
    64진법을 사용해 Binary데이터를 6bit씩 나누고 해당하는 문자를 다음의 색인표에서 치환한다.

문자열이 3개씩 나누어떨어지지 않을 경우 padding이라는 개념으로, 남는 자리를 =기호를 사용해 채워주어 빈 자리를 명시한다.

인코딩이 그래서 정확히 뭘까?

인코딩

정보의 형태/형식을 여러가지 목적에 따라 다른 형태/형식으로 변환하는 처리

🔹 base64의 단점

  • 6bit당 2bit의 OverHead의 발생으로 기존보다 데이터의 길이가 30%이상 길어지게 되며 인코딩,디코딩의 로직도 추가해야하기 때문에 성능에 부정적인 영향을 줄 수 있다.

base64를 왜 사용할까?

Base64를 사용하면 Binary데이터를 텍스트 기반 규격으로 다룰 수 있다.
JSON과 같은 문자열 기반 데이터 안에 이미지 파일 등을 Base64로 인코딩하면 UTF-8과 호환 가능한 문자열을 얻을 수 있다.

기존 ASCII코드는 시스템간 데이터를 전달하기에 안전하지 않다. 모든 Binary 데이터가 ASCII코드에 포함되지 않으므로 제대로 읽을 수 없는 문자가 있을 수 있기 때문이다.
반면 Base64는 ASCII 중 제어문자와 일부 특수문자를 제외한 53개의 안전한 출력문자만 이용하기 때문에 데이터 전달에 더 적합하다.

  • 모든 가능한 바이트값(예를들면, 이미지)를 신뢰할 수 없는 통신 채널을 통해 바이너리 데이터를 안전하게 전송할 수 있도록 한다.

🔹 base64는 어떤 경우에 쓰일까?

  • 바이너리 데이터를 텍스트로 다루고 싶을 때 보편적으로 사용하는 방식이다.
    XML, JSON, REST API등 문자열 기반 데이터를 주고받는 환경에서 multi-form을 다룰 경우 함께 사용된다.
  • 과거에는 서로다른 응용 프로그램마다 각각 다른 요구 사항이 있었기 때문에 조금씩 다른 방식을 기본 인코딩이 구현되는 경우가 많았는데, ASCII유형의 데이터를 바로 전송받아 처리하는 것은 프로토콜, 시스템마다 다르게 해석되어 데이터가 왜곡될 여지가 있기 때문에 적합하지 않았다.
  • HTML의 Data URI를 사용해 HTML, CSS에 인라인으로 그림을 넣는다면, 파일형식이 기본적으로 텍스트여야 하기 때문에 Base64를 사용할 수 있다.
    별도로 이러한 이유가 아니라면 굳이 base64를 사용할 이유는 없어보였다.

🔹 base 64로 인코딩하는 방법


const reader = new FileReader();
const uploadedFile = e.target.files[0];
reader.readAsDataURL(uploadedFile);
reader.onload = function (e) {
  const encodedImg = e.target.result;


💎 formData에 담아 전송하기

🔹 formData는 왜 사용해야 할까

  • 최근에는 formData가 많이 쓰이는데, 그 이유는 비동기식으로 데이터나 파일을 모두 전송할 수 있기 때문이다. 또한 form 태그를 직접사용하지 않아도 key/value방식으로 입력폼에 값을 쉽게 추가하여 한번에 여러 값을 보내줄 수 있다.

🔹 기본 사용법

FormData.append()

FormData 객체안에 이미 키가 존재하면 그 키에 새로운 값을 추가하고, 키가 없으면 추가합니다.

FormData.delete()

FormData 객체로부터 키/밸류 쌍을 삭제합니다.

FormData.entries()

이 객체에 담긴 모든 키/밸류 쌍을 순회할 수 있는 iterator를 반환합니다.

FormData.get()

FormData 객체 내의 값들 중 주어진 키와 연관된 첫번째 값을 반환합니다.

FormData.getAll()

FormData 객체 내의 값들 중 주어진 키와 연관된 모든 값이 담긴 배열을 반환합니다.

FormData.has()

FormData 객체에 특정 키가 포함되어 있는지 여부를 나타내는 boolean 을 반환합니다.

FormData.keys()

이 객체에 담긴 모든 키/벨류 쌍들의 모든 키들을 순회 할 수 있는 iterator를 반환합니다.

FormData.set()

FormData 객체 내에 있는 기존 키에 새 값을 설정하거나, 존재하지 않을 경우 키/밸류 쌍을 추가합니다.

FormData.values()

이 객체에 포함된 모든 밸류를 통과하는 iterator를 반환합니다.


🔹 어떻게 formData를 전송할까

formData는 콘솔에 출력되지 않는다.
console.log(formData)를 했다가 빈 값이 출력되어 코드의 어떤 부분이 잘못되었는지 고민하느라 꽤 오랜 시간을 보냈을 때쯤에 알게된 것은, formData는 일반적인 콘솔출력문으로 내부 값을 출력할 수 없다는 것이다. 확인하려면 다음과 같은 방법으로 출력할 수 있다.

    for (var value of formData.values()) {
      console.log(value);
    }

formData를 body에 보낼때는 content type을 헤더에 명시하지 않는다.

🔹 formData는 body에 그대로 보내준다.

백엔드와 통신하면서 애먹었던 부분이다.
아무리 보내도 백엔드에는 요청은 성공했지만 body에 담긴 값은 도달하지않았다.

그 때의 코드는 이렇다.

async function submitPrice() {
    const formData = new FormData();
    formData.append('price', price);
    await fetch(
      `url`,
      {
        method: 'POST',
        headers: {},
        body: {
        	'price': formData,
        }, 
      }
    )
  }

다시 JSON.stringify를 사용해보기도 했다.

        body: JSON.stringify{
        	'price': formData,
        }, 

formData에 대해 제대로 알지 못해서 이런 고민을 했다.
객체에 담을 필요도, JSON변환할 필요도 없이 body에 그대로 담아주면된다. (이리 저리 찾아보면서 참고했던 다른 코드들은 body객체에 담아주었던데..그건 뭘까..?)
호텔의 가격정보를 변경하는 함수는 이렇게 작성했다.

  async function submitPrice() {
    const formData = new FormData();
    formData.append('price', price);
    await fetch(
      `url`,
      {
        method: 'POST',
        headers: {},
        body: formData, 
        //formData는 객체 내부에 담아 보내지 않고,
        //그대로 body에 작성한다. JSON.stringify를 사용할 필요도 없다.
      }
    )
  }


💎 아쉬운점 / 보완하고 싶은 점

formData에 대해 제대로 알지 못하고 무작정 써보았는데, formData의 장점을 제대로 살리지 못했다는 생각이 들었다.
formData의 장점 중 하나는 form태그없이도 append를 사용해 key:value형태로 여러 인풋값을 저장해 한번에 보낼 수 있다는 것인데, 구현한 어드민 페이지는 막상 각 호텔의 가격/이미지 정보를 변경할 때마다 요청을 보내도록되어있었기 때문이다.

  • 리액트의 useForm을 사용해 리팩토링을 해보려는 생각을 하고 있었는데, 이럴 경우 formData는 크게 필요가 없을 것 같다. 혹은 굳이 useForm을 사용할 필요가 없을 것 같다. 다만, formData와 useForm의 차이와 무엇을 쓰는 것이 더 좋을지에 대한 고민을 해볼 수 있을 것 같다.
  • 변경할 정보를 입력한 뒤 저장버튼을 눌렀을 때 한번에 변경된 정보를 보내는 방식으로 만들면 formData를 제대로 사용할 수 있을 것 같다. 다만, 백엔드와 이미 맞춰둔 통신 방식이 있기 때문에 이 방법은 백엔드가 추가적으로 수정을 해야해 지금 시점에서 좋은 방법은 아닌듯 하다.


Reference

profile
Why?에서 시작해 How를 찾는 과정을 좋아합니다. 그 고민과 성장의 과정을 꾸준히 기록하고자 합니다.

0개의 댓글