[refactoring]이미지 최적화(압축 및 리사이징을 곁들인🍃)
🔗 들어가며: 왜 이미지 압축 / 리사이징이 필요했을까?
퀴즈 기반 학습 서비스 구현 과정에서 LazyColumn 대부분의 item에 이미지가 들어가게 되었다. 이때 사용자가 설정한 이미지 파일 용량이 클 경우, 이미지를 저장하거나 로딩할 때 비교적 오랜 시간이 걸리는 것을 확인하였다.
다른 팀에서 진행한 서비스 QA 내용 중에서도 이미지 업로드 및 로드 속도와 관련된 내용이 존재하였다. 따라서 이미지 압축과 리사이징을 통해서 업로딩 / 로딩 속도를 개선할 필요성을 다시 한번 느끼게 되었다.
(+ 추가로 우리 서비스의 경우, 전반적인 서버 구축을 fireStore
를 활용하였다. 이때 큰 용량의 이미지 파일을 그대로 업로드할 경우, 불필요하게 많은 서버 비용이 청구될 위험이 있다. 이러한 점을 추가적으로 고려해 최종적으로 이미지 압축 및 리사이징을 적용하기로 하였다.)
🔗 이미지: 관련 개념 정리
1️⃣ 이미지 종류: 비트맵 vs 벡터
비트맵 방식
은 픽셀들의 집합을 통해 이미지를 형성한다. 따라서 확대할수록 이미지가 깨지는 특징을 보인다.벡터 방식
은 수학적 연산에 의한 그래픽 이미지 표현 방식이다. 따라서 사이즈와 상관없이 그래픽이 깨지지 않는다.
2️⃣ 압축 방식 비교: 손실 vs 무손실
- 손실: A → 압축 → 복원 → a` (원본 데이터 ≠ 복원 데이터)
- 불필요한 데이터를 삭제하는 방식으로 압축 진행
- 사용: 이미지, 동영상, 오디오 등 미디어 압축
- 무손실: A → 압축 → 복원 → A (원본 데이터 = 복원 데이터)
- 사용: 파일 압축
3️⃣ 확장자: android bitmap compress 지원 포맷
비트맵 이미지의 확장자는 아래 종류보다 더 다양하지만 안드로이드 bitmap compress에서 지원하는 확장자를 중심으로 비교를 해보고자 한다.
확장자 | 압축 방식 | 파일 크기 | 압축 속도 | 화질 | 투명도 지원 | 사용 | 특징 |
---|---|---|---|---|---|---|---|
JPEG | 손실 압축 | 비교적 작음 | 빠름 | 압축률이 높을수록 화질 손실 발생 | 알파 채널 미지원 | 웹에서 널리 사용, 사진, 복잡한 이미지, 색상 변화가 많은 이미지에 적합 | |
PNG | 무손실 압축 | JPEG보다 큼 | 느림 | 원본 화질을 유지함 | 알파 채널 지원 | 투명 배경이 필요한 이미지, 텍스트 / 로고처럼 선명해야 하는 이미지 | 고해상도 이미지에 적합 |
WEBP(구글에서 만든 이미지 포맷) | 손실 / 무손실 압축 | JPEG 보다 작고, PNG와 비슷하거나 더 작음 | 높은 압축률에도 상대적으로 좋은 화질을 유지 | 알파 채널 지원 | api level 30부터 deprecated됨, 아래 두 종류로 나뉨 | 구글에서 만든 이미지 포맷 |
확장자 | 압축 방식 | 사용 |
---|---|---|
WEBP_LOSSY | 손실 압축 | android q(api level 29)부터 제공, 저장 공간이나 대역폭이 제한적인 경우 사용 |
WEBP_LOSSLESS | 무손실 압축 | 투명 배경이 필요한 이미지, 텍스트 / 로고처럼 선명해야 하는 이미지 |
4️⃣ 압축률이란?
압축을 진행했을 때 데이터 크기가 원본에 비해 얼마나 많이 줄어들었냐를 나타내는 지표이다. 즉, 압축률이 높을수록 파일의 크기가 더 많이 줄어드는 것이다.
🔗 이미지 압축: 비교 결과
다음의 노션 페이지를 본다면 우리 팀에서 진행한 확장자 및 압축률에 따른 압축 시간과 용량 차이를 확인할 수 있다.
1️⃣ 비교 결과
- 파일 크기: JPEG » WEBP
- 압축 속도: JPEG « WEBP
우리 팀은 용량을 최소화해 파일 업로드 및 로드 시간을 줄이고, 서버 비용을 최소화하는 것이 최종 목표였다. 용량 최소화를 위해 우선 무손실 압축 방식인 PNG는 비교에서 제외하였다. 또한, 압축 속도보다 파일 크기에 우선순위를 더 크게 두어 최종적으로 WEBP
를 활용해 이미지 압축을 진행하였다.
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
resizedBitmap.compress(Bitmap.CompressFormat.WEBP_LOSSY, 60, baos)
} else {
resizedBitmap.compress(Bitmap.CompressFormat.WEBP, 60, baos)
}
WEBP는 위에서 확인했듯이 손실 / 무손실 두 가지 방식 모두 지원한다. 우리 서비스의 대부분의 이미지는 프로필 이미지기 때문에 약간의 화질 저하가 존재해도 괜찮다. 따라서 WEBP_LOSSY
를 적용해 용량을 줄일 수 있도록 코드를 작성하였다. WEBP_LOSSY
의 경우, Android Q 이상부터 지원하기 때문에 버전에 따른 처리를 진행해줬다.
🔗 이미지 압축: 추가적으로 짚고 넘어갈 부분
JPEG가 PNG에 비해 더 적은 용량을 제공하는 이유
- 손실 압축(가장 큰 원인)
-
알파 채널 지원 여부
JPEG는 알파 채널을 지원하지 않기 때문에 PNG보다 용량이 작아진다.
-
JPEG 압축 속 크로마 서브 샘플링
JPEG는 주로 YCbCr(Y: 밝기, Cb, Cr: 색차 성분) 색상 공간을 사용한다. 이때 사람의 시각이 색상보다 밝기에 민감하다는 점을 활용해 색상 정보를 밝기 정보보다 낮은 해상도로 줄임으로써 데이터를 줄이게 된다.(크로마 서브 샘플링)
반면 PNG는 RGB 색상 공간을 활용하며, 색상 정보를 그대로 유지한다.
그럼에도 불구하고 PNG를 사용해야 하는 경우
- 무손실 압축이 필요한 경우
- 로고, 아이콘, 텍스트 이미지와 같이 선명한 경계선과 뚜렷한 색상 대비가 중요한 경우 손실 압축으로 인한 화질 저하가 눈에 띄게 나타날 수 있다.
- 편집이 잦은 이미지는 수정하고 저장할 때마다 압축 과정에서 데이터 손실이 누적되게 된다.
- 투명도가 필요한 경우
- WEBP 역시 투명도를 지원하지만, 일부 구형 브라우저나 시스템에서는 WEBP 자체를 지원하지 않을 수 있다.
- 팔레트 기반 이미지를 저장할 때
- 팔레트 기반 이미지는 제한된 색상 집합(팔레트)를 사용해 구성한 이미지를 의미한다.
- PNG: 팔레트 기반 지원
- JPEG: 팔레트 기반 미지원
- WEBP: 팔레트 기반 지원
- WEBP의 경우, 압축 방식이 PNG에 비해 팔레트 기반 이미지에 특화되어 있지 않음, 따라서 압축 효율이 PGN에 비해 낮음
- JPEG의 경우, 팔레트 기반을 지원하지 않음. 이미지를 24비트 트루 컬러로 변환한 후 압축을 적용하기 때문에 용량이 더 커지는 문제가 생길 수 있음
- 팔레트 기반 이미지는 제한된 색상 집합(팔레트)를 사용해 구성한 이미지를 의미한다.
- 호환성 문제
🔗 이미지 리사이징
다음의 노션 페이지를 본다면 우리 팀에서 진행한 리사이징에 따른 용량 및 화질 차이를 확인할 수 있다.
리사이징 역시 비트맵에서 제공하는 함수를 통해 손쉽게 적용이 가능했다. 우리 팀의 경우, 디스플레이 해상도 기준들을 참고해 리사이징을 진행하였다.
해상도 | 사이즈 |
---|---|
HD | 1280 x 720 |
FHD | 1920 x 1080 |
QHD | 2560 x 1440 |
UHD | 3840 x 2160 |
우리 팀에서는 압축 시간 대비 화질이 가장 좋은 사이즈를 확인해보았고, 이미지 width가 2560이 넘어갈 때 2560을 기준으로 리사이징을 하는 것을 최종적으로 결정하였다.
모바일 디바이스 중에서 가로가
2560
을 넘어가는 경우가 과연 많을까?멘토님께서 해당 부분에 대해 다음과 같은 의문을 제시해주었다. 대부분의 모바일 디바이스의 width는 2560을 넘어가지 않는다. 그렇기 때문에 2560을 기준으로 한다면 리사이징이 큰 의미가 없을 것 같다는 판단을 하였고, 추후 해당 사이즈를 수정할 예정이다.
🔗 마무리하며
최종적으로 우리팀은 WEBP 60을 통해 이미지 압축을 진행하였고, 가로 사이즈가 2560을 넘어갈 경우 리사이징 하는 방식으로 이미지 최적화를 진행하였다. 그 결과, 이미지 업로드 및 로딩하는 시간이 4초 -> 2초로 줄어드는 결과를 얻을 수 있었다.
이미지 압축 및 최적화 하는 방식은 생각보다 간단했다. 오히려 서비스의 특성에 맞게 적절한 압축방식과 리사이징을 고민하는 과정에 더 오랜 시간이 필요했던 것 같다. 이번 계기를 바탕으로 각 이미지 확장자의 차이에 대해 명확히 짚고 넘어갈 수 있었다. 이 경험은 추후 서비스 특성에 맞춰 올바른 확장자를 판단하는데 큰 도움이 될 것 같다!
댓글남기기