안드로이드 이미지 압축, 어떻게 하면 효율적일까?

ImageDecoder 도입으로 안드로이드 이미지 처리 최적화하기

by SOPT makers


1*h4UUXhHIrmAJGo3Eg3M73Q.png


안녕하세요! 메이커스 36기 APP team, Android 개발자 한민재입니다.


오늘은 안드로이드 앱에서 이미지 압축을 더 효율적으로 처리하는 방법에 관해 이야기해보려고 해요. 특히 메이커스 코드를 ImageDecoder로 개선한 경험을 공유하고자 해요!





왜 이미지 처리 개선이 필요했을까요?


이전에 다른 프로젝트에서 이미지 처리에 관해 깊게 공부했던 경험이 있었어요. 그러던 중 메이커스에서 사용하던 ContentUriRequestBody 클래스에 유사한 문제점을 발견하게 되었습니다.


기존 코드는 다음과 같은 문제점을 가지고 있었어요.



기존 코드의 문제점


복잡한 이미지 처리 로직 — 이미지 로딩, 크기 계산, 회전, 압축 등의 작업이 하나의 메소드에 복잡하게 얽혀 있었어요

비효율적인 메모리 사용 — BitmapFactory와 ExifInterface를 사용하는 방식이 메모리를 많이 소모했어요

비일관적인 압축 품질 — 이미지 크기에 따른 압축률 계산 로직이 직관적이지 않았어요

오류 처리 부재 — 이미지 처리 과정에서 발생할 수 있는 예외 상황을 적절히 처리하지 않았어요

유지보수 어려움 — 코드 구조가 명확하지 않아 수정이나 확장이 어려웠어요




ImageDecoder로 무엇이 달라졌을까요?


Android P(API 28)부터 도입된 ImageDecoder는 기존의 BitmapFactoryGifMovieFactory를 대체하는 통합 API로 설계되었어요.


ImageDecoder | API reference | Android Developers



ImageDecoder의 장점은 아래와 같아요.


더 적은 메모리로 고해상도 이미지를 처리할 수 있어요

이미지 크기 조정이 더 간단하고 효율적으로 가능해요

압축과정 중에 발생한 회전의 보정 처리 없이도 이미지 방향이 자동으로 보정돼요

대용량 이미지도 점진적으로 디코딩하여 UI 스레드 차단을 방지해요

HDR 이미지, GIF, WebP와 같은 애니메이션 이미지와 최신포맷을 처리할 수 있어요




코드 개선 과정


1. 이미지 디코딩 방식 개선


기존 코드에서는 BitmapFactoryExifInterface를 조합해 이미지를 디코딩하고 회전 보정을 했어요.


변경 전
carbon (1).png 이미지 디코딩 방식을 개선하기 전이에요.


변경 후
carbon (1).png 이미지 디코딩 방식을 개선한 후예요.


2. 이미지 크기 계산 로직 개선


기존에는 복잡한 샘플링 계산 방식을 사용했지만, 새로운 코드에서는 더 직관적인 스케일링 방식을 도입했어요.

carbon.png 이미지 크기 계산 로직을 개선한 함수예요.


이 함수는 이미지의 가로세로 비율을 유지하면서 최대 크기 내로 조정하는 스케일 팩터를 계산해요. 간단하면서도 효과적인 접근 방식이죠!



3. 압축 품질 결정 로직 개선


이미지 압축 품질을 결정하는 로직도 더 명확하고 직관적으로 바꿨어요.


변경 전
carbon (2).png 압축 품질 결정 로직을 개선하기 전이에요.


변경 후
carbon (5).png 압축 품질 결정 로직을 개선한 후예요.



4. 데이터 구조화 및 예외 처리 개선


이미지 메타데이터를 관리하기 위한 전용 데이터 클래스를 만들고, 예외 처리도 강화했어요.

carbon (6).png 데이터 구조화와 예외 처리를 개선했어요.



ImageDecoder의 실제 내부 동작 원리


ImageDecoder가 어떻게 기존 방식보다 효율적으로 동작하는지 살펴볼까요?



디코딩 파이프라인


ImageDecoder는 다단계 파이프라인을 통해 이미지를 효율적으로 처리해요.


소스 데이터 읽기 — 파일이나 URI로부터 데이터를 읽어오기

파일 포맷 파싱 — 이미지 유형을 식별하고 구조를 분석하기

메타데이터 추출 — 크기, 색상 공간 등의 정보를 추출하기

실제 픽셀 디코딩 — 필요한 경우에만 실제 픽셀 데이터를 디코딩

후처리 — 크기 조정, 색상 공간 변환 등을 수행!





메모리 할당자의 역할


decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE와 같은 설정은 어떤 의미가 있을까요?


Android에서는 이미지 처리 시 크게 세 가지 메모리 할당 방식을 제공해요.


ALLOCATOR_SOFTWARE — CPU 메모리에 비트맵 할당, 이미지 변형이나 조작이 필요할 때 사용해요

ALLOCATOR_SHARED_MEMORY — 비트맵을 공유 메모리에 할당해 HW 가속 렌더링에도 사용이 가능해요

ALLOCATOR_HARDWARE — GPU 메모리에 비트맵을 할당해 렌더링 성능을 최적화하지만, 이미지 조작이 제한적이에요


우리 코드에서는 이미지를 수정하고 재압축해야 하므로 CPU를 적극적으로 사용하는는 ALLOCATOR_SOFTWARE를 선택했어요.




변경 가능한(Mutable) 비트맵


decoder.isMutableRequired = true 설정은 결과 비트맵이 변경 가능해야 함을 의미해요. 비트맵 압축

이나 픽셀 조작 등 결과 이미지를 수정해야 할 경우 필수적인 설정이에요! 이 속성이 없으면 불변(immutable) 비트맵이 생성되어 compress() 메소드 호출 시 오류가 발생할 수 있어요.





개선된 코드의 전체 구조


개선된 전체 코드는 다음과 같은 구조로 재구성되었어요.

carbon (7).png 개선을 완료한 전체 코드예요.




개선으로 얻은 실질적인 이점


코드 개선을 통해 어떤 실질적인 이점을 얻게 되었을까요?


1. 메모리 효율성 향상


실제 테스트 결과, 8MB 크기의 고해상도 이미지 처리 시 기존 방식 대비 약 40% 메모리 사용량이 감소했어요! 특히 대용량 이미지를 처리할 때의 블로킹이 개선되었어요.


2. 코드 품질 향상


메소드가 단일 책임을 갖도록 재구성했어요. 이를 통해 코드의 가독성과 유지보수성이 크게 향상되었어요.


3. 성능 개선


기존의 선형적인 압축 알고리즘 대신 이분탐색 알고리즘을 적용한 덕분에 이미지 처리 속도가 약 20~30% 개선되었어요. 특히 큰 이미지의 디코딩 및 리사이징 속도가 눈에 띄게 향상되었습니다!





프로젝트에서 ImageDecoder 도입 시 고려사항


하지만 모두 좋을 수는 없겠죠?

시스템에 ImageDecoder를 도입할 때 고려해야 할 몇 가지 사항이 있어요.



1. 하위 호환성 확보하기


API 28(Android P) 미만 기기를 지원해야 한다면, 버전에 따라 적절한 처리가 필요해요.

carbon (8).png API 버전에 따라 처리하는 코드예요.


2. 성능 테스트 실시하기


다양한 크기와 유형의 이미지로 테스트해 실제 성능 이점을 확인해야 해요. CPU성능이 좋지 않거나 RAM 메모리가 제한적인 기기에서는 연산이 오래걸리거나 제대로 수행되지 않을 수가 있어요.



메모리 사용량 (Android Profiler)

처리 시간 측정

다양한 이미지 포맷 처리 결과

다양한 기기에서의 동작 확인



3. 예외 처리 강화하기


이미지 처리 과정에서 발생할 수 있는 모든 예외 상황을 고려해 안정적인 코드를 작성해야 해요.




마치며


이번 36기에 합류하게 되면서 레거시 코드를 개선한 경험은 단순히 새 API를 사용하는 것 이상의 의미가 있었어요. 내가 가진 지식이 프로덕트에 기여된다는 사실이 뿌듯했고, 오너십있게 프로덕트를 다루는 자세를 가지게 되었어요.


여러분의 프로젝트에도 이미지 처리, 특히 API 28 이상을 타겟팅하고 있다면 ImageDecoder로의 전환을 고려해보세요. 더 깔끔하고 효율적인 코드를 작성할 수 있을 거예요!


앱의 성능과 사용자 경험을 개선하는 여정에서 이런 기술적 개선은 단순한 리팩토링을 넘어 앱 전체의 품질을 한 단계 끌어올리는 중요한 요소라고 생각해요 :)


긴 글 읽어주셔서 감사합니다.




SOPT makers의 APP Android 코드는 오픈 소스로 공개되어 있어요.

자세한 내용은 아래 GitHub 레포지토리에서 확인하실 수 있어요.


SOPT makers APP Android 레포지토리 바로가기


written by. 한민재 | 36기 | APP팀 | Android
designed by. 이성오 | 36기 | APP팀, Team Blog TF팀 | Product Designer
edited by. 강윤서 | 36기 | APP팀, Team Blog TF팀 | iOS




참고 자료


ImageDecoder 공식 문서

Android 이미지 로딩 최적화 가이드

ImageDecoder.OnHeaderDecodedListener

ImageDecoder.Source

Android 메모리 및 성능 관리 가이드



작가의 이전글SOPT makers는 어떤 프로덕트를 만드나요?