이미지 업로드 로직을 구현하기 위한 기본지식 마련 (feat. webp와 손실 압축)

YJ KIM·2025년 1월 30일
1
post-thumbnail

글을 시작하기에 앞서서...

리처드 파인만이 한 말 중에
"공부한 것을 설명하지 못한다면 이것을 이해하지 못한 것이다." 라는 말이 있다.

이미지 로직 관련해서 항상 궁금했는데 매일 미루기 일쑤였다. 확실히 이해하고 넘어가고 싶어 자세히 설명을 해보려고 한다.


1. 이미지 관리 어떻게 할까?

서비스에 사진을 업로드 하는 경우는 매우 많다.
이러한 경우에, 이미지 데이터를 어떻게 관리해야 할까? 라는 물음이 생긴다.

이미지 데이터를 어떻게 관리할지? 라는 질문에 크게 4가지 답변을 할 수 있다.

  1. 이미지(바이너리 데이터)를 base 64 인코딩을 통해 문자열의 형태로 DB에 저장한다.
  2. 이미지를 그냥 DB에 무지성 저장한다.
  3. 서버 컴퓨터에 이미지를 저장하고 해당 파일의 경로를 db에 저장한다.
  4. 별도로 이미지를 저장하는 서버를 사서 (=스토리지) 저장한 만큼 돈 내고 쓴다.

이러한 4개의 답변 중 거의 정답에 가까운 건 4번이다.
사실 이게 정답이다 하는 건 없고 상황에 맞게 쓰면 되지만 1~3까지는 단점이 많다.

1. base 64 인코딩을 통해 문자열의 형태로 DB에 저장한다.

사실 제일 간단한 방법이다. 인코딩을 하긴 해야 되지만 파일을 문자열의 형태로 저장할 수 있고 문자열로 전달할 수 있으니 가히 이상적이다.

가장 큰 단점은 파일이 커진다는 것이다.

base 64 인코딩이란?
파일은 (이미지든 동영상이든) 다 2진수 파일이다. 그냥 단순히 0과 1로 이루어져 있다고 생각하면 된다. 이런 2진수 비트들을 6bit씩 묶는다. (2^6 = 64니 0~63의 수로 표현이 가능하다.)

즉 0~63까지 할당된 문자로 6bit를 표현하면 된다. (빈 값은 패딩으로 채우면 된다. 표 보면 패딩 문자는 '='로 나와있음.)

base 64로 인코딩 한다고 쳤을 때,
3byte = 24bit를 base64 인코딩 하면
6개의 bit 묶음 4개가 된다.

8개의 bit 묶음 3개가 -> 6개의 bit 묶음 4개가 된다는 것이다.
결론적으로 33%정도 용량이 커진다. 추가 연산도 해야되니 굳이 쓸 필요가 없다.

2. DB에 이진 형태로 저장한다.

이진 데이터는, Blob(Binary large object)의 형태로 DB에 저장할 수 있다.

사실 DB에 이진 데이터를 그냥 넣는다고 생각하면 좀 이질적으로 느껴지고 퍼포먼스가 안 좋을 것이라고 생각되는데 생각보다 퍼포먼스가 나쁘지 않다고 한다. (구글링하면 많이 나옴) 단점이 많이 없긴 하지만 내가 생각하는 가장 큰 단점은, DB에 파일을 굳이 저장할 필요가 없다. 사실상 파일 시스템에 저장하는 것이 최고의 퍼포먼스를 수행할텐데 DB에 저장해서 쿼리 형태로 저장하고 가져오고 할 필요가 없다. (내 생각)

3. 서버 컴퓨터에 이미지를 저장하고 (파일 시스템) 경로만 DB에 저장한다.

실제로 인턴 때 내부 시스템 구현하면서 이렇게 했었는데 다시 생각해보니 좀 미친 방법 같기도... 이미지 관리가 잘 이루어진다면 모르겠지만, 경로만 저장한다는 게 위험하다고 느껴진다. 객체지향 관점에서 바라본다면, 파일 경로와 파일의 응집도가 떨어진다고 볼 수도 있다.

4. 객체 저장 스토리지에 저장해서 저장한 용량만큼 돈 내고 쓴다.

거의 답을 정해놓은 것 같지만 내 생각엔 이것이 정답이다.
위에서 말한 단점들이 모두 해결되는 일종의 해결방안이기도 하다. 큰 단점은 돈.. 작은 서비스라면 AWS의 s3로 해결 가능할 것이고 무료 크레딧도 있으니 서비스가 크지만 않다면 괜찮다. 서비스가 커도 생각보다 비싸진 않아서 돈 내도 될 거 같음.

2. 근데 이미지가 뭐더라

생각해보니까 이미지에 대해 잘 모르는 것 같다.
iOS를 사용하는 사람은 HEIC 자료형에 대해 잘 알테지만 안드로이드 사용자는 잘 모를 수도 있다. 이렇게 이미지는 이진 데이터(파일)지만 여러 포맷이 존재한다.

근데 미리 말하자면 나는 모든 이미지를 webp로 변환할 예정이다.


1. 이미지란

간단하게 말하면 이미지란, 픽셀의 집합이다. 그리고 각 픽셀은 색상 정보이다.
제일 중요한 해상도는 각 이미지가 얼마나 세밀하게 표현되는지 정도를 의미한다.

같은 크기의 이미지라도 해상도가 높으면 선명하다. 같은 크기의 이미지라도 픽셀이 많으면 해상도가 높다.

이미지에는 여러 종류(포맷)가 있다. 이를 알기 위해서는 압축 이라는 개념에 대해 먼저 알아야 한다.

2. 압축

이미지 데이터는 생각보다 엄청나게 크다. 이미지 이야기는 아니긴 하지만, 동영상과 같은 데이터는 프레임이 미친듯이 많으니 정말 용량을 많이 차지한다. 그래서 압축을 통해 용량을 줄여야 한다.

압축 방법으로는 손실 압축 / 무손실 압축이 존재한다.

간단히 말하면,
손실 압축이란? 화질을 희생하면서 용량을 줄임
무손실 압축이란? 사람이 인식하기 어려울 정도의 부분을 제거하여 용량을 줄임 (대신 손실 압축보다 용량이 크다)

손실/무손실은 장단점이 확실하다. 그에 따라 어떤 포맷을 고를지 결정되기도 한다.

3. 이미지 포맷

포맷압축 방식특징
JPG손실 압축사진에 적합, 압축률이 높아 용량이 작아짐
PNG무손실 압축투명도 지원, 선명하지만 용량이 큼
GIF무손실 압축애니메이션 지원, 256색 제한
WebP손실/무손실JPG보다 용량 작고 품질 좋음, 브라우저 지원 증가 중
HEIC손실/무손실iPhone 기본 포맷, 효율적이지만 호환성이 낮음
BMP무압축원본 그대로 저장하지만 용량이 매우 큼

간단하게 정리할 수 있다.
안드로이드 카메라는 Jpg, iOS 카메라는 HEIC를 사용한다.

4. webp

위에서 말했듯이 난 webp 포맷으로 모두(HEIC, JPG) 변경하기로 결정했다.

왜 webp를 선택했을까?
webp는 구글에서 만든 효율적인 이미지 포맷으로 손실/비손실 압축을 모두 지원하고, 무손실에서는 png보다 효율적이고 손실에서는 jpg보다 효율적이다.

큰 단점이라고 하면, iOS 지원이 상대적으로 늦어서 iOS 14부터 가능하다는 점과 지원이 안 되는 브라우저가 있다는 점이다. (거의 다 되긴 함)

5. 손실 webp vs 무손실 webp

안드로이드 휴대폰에서 사진을 찍으면 사진은 jpg로 저장된다. 손실 압축이 된 것이기 때문에 이를 무손실 webp로 변환할 필요는 없다. (관련해서 용량이 더 커진다는 말이 있다. https://developers.google.com/speed/webp/faq?hl=ko#can_a_webp_image_grow_larger_than_its_source_image 관련 글 확인)

아이폰에서 기본카메라로 사진을 찍으면 사진은 손실 Heic로 저장된다. 하지만 특정 옵션을 통해 무손실 Heic로 저장되는 경우도 있다. 하지만 대부분 손실 HEIC이고 손실 압축된 이미지를 무손실 압축하면 크기만 커지고 도움이 안 된다. (화질이 좋아지지 않음)

결론적으로는, 이미지는 기본적으로 손실 압축된 이미지라고 판단하고 모두 손실 webp로 포맷 변경하는 것이 효율적인 로직을 구현할 수 있다.

서비스에 따라서 클라우드 저장 서비스가 아닌 경우, 손실을 한다고 해서 큰 영향이 없다고 생각한다. (내 생각)

6. 리눅스에서 파일 데이터 읽는 법

공부 하다가 알게된 건데 잊을까봐

이미지 파일은 헤더가 존재한다. 리눅스에서 xxd 명령어로 앞 몇 바이트만 확인하면 현재 이미지 파일이 어떤 포맷인지 알 수 있다.

xxd -b -l 8 image.jpg
00000000: 11111111 11011000 11111111 11100000 00000000 00010000  ......
00000006: 01001010 01000110                                      JF

이진수로 맨 앞 2바이트를 읽으면, 1111 1111 1101 1000(2)이다.
16진수로 FF D8(16)인데 JPEG 파일의 시작을 나타내는 마커이다.

이후에 다른 마커도 있어서 파일 시스템은 파일 확장자와 파일의 전체적인 내용을 보고 현재 파일의 포맷이 무엇인지 확인한다고 한다.

하다가 궁금해진 건데 이진 파일의 경우 그냥 1111 1111 1101 1000으로 시작되는 경우도 있지 않나? 했는데 마커가 엄청 많아서 우연하게 겹칠 일은 절대 없다.

3. 어떤 스토리지를 사용해야 할까?

그럼 여태까지 정한 게,

스토리지 서비스를 이용하여 이미지를 저장하고,
이미지는 손실 webp로 변환하여 압축하기로 결정했다.

그럼 어떤 스토리지 서비스를 이용해야 할까?

알아본 것중엔 제일 유명한 AWS의 s3와 Firebase의 cloud storage가 있다.

https://aws.amazon.com/ko/s3/
https://firebase.google.com/docs/storage?hl=ko

둘 다 가격적인 측면에서는 비슷한 정도의 요금을 형성한다. 사용하는 서비스에 따라 알맞게 선택하면 될 것 같다.

profile
모르면 쓰지 말고 쓸 거면 알고 쓰자

0개의 댓글