Base64 , Data URL , FileReader 에 대해...

조민호·2023년 5월 17일
3

웹에서 이미지 및 각종 파일을 다루게 되면서 그때그때 필요한 정보들을 가지고 구현에만
집중하다 보니 관련 개념들이 파편화 되어 정리가 안 되다 보니 너무 답답했습니다

그래서 이미지나 비디오 같은 파일들을 다루는 관련 자료를 바탕으로 다시 정리 해 보았습니다




base 64


Base 64 는 데이터를 64진법 으로 나타내는 것으로,

8비트 2진 데이터를 (플랫폼의) 문자 코드에 영향을 받지 않는 공통 ASCII 영역의 문자들로만 이루어진 일련의 문자열로 바꾸는 인코딩 방식을 가리키는 개념입니다

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 으로 나타내며
총 64개의 문자로 구성되어 있기 때문에 Base 64라고 불리웁니다.

A-Z,a-z,0–9,/+ 만을 사용하기 때문에, 문자 포맷이 달라 데이터를 손상시킬 수있는 시스템 간에 안정적으로 전송 될 수 있습니다

html에 img의 src에 url이 아닌 숫자와 문자로 구성된 긴 코드가 들어간 경우가 있었는데 data:image/png;base64와 같은 형태가 바로 이것입니다

아래와 경우에는 이미지를 base64인코딩 방식으로 사용합니다


  1. 크기가 작은 이미지를 이미지 파일없이 html에 만들어 넣는다거나

  2. 간단한 페이지를 작성해 임시로 이미지를 사용하는 경우

  3. 메일을 html으로 작성해서 보내는 경우


Javascript 에서의 Base 64 변환

  • btoa(DOMString data): DOMString; 메소드 btoa() 은 입력 문자열을 Base 64 으로 변환해서 반환합니다 만약 입력 문자열에 유니 코드 같은 btoa 에서 이해할 수 없는 문자열이 들어오면 InvalidCharacterError 가 발생 합니다
  • atob(DOMString data): ByteString; 메소드 atob() 은 인코딩된 Base 64 문자열을 디코드 합니다 만약 입력 문자열에 Base 64 에 포함되지 않는 문자 (A-Z,a-z,0–9+/ 이외) 가 입력되면 DOMException 이 발생합니다
var str = "The quick brown fox jumps over the lazy dog.";
 
var encodeStr = window.btoa(str);
console.log(encodeStr);
// VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4=
 
var decodeStr = window.atob(encodeStr);
console.log(decodeStr);
// The quick brown fox jumps over the lazy dog.

유니코드를 Base 64 로 변환해야할 경우, 일반적으로 InvalidCharacterError 예외가 발생합니다

하지만 유니코드를Uniform Resource Identifier (URI) 컴포넌트로 변경하고 다시 Base 64 변경한다면 가능합니다

encodeURIComponent() 와 decodeURIComponent 를 이용하여, 유니코드 를 URI 으로 변경하여 사용할 수 있습니다

var uni = '🙂';
var data = encodeURIComponent(uni);
// %F0%9F%99%82
var encode = window.btoa(data);
// JUYwJTlGJTk5JTgy
var decode = window.atob(encode);
// %F0%9F%99%82
decodeURIComponent(decode);
// 🙂



그러나 위의 함수들은 바이너리 데이터(예: 이미지, 비디오 파일 등)를 직접 다루지는 못합니다.

  • ASCII 문자열과 Base64 문자열 사이의 변환에 사용되는 것이 atob() , btoa() 함수이며

  • 이미지나 비디오 파일등에 대한 바이너리 데이터와 Base64 문자열 사이의 변환은

    FileReader API의 readAsDataURL 함수를 사용해야 합니다


그렇다면 FileReader 란 무엇일까

FileReader 는 File 혹은 Blob 객체를 이용하여, 웹 애플리케이션에서 파일을 비동기적으로 읽을 수 있게 해주는 Web API 입니다

FileReader의 메소드 종류는 아래와 같습니다

메서드설명
readAsText(blob, [,encoding)텍스트 형식으로 읽기
readAsArrayBuffer(blob)ArrayBuffer 형식으로 읽기
readAsDataURL(blob)data URL이 가리키는 데이터 읽기
readAsBinaryString(blob)이진 데이터 형식으로 읽기

FileReader의 모든 메소드는 데이터를 읽는 작업을 비동기로 처리합니다.

FileReader는 지정된 Blob을 읽으면서 readState 프로퍼티를 업데이트 합니다
readState 값은 아무것도 읽지 않았다면 0 ,
어떤 데이터를 읽으면 1, 모두 다 읽어 들여서 작업을 완료하면 2를 가지고 있게 됩니다


그 중에서 가장 자주 쓰이는 readAsDataURL() 에 대해 보면 ,

Input 태그로 FileList 를 얻은 뒤, FileReader.readAsDataURL() 을 통해

파일의 내용을 읽어서 그 결과를 Base64 형태로 인코딩된 Data URL로 반환 할 수 있습니다

이 URL은 data: 스키마와 MIME 타입 정보, 그리고 Base64로 인코딩된 데이터를 포함합니다.


FileReader API의 readAsDataURL 의 예시를 봅시다

<input type="file" id="imageFile" name="imageFile" accept="image/*">
<img id="outputImage" />
                                                 //input창 입력 이벤트 할당
document.getElementById('imageFile').addEventListener('change', function (e) {
  let file = e.target.files[0]; // Input 태그로 FileList의 첫번째 값을 가져옴
  let reader = new FileReader(); //FileReader 객체 생성
 
	**// 2) 읽어지는 이벤트 발생시 , 
  //    Base64로 인코딩된 Data URL을 이미지 태그의 src 속성에 설정**
  reader.onloadend = function () { 
    document.getElementById('outputImage').src = reader.result;
  };

	**// 1) 선택된 이미지 파일을 readAsDataURL()을 통해 Base64로 인코딩된 
  //    Data URL 형태로 읽음**
  if (file) { 
    reader.readAsDataURL(file);
		**// 읽혀진 Data URL은 reader객체의 result속성에 저장됨**
  }
}); 

위 예제에서 사용자가 이미지 파일을 선택하면, FileReader 객체가 생성되고 해당 파일을 읽어서 Base64로 인코딩된 Data URL을 생성합니다. 이 Data URL은 이미지 태그의 src 속성으로 설정되어 웹 페이지에 표시됩니다.

  • FileReader 객체에 'loadend' 이벤트 리스너를 추가합니다. 이 리스너는 FileReader가 파일 읽기를 완료하면 실행됩니다. 이 리스너 함수에서는 읽어온 결과(즉, Base64로 인코딩된 이미지 데이터)를 이미지 태그의 src 속성에 설정합니다.
    • readAsDataURL() 메소드를 호출하여 input창에 이미지가 선택됐다면, 이미지 파일을 읽기 시작합니다. 이 메소드는 파일을 Base64로 인코딩된 Data URL 형태로 읽습니다.
    • FileReader가 파일 읽기를 완료하면, 앞서 설정한 'loadend' 이벤트 리스너 함수가 실행됩니다. 이 함수에서는 FileReader의 결과를 이미지 태그의 src 속성에 설정합니다. 이렇게 하면 웹 페이지에 선택한 이미지가 표시됩니다.

그렇다면 왜 굳이 코드를 실행 순서대로 적지 않고, reader.readAsDataURL(file) 을 마지막에 작성 했을까요?

결론부터 말하자면 , 파일을 읽는 작업이 시작되기 전에 onloadend 이벤트 리스너를 먼저 설정하는 것이 좋습니다.

만약 readAsDataURL 호출이 아주 빠르게 완료되어 (예를 들어, 아주 작은 파일을 읽는 경우) onloadend 이벤트가 발생하고, 그 시점에 아직 onloadend 이벤트 리스너가 설정되지 않았다면, 그 이벤트는 놓치게 됩니다.

이렇게 되면 이미지가 로드되었음에도 불구하고, 이를 처리하는 코드가 실행되지 않게 됩니다.

따라서, readAsDataURL를 호출하기 전에 onloadend 이벤트 리스너를 먼저 설정해주는 것이 좋으므로 reader.onloadendreader.readAsDataURL(file); 전에 설정한 것입니다

이렇게 하면 파일 읽기가 완료되는 시점에 반드시 이벤트 리스너 함수가 호출되도록 할 수 있습니다.



반대로, 이미 Base64로 인코딩된 문자열이 있고 이것을 이미지로 디코딩하려면,

그 문자열을 기반으로 Data URL을 생성하여 이미지 태그의 src 속성으로 설정해주면 됩니다

이미 Base64로 인코딩된 문자열은 이미 데이터의 일부이며,
이를 Data URL 형식에 맞게 적절한 접두어와 함께 조합해서 사용하는 것입니다

<img id="outputImage" />
let base64String = '여기에 Base64로 인코딩된 문자열을 넣으세요';
document.getElementById('outputImage').src = 'data:image/jpeg;base64,' + base64String;
																	// Data URL 형식에 맞게 적절한 접두어와 함께 조합 후 src에 할당

이렇게 하면 웹 브라우저가 자동으로 Base64 인코딩된 문자열을 디코딩하여 이미지로 표시해줍니다.




위에서 계속 언급한 Data URL에 대해서도 알아봅시다


Data URL scheme


DataURL 이미지, 비디오, 오디오 파일, 또는 다른 형태의 데이터를 URL 형태로 인코딩하여 직접 웹 페이지에 내장시키는 방법 입니다

그러므로 이미지 같은 파일들을 문서 (Html, JS, CSS) 에 인라인으로 작성할 수 있는데, 

작성된 이미지와 같은 정보는 이미 문서에 포함되어 있기때문에, 서버에 추가적인 HTTP 요청을 요청하지 않고도 이미지를, 사용할 수 있는 것입니다

Data URL의 구성

  1. Data URIs는 접두사(data:)

  2. 데이터의 타입을 가리키는 MIME 타입

  3. 텍스트가 아닌 경우 사용될 부가적인 base64 토큰

  4. 데이터 자체

이렇게 Data URL의 일반적인 형식은 총 4가지 부분으로 구성됩니다

data:[<mediatype>][;base64],<data>

각 형식에 대해 자세히 보자면

  • data:
    모든 Data URL은 data:로 시작합니다. 이것은 URL이 데이터를 직접 포함하고 있다는 것을 나타냅니다.

  • [<mediatype>]
    선택 사항입니다. 이 부분에는 데이터의 MIME 타입이 올 수 있습니다. 예를 들어, HTML 문서의 경우 text/html, JPEG 이미지의 경우 image/jpeg 등이 올 수 있습니다.

  • [;base64]

    선택 사항입니다. 이 부분이 있으면 데이터가 Base64로 인코딩되었다는 것을 나타냅니다.

    이 부분이 없다면 base64 인코딩이 아닌 url인코딩이 적용된 것으로 간주합니다 (아래 예시 있음)

  • , (콤마)

    data:<data>사이에는 콤마(,)가 있습니다.

  • <data>
    실제 데이터입니다. 만약 ;base64가 있으면 이 데이터는 Base64로 인코딩된 형태여야 합니다.

사용 예시 1

위에서 언급했었던

Base64로 인코딩된 문자열이 있고 이것을 이미지로 디코딩하려면,

그 문자열을 기반으로 Data URL을 생성하여 이미지 태그의 src 속성으로 설정해주면 된다고 했습니다

<img id="outputImage" />
let base64String = '여기에 Base64로 인코딩된 문자열을 넣으세요';
document.getElementById('outputImage').src = 'data:image/jpeg;base64,' + base64String;
																	// Data URL 형식에 맞게 적절한 접두어와 함께 조합 후 src에 할당

이미 Base64로 인코딩된 문자열은 이미 데이터의 일부이며,
이를 Data URL 형식에 맞게 적절한 접두어와 함께 조합해서 사용하는 것입니다

'data:image/jpeg;base64,' + base64String;
  • [] 으로 JPEG 이미지 데이터를 명시합니다

  • [;base64] 으로 콤마(,) 뒤에 오는 부분이 Base64로 인코딩되었다는 것을 명시합니다

  • 부분에 데이터를 넣습니다

    Base64로 인코딩된 문자열(변수 base64String)은 이미 데이터의 일부이므로
    data:image/jpeg;base64 와 함께 , <data> 부분에 넣어서 사용하는 것입니다

따라서 위의 코드는 JPEG 이미지 데이터를 Base64로 인코딩한 Data URL을 생성하는 코드입니다

이렇게 생성된 Data URL은 <img> 태그의 src 속성 등에 사용할 수 있습니다.


사용 예시 2

HTML ⇒ img태그 src

<img src="data:image/<이미지확장자>;base64,<data코드>")

CSS ⇒ background-image

background-image: url('data:image/<이미지확장자>;base64,<data코드>')

사용 예시 3

간단한 text/plain 데이터

data:,Hello%2C%20World!

부분으로 사용된 Hello%2C%20World!

base64로 인코딘 된것이 아닌 , URL 인코딩 (or 퍼센트 인코딩)이 적용된 문자열입니다

URL 인코딩은 URL에서 안전하게 사용할 수 있는 문자만을 사용하여 데이터를 표현하는 방법입니다.

URL에서 직접적으로 사용할 수 없는 문자들 (공백, 특수문자, 비알파벳 문자 등)은 % 기호와 그 뒤에 따르는 두 개의 16진수 숫자(해당 문자의 ASCII 코드)로 인코딩됩니다


예를 들어, Hello%2C%20World!에서 %2C는 콤마(,)를, %20는 공백을 나타내는 것입니다.

따라서 이 문자열을 디코딩하면 Hello, World!가 됩니다.

Data URL에서는 Base64 인코딩이나 URL 인코딩 중 하나를 사용하여 데이터를 안전하게 표현합니다.

이처럼 ;base64가 없는 경우에는 URL 인코딩이 사용된 것으로 간주합니다.

위 예제의 base64 인코딩 버전

data:text/plain;base64,SGVsbG8sIFdvcmxkIQ%3D%3D

<h1>Hello, World!</h1>인 HTML 문서

data:text/html,%3Ch1%3EHello%2C%20World!%3C%2Fh1%3E

자바스크립트 얼럿을 실행하는 HTML 문서

(script의 닫는 태그가 필요하다는 것을 주의하세요)

data:text/html,<script>alert('hi');</script>

참고 :

Base 64 간단 정리하기

데이터 URIs - HTTP | MDN

Base 64와 base64 img 사용하기

[Javascript] API 활용 - Blob

profile
웰시코기발바닥

0개의 댓글