안드로이드 앱에서 이미지를 보여주는 화면이 많다. 서버로부터 받은 url 이미지를 보여줘야 할 때도 있고, local에 저장해뒀다가 보여줘야 하는 경우도 있다. 오늘 이야기할 내용은 Glide를 사용해서 SVG 이미지를 화면에 보여주는 과정에서 내가 겪은 삽집을 풀도록 하겠다.

먼저, SVG 이미지가 무엇인지 알아보자.

SVG VS PNG

SVG는 Scalable Vector Graphics의 약자이다.
JPEG, PNG처럼 그래픽 포맷 중 하나이다. SVG는 벡터 기반으로 리사이징이 되어도 깨지지 않는다는 장점을 가지고 있다. 즉, 모든 해상도에서 자유자재로 활용할 수 있기 때문에 특정 해상도에 제한되어 있지 않다.

장점

  1. 특정 사이즈에 구애받지 않는다.

즉, 위에서 언급했듯이 어느 해상도에서든 pixelate 되지 않는다. SVG는 어떤 사이즈로든 그 모습 그대로 유지된다. 그렇기 때문에 사이즈별로 아이콘을 모두 생성해서 개발자에게 넘겨줄 필요가 없다. SVG 파일 하나로 모든 해상도를 대응할 수 있다.

  1. 작은 파일 사이즈

비트맵 이미지(PNG, JPEG) 같은 경우 파일 크기를 결정하는 주요 요소는 해상도이다. 예를 들어 5000x5000 픽셀 이미지는 항상 500x500보다 파일 사이즈가 크다.
반면, SVG 그래픽 같은 경우 파일의 크기를 결정하는 주요 요소는 바로 복잡도이다. Path가 비교적 적은 간단한 이미지는 PNG, JPEG 보다 파일 사이즈가 적을 수도 있지만 이미지를 구성하는 요소의 복잡도(레이어가 많다든지 특정 효과가 많다드지)에 따라서 파일 사이즈가 커진다. 하지만, 이런 용량 문제는 SVG Optimizing이라는 것을 하게 되면 어느 정도 해결이 된다. 작은 파일 사이즈로 인해 로딩 시간도 줄어든다는 장점 또한 있다.

Bitmap과 SVG 구성요소

  • 비트맵 그래픽 : Raster Graphics(픽셀 기반)

대표적인 포맷은 JPEG, PNG이다. 이들은 픽셀로 구성되어 있다. 예를 들어, 2x2 픽셀인 비트맵 이미지는 총 4px로 구성되어 있다. 개개인에 대한 픽셀들은 자유자재로 바꿀 수가 없고 움직일 수도 없다. 그렇기 때문에 100% 이상으로 이미지를 확대하면 Pixelate가 된다.

  • SVG 그래픽 : 벡터 기반

픽셀로 구성되어 있지 않고 작업하고 있는 그래픽에 대한 정보로 구성되어 있다. 그렇기 때문에 어떤 사이즈로든 자유자재로 늘어나는 것이 가능하다. 이러한 이유로 인해 코드로 쉽게 적용된 스타일을 수정할 수 있다. 예를 들어 동그라미의 보더 값을 6에서 8로 바꾼다던지 색상을 그레이에서 블랙으로 바꾼다던지 또는 사이즈를 변경한다던지 등이다.

위와 같은 장점이 있기 때문에 현재 진행하고 있는 프로젝트에서 PNG보다는 SVG를 채택해서 사용하려고 노력 중이다.

디자이너가 주는 이미지는 일러스트로 그리거나 어떤 작업을 거친 후에 주는 것이 많다. 이러한 이미지들은 대부분 용량이 크다.

용량이 큰 이미지를 내가 사용하는 imageView에 그냥 세팅을 해서 보여준다면 OOM(Out Of Memory)를 겪게 될 것이다. 이미지의 용량이 커서 사용하고 있는 메모리에 로드할 수 없기 때문이다.

이 문제를 해결하는 방법은 여러 가지가 있는데, 그 중에 나는 편리하게 처리하기 위해서 Glide 라이브러리르 사용했다. 이미지 로드를 하기 위한 라이브러리는 이외에도 Picasso, Fresco 등이 있다. 나는 이 중 Glide가 사용 경험도 있고 편해서 선택을 했다.

이미지 로딩 라이브러리 중 Picasso와 Glide 비교에 관한 글이다. [안드로이드] Image Loading Library

Glide

이미지 로딩 라이브러리 중 하나로써 Glide는 많은 개발자들이 사용한다. 이유는 사용법이 간편하고 다양한 기능을 지원하기 때문이다.

기본 사용법은 다음과 같다.

1
2
3
4
5
fun setImageBackground(){
Glide.with(context)
.load(R.drawable.image_error)
.into(imageView)
}

이제 내가 원하는 svg 이미지를 glide를 통해 적용해보려고 한다. 나는 png나 svg나 어차피 이미지이기 때문에 똑같은 방식으로 적용하면 될 것이라고 생각했다.

인생은 쉽지 않다. 쉽게 될 것이라고 생각했던 것은 큰 오산이었다… 그래서 구글의 힘을 빌려보기로 했다.
android glide load svg로 검색한 결과 다음과 같이 스택 오버플로우에 많은 질문이 있는 것을 알 수 있었다. 역시 쉬운 것은 없다.

열심히 구글링을 하던 중 괜찮아 보이는 방법을 찾았다.

  1. 라이브러리 사용
  • GlideToVectorYou 라는 라이브러리이며 SVG 이미지를 쉽게 로드할 수 있다.
  • 구현부분을 자세히 보면 구글의 샘플 코드를 확인할 수 있다.
  • 단점 : with() 함수의 인자로 activity 밖에 넘기지 못한다.
  1. 구글의 샘플 코드 사용(구 버전)
  • 스택 오버플로우의 답변 내용을 보면 구글의 샘플 코드를 확인하라는 말을 자주 볼 수 있다.
  • 그래서 링크를 타고 들어가봤는데, 대략 4년 전에 사용되었던 방식이 있다.
  • 단점 : 구 버전이므로 현재 버전에서는 Deprecated된 함수들이 대부분이다.
  1. 구글의 샘플 코드 사용(최신 버전)
  • 구 버전 뿐 아니라 최신 버전에 맞게 사용할 수 있는 샘플 코드를 제공하고 있다.
  • SVG Sample

자, ‘이런게 삽질이라고?’ 라고 생각하시는 분들이 있을 수 있다.
하지만, 내가 겪은 과정은 삽질이라고 표현할 수 있다.
이유는 처음에 SVG 이미지를 그냥 로드할 수 없다는 걸 알게되었다는 점과 이를 로드하기 위해서 어떤 방식이 있는지 찾아보는 과정, 그리고 실제로 동작하는지 확인해보기 위해서 많은 시간을 들였다.

구글의 샘플 코드를 보고 가장 먼저 의문이 드는 것은 GlideApp이다. 이게 뭘까? 직접 쳐봐도 뜨지도 않는다. 그럼 구글은 이 뜨지도 않는걸로 어떻게 해결한 것일까??

해결 방법은 의외로 간단하다. build.gradle(Module:app)에 아래와 같은 의존성을 추가하면 된다.

1
2
3
4
5
// glide
implementation 'com.github.bumptech.glide:glide:4.8.0'
implementation 'com.github.bumptech.glide:annotations:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
kapt 'com.github.bumptech.glide:compiler:4.8.0'

버전은 4.8.0으로 했다. 최신 버전으로 했었는데, 변경하는 과정에서 Crash가 발생해서 4.8.0으로 하는 것이니 사용할 때는 최신 버전으로 해도 무방할 것 같다.

그리고 Clean Project -> Rebuild Project 를 하면 GlideApp이 생기는 걸 확인할 수 있다. 다음에 AppGlideModule을 상속받는 MyAppGlideModule 클래스를 생성하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
@GlideModule
class MyGlideModule : AppGlideModule() {

override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
registry
.register(SVG::class.java, PictureDrawable::class.java, SvgDrawableTranscoder())
.append(InputStream::class.java, SVG::class.java, SvgDecoder())
}

override fun isManifestParsingEnabled(): Boolean {
return false
}
}

SVG 클래스를 위해서 아래와 같은 의존성을 하나 더 추가해줘야 한다.

1
implementation 'com.caverock:androidsvg:1.2.1'

그리고 여기서 사용되는 클래스들은 다음과 같다.

1.SvgDecoder

link를 통해서 읽은 inputStream을 SVG 내부 표현으로 디코딩을 한다는 의미이다.
정확하지는 않지만 link 즉, url을 통해 읽어들인 다음 inputStream에 담고 이를 통해 SVG로 만든다. 만들 때는 androidsvg 라이브러리를 사용한다.

2. SvgDrawableTranscoder

1번에서 만든 SVG를 안드로이드에서 사용할 수 있는 PictureDrawable로 변환한다. 이는 Drawable 클래스를 상속받고 있기 때문에 이렇게 변환된 데이터를 안드로이드에서 사용할 수 있다.

3. SvgSoftwareLayerSetter

3번은 필수가 아닌 선택이다. 하지만, 필수적으로 사용해야 될 것 같다. 왜냐하면 리스너를 구현한 것이기 때문에 이를 통해서 내가 로드하려는 이미지가 성공적으로 로드되었는지, 실패했는지를 확인할 수 있다.

이를 통해서 내가 로드하려는 SVG 이미지가 성공적으로 로드되었는지, 로드되지 않고 실패했다면 어떤 원인인지 파악할 수 있어 디버깅하는데 더 수월하다.

여기까지 내가 삽질했던 과정을 설명했다. 사용되는 클래스에 대한 정확한 파악은 하지 못했지만, 어떻게 동작하는지는 이해가 되었다. 나의 이해를 바탕으로 작성한 글이기 때문에 사실과 다른 부분도 존재할 것이다. 이 부분에 대한 이견이 있다면 알려주시면 감사할 것 같다.

참고