대략 코드작성 및 배포를 마치고, 제가 코드 작성 시 마주했던 문제점들에 대해 하나씩 정리해볼까 합니다..
저는 이미지 미리보기를 구현해야했는데, type이 file인 input을 클릭 후 이미지를 올리면 이미지가 보이지 않는 문제를 마주했었습니다.
그래서 input을 통해 받아오는 이미지의 형태가 어떤건지 콘솔에 찍어보았더니, 파일형식으로 이미지를 받아오는 것을 확인할 수 있었습니다.
처음 접해보는 파일 형식.. 이게 무엇인가..? 이 객체 형태 그대로 img 태그의 src속성으로 직접적으로 주입하니 이미지가 보이지 않았습니다. 그래서 이게 왜그런가..? 그리고 대충인터넷을 뒤적거렸더니 base 64 로 변환해서 주입해야한다는 블로그 글을 보고 해결을 하긴 했지만, 어떻게 제가 해결했는지 이해가 되지 않아 블로그를 적어봅니다.
● Base 64
- base 64란 인코딩 방식을 이야기합니다. 0과 1로 이루어진 이미지 데이터를 base 64 즉, 이진데이터를 인코딩해 텍스트 형식으로 변환하는 것을 말합니다.
그래서 저도 file형식을 어찌저찌해서 base 64로 인코딩 하니 아래와 같이 길고~~긴~~ 텍스트 형식을 볼 수 있었습니다.
즉, 저 길고 긴 텍스트에 이미지가 담겨있는 것이죠. 따라서 서버에 이미지를 요청할 필요도 없고 쉽게 이미지를 img 태그로 보여줄 수 있습니다.
- base 64 형식이면 img 태그의 src에 직접적으로 주입할 수 있다.
- base 64 형식이면 변수로 저장해 사용할 수 있다.
- 근데 바이너리 데이터 대비 33%나 용량이 증가해, 남용한다면 로딩속도가 느려진다.
이미지 URL을 Base 64 형식으로 변환하는 방법
(async () => {
const data = await fetch('https://play-lh.googleusercontent.com/hYdIazwJBlPhmN74Yz3m_jU9nA6t02U7ZARfKunt6dauUAB6O3nLHp0v5ypisNt9OJk');
const blob = await data.blob();
const reader = new FileReader();
reader.onload = () => {
const base64data = reader.result;
console.log(base64data)
}
reader.readAsDataURL(blob);
})()
- fetch로 url을 불러오기
- blob형식으로 변환 한다.
- FileReader라는 내장 객체를 사용해 파일리더가 blob을 읽으면, readAsDataURL 메서드를 사용해 base64 형태로 변환한다.
● Blob 형식
BLOB은 Binary Large OBject의 약자로 주로 이미지, 오디오, 비디오와 같은 멀티미디어 파일 바이너리를 객체 형태로 저장한 것을 의미한다. 멀티미디어 파일들은 대다수 용량이 큰 경우가 많기 때문에, 이를 데이터베이스에 효과적으로 저장하기 위해 고안된 자료형이라 볼 수 있다. (string 타입, number 타입이 있듯이 blob 타입이 있다고 이해하면 된다)
좀더 이해하기 쉽게, base64와 어떤점이 다른지 비교해보자.
- base64는 바이너리 데이터를 다루기 위해 텍스트(문자열) 형태로 저장한 포맷이라고 했었다.
- blob이란 바이너리 데이터를 다루기 위해 객체(Object) 저장하는 것이다.
앞서 base64 포맷으로 이미지 바이너리 파일을 브라우저에서 표현하려면, FileReader 객체를 이용해 변환후 <img> 태그의 src 속성에 넣으면 가능했었다. 하지만 다음과 같이 문자열이 굉장히 길어져 가독성이 안좋을 뿐만 아니라, base64 이미지를 이곳저곳 여러개 사용할 경우 결과적으로 용량 문제 때문에(33% 커진다) 문서 자체를 로딩하는데 많은 시간이 걸려 오히려 느려질수 있다는 단점을 지니고 있다.
앞으로 나올 Blob, File 형태는 img태그의 src에 직접적으로 주입해 사용할 수 없다. 따라서 변환의 과정이 있다.
Blob은 두 가지 변환과정있다.
아까 위처럼 readAsDataURL 메서드를 활용한다.
내장 객체인 URL의 createObjectURL 메서드를 활용해 img태그의 src에 직접 주입할 수 있는 url형식으로 변환시킨다.
<img src="" alt="" style="width:150px; display: block;">
<a download="img.jpg" href="#">이미지 다운로드</a>
<script>
fetch('https://www.business2community.com/wp-content/uploads/2014/04/Free.jpg')
.then((response) => response.blob())
.then((blob) => {
const url = URL.createObjectURL(blob);
document.querySelector('img').src = url;
document.querySelector('a').href = url;
});
</script>
URL.createObjectURL 메서드를 이용하면 Blob 객체를 가지고 고유한 URL을 생성할 수 있다. 이때 생성되는 URL의 형태는 blob:/의 형태를 띄게 된다. 그리고 변환된 URL은 source(src)를 속성으로 가지는 모든 HTML 태그와 CSS 속성에서 사용 가능하다.
● File
내가 인풋에 올렸을 때, 마주했던 File 형태이다. 자바스크립트에서의 File 객체는 Blob 객체를 확장한 객체로 주로 파일시스템과 관련된 기능을 담당한다. 파일시스템은 OS/서버단의 영역인데, 브라우저 상에서도 파일을 주고 받는 등의 기능이 필요하기 때문에 이를 지원하기 위한 규격으로 볼 수 있다. 브라우저에서 자바스크립트를 이용해 파일을 다루기 위한 방법으로는 File 객체를 이용하거나 html의 input태그를 이용하는 두 가지가 있다.
만약 input 태그에 이미지를 여러개를 올리면 fileList, 배열이 들어오게되는데 그 안에 요소들로 File이 들어가있게된다. ( File[ ] ) 그런데 fileList형식은 배열이 아니고 유사배열객체이기때문에 배열 메서드를 쓰려면 배열로의 변환이 필요하다.
❍ FileReader 객체
위에서 사용해봤던 FileReader는 Blob 또는 File과 같은 객체로부터 데이터를 읽어 들이기위한 목적으로 사용되는 객체이다. 읽어들인 데이터는 주로 이벤트를 사용하여 필요한 타이밍에 데이터를 전달한다. 생성된 FileReader 객체에서 사용할 수 있는 주요 메서드는 아래와 같다.
File 형식도 src에 직접 주입하기 위해, base64로 변환시켜주면된다. 아래와 같이 할 수 있겠다.
<img src="">
<input type='file' onchange='readFile(this)' />
<script>
function readFile(input) {
const file = input.files[0]; // 첨부된 파일을 가져옴
const reader = new FileReader();
reader.readAsDataURL(file); // 파일을 base64로 변환
reader.onload = function() {
console.log(reader.result); // 읽은 파일 소스단에 출력
document.querySelector('img').src = reader.result;
};
reader.onerror = function() {
console.log(reader.error);
};
}
</script>
● 실제 프로젝트 코드로 주입
- 확장 이름이 jpg, jpeg, png만 받도록 했습니다.
- 파일 리더를 통해 base 64로 변환시켰습니다.
- 변환이 되면 SavedFile이라는 state에 배열로 저장하였습니다.
- 이후 이미지는 배열을 돌면서 하나씩 img태그에 주입해 보여주었습니다.
해결한 모습!!