자바스크립트를 사용하여 캔버스 요소를 이미지로 변환하여 서버에 비동기식 ajax로 전송하는 방법을 알아봅니다.



# 자바스크립트 캔버스 이미지 서버로 파일 전달하기
HTML5를 사용하여 Canvas 요소를 이미지로 변환하거나 저장할 수 있습니다. 그렇다면 변환된 캔버스 이미지를 서버에 ajax로 전달할 수 있을까요?
캔버스 이미지를 서버에 ajax로 전달하기
결론부터 얘기하면 가능합니다. 물론 과정은 조금 복잡하며 아래와 같은 순서를 따라야만 합니다.

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

모든 과정들을 하나씩 나열해 보았습니다. 이제 위 과정들을 모두 수행 후 서버에서 파일을 전달 받아 처리하면 모든 과정은 끝나게 됩니다. 그럼 자바스크립트를 사용하여 실제 코드를 작성해 보겠습니다.


! Canvas 요소의 이미지 ajax로 서버 전송하기 예제 보기
이제 자바스크립트를 사용하여 실제 코드를 작성해야 합니다. 아래의 코드를 봐주세요. uploadCanvasToServer() 함수는 캔버스 이미지를 ajax 호출하기 까지의 모든 과정을 담고 있는 코드입니다. 일단 전체 코드를 확인하면 아래와 같습니다.

@ uploadCanvas.js
uploadCanvasToServer = function() {
  const canvas = document.getElementById('myCanvas');
  const imgBase64 = canvas.toDataURL('image/jpeg', 'image/octet-stream');
  const decodImg = atob(imgBase64.split(',')[1]);

  let array = [];
  for (let i = 0; i < decodImg .length; i++) {
    array.push(decodImg .charCodeAt(i));
  }

  const file = new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
  const fileName = 'canvas_img_' + new Date().getMilliseconds() + '.jpg';
  let formData = new FormData();
  formData.append('file', file, fileName);

  $.ajax({
    type: 'post',
    url: '/upload/',
    cache: false,
    data: formData,
    processData: false,
    contentType: false,
    success: function (data) {
      alert('Uploaded !!')
    }
  })
};

전체 코드를 알아봤습니다. 이제 코드를 주요 기능으로 나누어 알아보겠습니다. 코드는 ajax로 전송하기 이 전과 이 후로 구분합니다.
const canvas = document.getElementById('myCanvas');
const imgBase64 = canvas.toDataURL('image/jpeg', 'image/octet-stream');
const decodImg = atob(imgBase64.split(',')[1]);

캔버스 요소를 base64값으로 변환하였고 저장시 포맷은 image/jpeg로 설정하였습니다. 그리고 서버에 전송할 수 있는 blob 타입으로 변환하기 위해 base64를 atob()로 다시 디코딩하였습니다.
let array = [];
for (let i = 0; i < decodImg .length; i++) {
  array.push(decodImg .charCodeAt(i));
}

const file = new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
const fileName = 'canvas_img_' + new Date().getMilliseconds() + '.jpg';
let formData = new FormData();
formData.append('file', file, fileName);

new FormData() 생성자를 사용해 서버에 파일을 전달할 수 있습니다. new FormData()에 파일을 추가하기 위해 디코딩된 값을 형식화 배열로 변환 후 new Blob()를 사용하여 blob 타입으로 변환하였습니다. 그리고 파일의 이름을 저장했습니다.

파일 이름을 함께 저장해야 할까?
파일의 이름을 FormData()에 함께 저장하지 않을 수 있지만 이 경우 서버에서 파일의 이름 값이 blob의 이름으로 전송되게 됩니다. 즉 서버에서는 파일의 확장자와 이름을 전달 받지 못하게 되죠. 예제에서는 이름은 canvas_img_ 그리고 밀리세컨드 값을 이용해 임의의 이름을 가지도록 하였습니다. 임의의 이름은 중복된 이름을 피하기 위해서 입니다.

이제 마지막으로 ajax를 사용한 서버 전달 코드입니다.
$.ajax({
  type: 'post',
  url: '/upload/',
  cache: false,
  data: formData,
  processData: false,
  contentType: false,
  success: function (data) {
    alert('Uploaded !!')
  }
})

위 예제는 jQuery의 ajax() 메소드를 사용한 예제입니다. 물론 fetch나 axios 등 모두 가능합니다. 위 코드에서 주요한 부분은 type인 메소드는 'post' 값이어야 합니다. 그리고 processData의 값을 false로 설정해야 하는데 그렇지 않으면 string으로 자동 변환되는 문제가 생길 수 있습니다. 마지막으로 contentType은 false로 설정합니다.

여기까지 canvas 이미지를 ajax를 사용한 비동기 방식으로 서버 전달하는 모든 과정들을 알아보았습니다.