잡지식 정리 정돈
블로그 사진 용량, 서버에 보내기 전에 브라우저에서 줄이기
자체 개발한 블로그로 기존 네이버, 티스토리 블로그의 컨텐츠를 전부 옮겨온지 2주쯤 되었다. 이런 저런 자잘한 수정할게 아직도 많긴 한데, 블로그 편집기를 손보다가 문득 사진 한 장 당 용량이 얼마나 차지하는지 궁금해졌다.
네이버나 티스토리 같은 곳은 사진 저장 공간을 무료로 제공하다시피 하기 때문에, 공간 절약을 위해 해상도를 낮추고 압축을 통해 티가 나지 않는 선에서 화질을 떨어뜨리는 등 나름 조치가 되어 있을 것이다. 하지만, 내가 개발한 이 시스템은 그런 걸 전혀 염두하지 않고 만들었기에 어떨지 감이 전혀 안왔다.
실제로 확인해보니, 사진 아홉 장이 약 93.6MB였다. 개당 약 10MB 꼴. 화면에 표시될 땐 본문 너비에 맞춰 적당한 크기로 표시되지만, 화면에 작게 표시된다고 해서 용량도 작은 건 아니었던 거다. 사진 한 장 당 1~2MB 정도 수준으로 줄여서 업로드하는 기능을 조만간 적용시켜야 할 듯 하다. 아래는 사진을 압축해 올리는 방법에 대해 연구하다가 알게된 사실인데 개인적으로 전혀 몰랐던 부분이라 기록으로 남겨본다.
블로그 등에 올리는 사진을 압축하려면 사전에 별도 프로그램을 통해 압축을 미리 해놓던가, 서버에 올린 뒤 처리해야 할 것으로 생각하기 쉽다. 그런데 블로그 글을 쓰면서 편집기에 사진을 첨부하거나 붙여넣을 때, 편집기가 띄워진 그 브라우저 내에서 사진을 줄일 수 있다. 브라우저에 띄워진 편집기에서 압축된 업로드용 사본을 만든 뒤 서버로 보내는 것이다. 아래는 그 과정이 진행되는 순서다.
파일 선택 창에서 사진을 고르면 웹페이지는 그 파일을 읽을 수 있는 File 객체를 받는다. 이름과 용량만 전달받는 것이 아니라, 사용자가 건네준 파일의 내용도 읽을 수 있다. 브라우저가 컴퓨터의 모든 파일을 마음대로 뒤지는 것은 아니다. 파일 선택 창에서 사용자가 선택한 사진이 대상이 된다. MDN: File
어디에선가 Ctrl+c를 통해 클립보드에 복사해둔 상태의 이미지를 Ctrl+v 로 붙여넣기하는 경우에도 해당한다. 클립보드가 이미지 데이터를 제공하면 붙여넣기 이벤트에서 이를 파일로 꺼낼 수 있다. 이렇게 얻은 파일을 '파일 첨부'와 동일한 처리 과정을 거치게 하면 된다. 다만 웹페이지에서 복사한 것이 이미지 파일의 내용이 아닌, 이미지 파일의 주소나 HTML일 수도 있어, 모든 붙여넣기가 그렇게 처리된다고 보면 안된다. MDN: clipboardData, getAsFile
다음에는 편집기 상에 파일을 그림으로 펼친다. JPEG나 PNG는 그림을 보관하는 형식이다. 편집하려면 그 안에 저장된 내용을 해석해 가로,세로의 픽셀로 읽어야 한다. 브라우저의 createImageBitmap()이 이런 역할을 하고, Canvas라는 작업 공간에 원하는 크기로 그릴 수 있다. 이미지를 열어 작은 종이에 다시 그리는 과정에 가깝다. 실제 작업은 서버가 아니라 사진을 첨부하는 사람의 컴퓨터나 휴대폰 속 브라우저에서 이뤄진다. MDN: createImageBitmap, drawImage

여기서 PNG를 다루는 기준이 조금 흥미롭다. PNG는 투명도를 담을 수 있지만, PNG라고 해서 사진의 어딘가가 반드시 투명한 것은 아니다. 투명도를 기록하는 칸은 있는데 모든 칸에 ‘완전히 불투명’이라고 적혀 있을 수도 있다.
Canvas에서 픽셀 정보를 읽으면 빨강·초록·파랑과 투명도, 즉 RGBA 값이 나온다. 보통의 8비트 표현에서는 A(알파 채널)가 255이면 완전히 불투명하고, 그보다 작으면 반투명하거나 투명한 픽셀이다. ‘알파 채널이 있나’만 보는 것과 ‘실제 픽셀에 투명한 부분이 있나’를 확인하는 것은 다르다. 실제 투명 부분이 있는 이미지는 JPEG로 바꾸면 그 특성을 잃으므로, 투명도를 보존하는 PNG로 구분한다. MDN: 픽셀 데이터 읽기, RGBA 배열
크기를 줄이는 일도 둘로 나뉜다. 하나는 1) 가로,세로 픽셀 수를 줄이는 것이고, 다른 하나는 같은 그림을 파일로 저장할 때 2) 더 강하게 압축하는 것이다. 이번에 다시 압축할 정지 사진에는 가로,세로 비율을 유지하며 최대 16MP로 줄이는 기준을 잡았다. 16MP는 약 1,600만 화소라는 뜻이지 16MB라는 뜻이 아니다. 4:3 사진이라면 대략 4608×3456 정도다. 이미 16MP보다 작은 사진은 이 기준으로 해상도를 줄이지 않으므로, 압축을 다시(더 강하게) 하는 것에 의해서만 용량을 아껴볼 수 있다.
블로그에 올릴 사진을 얼마나 줄일지 기준을 잡을 때는 Google 포토를 참고했다. 평소 모든 사진을 그곳에 저장하고 있어, 이미 보관된 내 사진의 해상도와 용량을 비교해 볼 수 있기 때문이다. Google 포토의 '저장용량 절약'도 사진을 압축하고, 16MP가 넘으면 해상도를 줄인다. 다만 Google과 같은 압축 엔진을 쓰는 것은 아니고, 공식 안내에도 고정된 파일 용량이나 압축 품질 수치가 나와 있지는 않다. 따라서 16MP라는 기준을 참고하더라도 사진 한 장이 반드시 1MB로 줄어드는 것은 아니다. Google 포토: 백업 화질
Canvas에 그린 결과는 toBlob()으로 다시 파일 데이터로 만들 수 있다. 이때 JPEG 같은 형식과 압축 품질을 지정한다. 만들어진 Blob의 용량을 확인하고 업로드할 결과를 고르면 된다. 품질을 낮출수록 세부 표현이 달라질 수 있으므로, 최종 용량만큼 화면에서 보이는 결과도 중요하다. MDN: toBlob
모든 이미지를 무조건 JPEG로 바꾸는 것은 아니다. 해상도를 줄일 필요가 없는 작은 그림은 JPEG로 바꾼 결과가 더 크면 원본을 쓴다. GIF, APNG, 애니메이션 WebP·AVIF는 움직임을 잃지 않도록 원형을 보존한다. 16비트 PNG처럼 더 세밀한 투명도를 담을 수 있는 파일도 변환을 보수적으로 다룬다. 작게 만드는 것과 원래 표현을 유지하는 것 사이에 기준이 필요했다.
이 과정에서 내 폴더에 있던 원본 사진을 덮어쓸 필요는 없다. 원본을 읽어 별도의 업로드용 데이터를 만들고, 그것을 서버에 보내는 흐름이다. 첨부한 파일과 새로 만든 파일을 구분하면 ‘블로그에는 작은 사진을 올리고, 내 원본은 그대로 두는 것’이 가능하다. MDN: Blob
압축 작업을 브라우저가 맡으면 이 단계만을 위해 외부 이미지 변환 서비스를 호출할 필요도 없다. 대신 처리하는 동안 내 기기의 연산과 메모리를 사용한다. 서버는 전달받은 파일을 검사하고 저장한다. 사진의 전송량과 보관 용량은 줄일 수 있지만, 같은 사진 한 장을 요청하는 횟수까지 줄어드는 것은 아니다.
사진 아홉 장에 93.6MB였으니, 앞으로 사진을 계속 올릴 생각이라면 저장 공간도 함께 신경 써야 한다. 그렇다고 글을 쓸 때마다 사진을 따로 압축해 준비하는 건 번거롭다. 사진을 첨부하는 과정에서 편집기가 업로드용 사본을 줄여주면, 원본은 그대로 보관하면서 블로그에 쌓이는 파일의 용량을 줄일 수 있다. 글 쓰는 순서는 그대로 두고 사진이 저장되는 방식을 바꾸는 쪽이, 계속 사용하기에도 편하겠다.