그림판(canvas API)으로 그린 그림 서버와 소통 post, get(Feat: encoding, decoding 과정)

Bitnara Lee·2021년 12월 24일
0

Canvas API를 이용하여 구현한 그림판으로 그림을 그린 후,
해당 그림을 서버로 post 요청했다.
이 후 mypage의 갤러리에서 저장한 그림들을 get 요청하고
서버에서 받은 데이터들을 buffer로 base64인코딩한 다음 렌더링 코드를 짰다.
but 해당 그림들이 제대로 표시되지 않았다.

console.log로 인코딩한 데이터들을 확인한 결과, 해당 문자열의 앞부분에 불필요한 문자열이 붙어서 렌더링되고 있었다.
해당 갤러리나 서버에서의 코드는 문제 없어 보였고,
그림판 그림을 post 할 때의 코드를 확인해야 할 것 같았다.
요청 로직을 구현할 때 구글링한 결과들을 참고하여 구현했지만, 이미지 데이터를 가공하여 서버에 보내는 과정을 100% 이해한 것은 아니었기에 이 기회에 다시 공부하며 정리해보았다.

깃헙 - 에러 핸들링 과정 정리

클라이언트에서 서버에 post 요청할 때

0. 순서

  • Canvas 이미지를 데이터로 저장(base64 인코딩)
  • 저장된 Canvas 이미지를 base64에서 디코딩
  • 디코딩된 값을 바이트 배열로 변환 후 저장
  • typed array인 8bit unsigned array로 변환
  • new blob() 생성자를 사용해 blob 값으로 변환
  • FormData() 생성자를 사용해 이미지 값을 서버의 데이터로 저장
  • ajax의 post 메소드를 사용하여 서버에 전송

1. Canvas 이미지를 데이터로 저장(base64 인코딩)

toDataURL 메소드를 이용하여 canvas의 이미지를 base64 문자열로 가져오고,
적합한 포맷을 설정해준다.
(base64 Encoding, 포맷은 image/png로 설정)

  • 인코딩

    • 정보의 형태나 형식을 표준화, 보안, 처리 속도 향상, 저장 공간 절약 등을 위해서 다른 형태나 형식으로 변환하는 처리 혹은 그 처리 방식을 말한다.
  • base64

    Base64를 글자 그대로 직역하면 64진법이라는 뜻.
    2의 제곱수에 기반한 진법 중 화면에 표시되는 ASCII 문자들로 표시할 수 있는 가장 큰 진법

    • 8비트 이진 데이터(예를 들어 실행 파일이나, ZIP 파일 등)를 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식
      전자 메일을 통한 이진 데이터 전송 등에 많이 쓰이고 있다.
      Base 64에는 어떤 문자와 기호를 쓰느냐에 따라 여러 변종이 있지만, 잘 알려진 것은 모두 처음 62개는 알파벳 A-Z, a-z와 0-9를 사용하고 있으며 마지막 두 개를 어떤 기호를 쓰느냐의 차이만 있다.
  • MIME 정의하는 전자 메일 base64
    • 임의의 바이트 스트림을 화면에 표시할 수 있는 ASCII 문자들로 바꾸는 인코딩 방식.
      이 인코딩은 인터넷 전자 메일을 전송할 때 MIME의 content transfer encoding의 하나로 정의된다.
    • 인코딩된 문자열은 알파벳 대소문자와 숫자, 그리고 "+", "/" 기호 64개로 이루어지며, "="는 끝을 알리는 코드로 쓰인다.

-> Base64 Encoding은 Binary Data를 문자열로 변경하는 Encoding이다.

HTML, CSS, JSON등의 다른 포맷 안에 이미지데이터를 첨부할때 주로 Base64 이미지가 사용된다. HTML 문서에 이미지 데이터를 포함함으로써 브라우저는 부가적인 파일 요청을 할 필요가 없다

ASCII는 시스템 간 데이터를 전달하기에 안전하지 않다.

Base64는 ASCII 중 제어 문자와 일부 특수문자를 제외한 64개의 안전한 출력 문자만 사용한다.
(* 안전한 출력 문자란 문자 코드에 영향을 받지 않는 공통 ASCII를 의미)

즉, Base64는 HTML 또는 Email과 같이 문자를 위한 Media에 Binary Data를 포함해야 될 필요가 있을 때, 포함된 Binary Data가 시스템 독립적으로 동일하게 전송 또는 저장되는 걸 보장하기 위해 사용한다.

개념 참고: 위키백과

MIME(영어: Multipurpose Internet Mail Extensions)
전자 우편을 위한 인터넷 표준 포맷이다. 전자우편은 7비트 ASCII 문자를 사용하여 전송되기 때문에, 8비트 이상의 코드를 사용하는 문자나 이진 파일들은 MIME 포맷으로 변환되어 SMTP로 전송된다. 실질적으로 SMTP로 전송되는 대부분의 전자 우편은 MIME 형식이다. MIME 표준에 정의된 content types은 HTTP와 같은 통신 프로토콜에서 사용되며, 점차 그 중요성이 커지고 있다.

, (쉼표) 뒤에서 시작하는 것이 캔버스의 base64 표현이므로 쉼표로 문자열을 분할해 인덱스 1내의 데이터만 가져온다.

   const canvas: HTMLCanvasElement = canvasRef.current;
   const image = canvas.toDataURL('image/png').split(',')[1];

서버에 전송할 수 있는 blob 타입으로 변환하기 위해 base64를 (atob()로) 다시 디코딩
-> 현재 atob은 사용불가, Buffer 추천

 const toBinaryIMG = Buffer.from(image, 'base64').toString('binary');
  • Buffer
    • 한 장소에서 다른 장소로 이동하는 동안 데이터를 임시로 저장하는 데 사용되는 물리적 메모리 저장소의 영역
    • 노드에서 각 버퍼는 V8 외부에 할당된 일부 원시 메모리에 해당한다.
    • 버퍼는 정수 배열처럼 작동하지만 크기를 조정할 수 없다.
    • Buffer 클래스는 전역이다. 이진 데이터를 직접 처리하고 다양한 방식으로 구성할 수 있다.

Buffer 사용 이유 : 바이너리 데이터들의 스트림을 직접 다룸

2. Buffer로 디코딩한 값을 하나하나 해독해서 유니코드 값 배열에 저장.

const array = [];
	for (let i = 0; i < toBinaryIMG.length; i += 1) {
		array.push(toBinaryIMG.charCodeAt(i));
	}
  • charCodeAt()
    • 주어진 인덱스에 대한 UTF-16 코드를 나타내는 0부터 65535 사이의 정수를 반환

3. typed array인 8bit unsigned array로 변환

ArrayBuffer

  • 자바스크립트에서 구현된 버퍼
    고정된 크기의 메모리 공간에 바이너리 데이터를 저장하는 객체

ArrayBufferView

  • ArrayBuffer에 저장된 바이너리 데이터에 접근하는 객체
    • TypedArray, DataView 2개가 제공된다.

TypedArray

  • ArrayBufferView의 한 종류
    배열 요소의 타입/크기를 개발자가 지정하여 생성할 수 있다.
    • Uint8Array, Uint16Array, Float32Array 등이 있다.

ArrayBuffer와 TypedArray의 관계

  • TypedArray는 ArrayBuffer에 저장된 바이너리 데이터를 이용해 만드는 배열이다.
const u8arr = new Uint8Array(array); //8비트의 typed array을 생성. 

인자로 들어온 배열과 동일한 요소의 TypedArray생성 (ArrayBuffer도 함께 생성된다.)
Uint8Array 배열의 각 요소는 1byte(8bit)

ArrayBuffer,TypedArray 잘 정리된 블로그

4. new blob() 생성자를 사용해 blob 값으로 변환

const file = new Blob([u8arr], { type: "image/png" }); 
  • Blob(Binary Large OBject)

    • 주로 이미지, 오디오, 영상 등의 데이터를 다룰 때 사용한다. (물론 꼭 미디어 관련해서만 사용하는 것이 아니라 html, plain text 등 바이너리로 표현 가능한 많은 데이터에서 쓸 수 있다.)

    • 대개 데이터의 크기(Byte) 및 MIME 타입을 알아내거나, 데이터를 송수신을 위한 작은 Blob 객체로 나누는 등의 작업에 사용

    • Blob 생성자는 입력받은 배열의 요소들을 모두 합쳐 바이너리 데이터 형태로 저장하는 Blob객체를 반환한다.

5. FormData() 생성자를 사용해 이미지 값을 서버의 데이터로 저장

 const formdata = new FormData(); // formData 생성
 formdata.append("picture", file); // formdata에 file data 추가
 formdata.append('title', title);
 formdata.append('emotion', emotion);

6. ajax의 post 메소드를 사용하여 서버에 전송

(참고)
https://pks2974.medium.com/base-64-%EA%B0%84%EB%8B%A8-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0-da50fdfc49d2
https://lienkooky.tistory.com/93
https://tk-one.github.io/2018/08/28/nodejs-buffer/ (Buffer에 관해)

profile
Creative Developer

0개의 댓글