[안드로이드 UI최적화] 리소스 관리


안드로이드 기기는 해상도와 인치가 모두 다르게 출시 된다. 현재 구글 플레이스토어 퍼블리싱 사이트에 등록된 알려진 기기만 3,600대가 넘는다. 모든 기기에서 애플리케이션이 실행되기 위해서는 안드로이드 SDK에서 제공되는 개발 툴과 리소스의 관리 구조를 잘 파악해야 한다. 이번 장에서는 안드로이드가 내부적으로 리소스를 어떻게 관리 하는지에 대해 알아보겠다. 안드로이드 리소스 관리 프레임워크는 개발적인 요소와 디자인 요소가 복합적으로 제공하기 때문에 개발자 뿐만 아니라 디자이너도 반드시 알고 있어야 할 부분이며 이에 대해 정리해본다.

 

 

1.1 안드로이드 리소스 사용법

다양한 리소스를 선택적으로 사용할 수 있도록  안드로이드 운영체제에서 제공한다. 애플리케이션 실행시 기기의 환경에 가장 밀접한 리소스를 선택적으로 사용 할 수 있다. 예를 들면, 기기가 사용하는 언어에 따라 문자열 리소스를 읽어 올 수 있거나, 가로 모드 또는 세로 모드일때 레이아웃 리소스를 다르게 사용 할 수 있다. 이렇게 다양한 리소스 타입은 어떤 안드로이드 애플리케이션이든 사용 가능하다. 다양한 리소스를 사용하기 위해서는 안드로이드가 운영체제가 관리하는 규칙을 따라야 한다. 기본적으로 리소스가 관리되는 곳은 애플리케이션 프로젝트 내에 res/ 하위에 디렉터리에 위치하며 XML 문서로 이루어진다.

 

주요 리소스 항목

 

  • Drawable 정의. 비트맵 또는 XML로 선언한 Drawable (res/drawable)
  • 레이아웃 정의 (res/layout)
  • 애니메이션 정의 (res/ani)
  • 컬러값 정의 (res/color)
  • 메뉴 정의 (res/menu)
  • 그 외 문자열, 색상 정보, 각종 단위 (res/values)

 

 

1.2 리소스 수식어

 

안드로이드 운영체제는 기기에 따라 대체 할만한 리소스가 있는지 자동적으로 결정 할 수 없다.  그렇기 때문에 기기 환경에 따라 사용할 리소스를 정의해야 한다. 미리 정의된 수식어로 리소스 디렉터리들을 확장해 다양한 기기의 환경을 구분 할 수 있다. 리소스 폴더 이름 뒤에 수식어를 붙이면 되는데 수식어 사이에는 대시(-) 를 사용하게 된다. 예를 들어, res/layout-land 라는 것은 기기가 가로 모드로 전환되거나 실행 될 때 여기에 위치한 레이아웃을 읽게 된다.

 

주요 수식어

 

  • 가로모드

“land” 수식어를 통해 가로모드 분기한다.

 

  • 안드로이드 SDK 버전

“v<SDK 버전 코드>” 수식어를 통해 SDK 버전 분기한다.

 

  • 해상도

“<세로 px사이즈>x<가로 px사이즈>” 수식어를 통해 가로세로 px사이즈보다 크거나 같은 경우 분기된다.

 

  • 스크린 타입

“<스크린 타입>” 수식어를 통해 분기한다.

 

  • 최소 DP 사이즈(안드로이드 OS 3.2)

“sw<dp사이즈>dp” 수식어를 통해 단말의 가로와 세로 dp크기가 같거나 큰 경우 분기된다.

 

  • 가로 DP 사이즈(안드로이드 OS 3.2)

“w<가로 dp사이즈>dp” 수식어를 통해 단말의 가로 dp크기가 같거나 큰 경우 분기된다.

  • 세로 DP사이즈(안드로이드 OS 3.2)

“h<세로 dp사이즈>dp” 수식어를 통해 단말의 세로 dp크기가 같거나 큰 경우 분기된다.

 

  • 언어

“국가 코드” 수식어를 통해 단말에 해당되는 언어로 분기된다.

수식어를 통해 리소스를 분기 할 수 있는 것은 다양한 기기지원을 위한 안드로이드만의 강점이다. 주의할 점은 모든 기기에 맞는 리소스를 제공 할 필요는 없다. 모든 기기에 맞는 리소스를 제공하게 되면 복잡도가 높아져 유지보수시 어려움이 생기게 된다. 테블릿을 위한 새로운 레이아웃을 제공한다던가, 가로모드를 위한 새로운 레이아웃을 변경한다던가 특정언어를 반드시 지원해야하는 상황등 특별한 경우를 제외하고는 기본 폴더를 바탕으로 작업을 하면된다.

 

 

수식어 예제

 

res/drawable-sw720dp: 해상도의 가로세로 DP >= 720일때 이미지를 분기한다.

res/values-ko: 단말의 사용되는 언어가 한국어일 때 분기한다.

 

 

1.3 리소스 매치 방법

 

애플리케이션 실행시 수 많은 수식어를 가진 리소디렉터리들리중 가장 최적의 리소스를 찾을 것이다. 이는 리소스 매치 알고리즘을 통해 하나하나 제거하는 방법으로 최종적으로 하나가 남게 되는 방식이다. 대부분 리소스 분기는 다양한 해상도를 처리하기 위해 분기를 하는데 주의 해야 될 점을 간단한 예제를 통해서 알아보자.

 

레이아웃:

res/layout/

res/layout-800×480

res/layout-1280×800

res/layout-1920×1080

 

기기 설정 정보:

화면 해상도 1024×768

실제로 이런 식의 작업은 정말 비 효율 적이나 예를 들어 다음과 같이 수식어를 통해 해상도를 분기했다고 가정해보자. 기기의 화면 해상도는 1024×768이기 때문에 최적의 해상도가 없어 리소스 정책에 의해 현재 화면보다 작은 res/layout-800×480의 리소스를 사용하게 된다. 이런 경우 전혀 원하지 않는 리소스를 사용하기 때문에 문제가 된다. 이렇게 화면 크기와 관련해서는 분기 처리를 해야 될 필요가 있다. 좀 더 상세한 알고리즘은 안드로이드 문서(http://developer.android.com/guide/topics/resources/providing-resources.html#BestMatch)를 참고하기 바란다.

 

 

1.4 수식 조합

 

하나의 수식어보다 구체적인 분기를 위해서는 두개 이상의 조합이 필요한 경우가 많다. 안드로이드 플랫폼에서는 개발자가 임의의 방법으로 수식어를 조합 하도록 지원한다. 조합을 통해서 적절하게 사용 할 수 있지만, 잘못된 사용으로 큰 문제를 발생하기도 한다. 예를 들어 가로, 세로모드와 해상도의 밀도에 따라 사용하는 이미지가 잘라질 수 있다. 그렇기 때문에 res/drawable-land-hdpi, res/drawable-land-mdpi / res/drawable-port-hdpi, res/drawable-port-mdpi 이렇게 조합으로 분기할 수 있다.

 

수식어 조합 예제

 

  • res/layout-land-hdip-v11

가로모드이고 HDPI 단말이며 안드로이드 SDK 버전 11 보다 큰 경우에 레이아웃을 분기한다.

 

  • res/layout-large-1080×720

Large 스크린 사이즈를 가졌으며 해상도가 가로 1080px 세로 720px보다 같거나 클때 분기한다.

 

 

1.5 안드로이드와 화면 밀도

 

작은 화면에 최적화된 사용자 경험을 보여주어야 하는 스마트폰의 특성으로 화면의 밀도와는 연관성이 높다. 화면 밀도는 같은 화면 사이즈에 대해서 얼마나 많은 정보를 표현 하는지에 따라 밀도가 높고 낮을 수 있다.  예를 들어, 일반적으로 사용하는 컴퓨터 모니터가 있다. 동일한 크기를 가진 20인치 모니터가 있는데, 하나는 1024×768 해상도를 가졌으며 또 다른 하나는 1920×1280 해상도를 가졌다고 생각 해보자. 그러면 같은 인치이지만 1024×768 해상도를 가진 모니터가 더 크게 보일 것이다. 같은 크기의 모니터이지만 정보를 표현하는 크기가 다르기 때문에 이런 차이를 보이게 된다. 즉 1024×768 해상도는 화면 밀도가 낮고, 1920×1280 해상도는 밀도가 높다.  이렇게 대형 모니터에서는 해상도가 크고, 창을 분리해서 작업 하는 등 다양하게 사용자들이 설정을 통해 사용 가능하기 때문에 문제가 되지 않는다.

대형 모니터와는 다르게 작은 화면을 가진 스마트폰에서는 화면크기와 해상도 크기로 인해 어떤점이 문제가 되는지 간단한 예를 들어보자. 아래와 같이 동일한 화면 크기를 가졌지만 해상도가 다른 2개의 스마트폰이 있다고 가정하자. 이 2개의 폰에 6×6픽셀로 이루어진 이미지를 표시하면 2개의 물리적인 크기는 다르게 표시된다. 이유는 B스마트폰의 해상도가 A스마트폰에 비해 더 크기 때문이다.

 

151
스마트폰은 대부분 터치스크린으로 UI컨포넌트를 통해 애플리케이션을 제어하게된다. UI컨포넌트가 해상도에 따라 크기가 바뀌게 되면 사용성이 떨어진다. 예제 1.5.1 에서 보듯이 A스마트폰에서는 UI컨포넌트가 잘보이고 잘 눌렸지만 단지 해상도크기의 변경으로 B스마트폰에서는 더 작아져 UI컨포넌트를 누르기 힘들어진다. 이런 문제는 스마트폰을 사용하는 사용자에게 매우 불편함을 초래 한다.<예제 1.5.1> 스마트폰에서 인치와 해상도의 따른 문제점

같은 화면 크기를 가졌을때 해상도와 관계 없이 물리적으로 같은 크기의 이미지를 표시하는 방법으로 이러한 문제점을 해결 할 수 있다. 예제 1.5.1 B스마트폰에 이미지를 그릴때 A스마트폰에 해상도가 2배이기 때문에 6×6크기의 이미지를 그리는 것이 아닌 12×12크기의  이미지를 그리게 되면 물리적으로 같은 크기가 된다. 예제 1.5.2에서 다른 해상도를 가졌지만 A스마트폰과 B스마트폰의 이미지는 동일한 물리적인 크기를 보이게 된다.

전혀 다른 해상도와 화면 크기를 가졌더라도 동일한 물리적인 크기의 UI컨포넌트를 보여주는 것이 애플리케이션 사용자에게 사용성을 높일 수 있는 최선의 방법이다.

 

152
안드로이드에서의 해상도 단위<예제 1.5.2> 스케일링을 통한 이미지 표시

 

안드로이드에서는 스마트폰의 다양한 화면크기와 해상도를 지원하게 하기 위해 DPI(Dots Per Inch)라는 단위로 기준을 잡게된다. DPI는 인치당 도트(가상의 크기)를 몇개를 가지는지에 대한 단위이다. 예를 들어 100DPI라는 말은 1인치에 100개의 도트가 있다는 것이다. DPI수치가 높을 수록 1인치당 표현될 수 있는 도트가 많으며, 보다 선명해진다. DPI는 기기 제조사가 제품 개발시 파편화되는 점을 방지하기위해 반드시 그룹화된 단위를 사용하기위해 구글에서는 가이드라인을 제공한다. 이런 그룹화된 DPI단위를 사용하면 화면 크기와 해상도와 관계 없이 물리적으로 동일한 크기를 가지게 안드로이드 SDK를 통해 지원한다.

 

DPI 이름 밀도 주요기기
low density ldpi 120dpi HTC 디자이어팝
medium density mdpi 160dpi LG 옵티머스 원
high density hdpi 240dpi 삼성 갤럭시 S1, S2
TV density tvdpi 213dpi A 720p TV
Extra high density xhdpi 320dpi 삼성 갤럭시 S3
Extra Extra high density xxhdpi 480dpi 삼성 갤럭시 S4, 넥서스 5
Extra Extra Extra high density xxxhdpi 640dpi _
non-scaled images nodpi _ _

 

<표 1.5.1> 안드로이드 일반적인 밀도 단위

 

기기 제조사는 인치 당 표현되는 픽셀의 갯수인 PPI라는 단위를 참고하여 DPI를 선정하게 된다. 예를 들어 넥서스 5는 4.95인치에 1920×1080해상도를 가졌기 때문에 인치 당 표현되는 픽셀의 갯수는 445PPI이다.

DPI를 선정하기 위해 455PPI와 비슷한 수치인 480DPI를 선정하게 된다. 대부분 제조사의 경우 DPI 가이드라인을 지키지만, 이런 가이드라인이 나오기 전 생산된 갤럭시 탭1의 경우에는 지키지 않아 물리적으로 동일한 크기가 맞지 않는 문제점이 있을 수 있다.

 

 

리소스 분할과 자동스케일링

 

안드로이드에서는 이미지관련된 리소스는 별도의 화면에 따른 리소스를 등록하지 않으면 기본적으로 기기의 해상도에 맞도록 자동으로 크기를 조정하여 보여준다. 이렇게 이미지를 자동스케일링하게 되는 경우 어떤 경우에는 원하는 결과물이 나올 수도 있지만 심한 경우 이미지가 늘어서 흐려지는 경우가 있을 수도 있다. 예를 들면 hdpi를 기준 이미지로 잡으면  xxhdpi단말에 표시하게 되는 경우 xxhdpi이미지리소스를 별도로 등록 하지 않았으면 hdpi이미지를 xxhdpi단말에 동일한 크기로 보여주기 위해 이미지를 늘리게 된다. 이런 문제를 흐려지는 문제를 감안하지 않고 싶다면, xxhdpi이미지를 등록하면 각각의 기기에대한 최적의 이미지를 사용할 수 있다. 하지만 이렇게 다양한 이미지리소스를 만들거나 관리하는데도 많은 문제점이 있다. 그렇기 때문에 자동스케일과 리소스분할을 상황에 맞도록 적절한 선택이 필요하다.

기준이 되는 적절한 선택 방법은 현재기준으로 가장 많이 사용되는 해상도를 선택하면 된다. 안드로이드의 개발사이트(http://developer.android.com/about/dashboards/index.html#Screens)를 통해 현재 사용되는 기기들의 해상도 정보 비율을 볼 수 있다. 지금 글을 작성 하는 시점에 hdpi가 33.3%, xhdpi 20.2%의 점유율을 보이고 있다. 앞으로 hdpi는 점점 줄어 들고 xhdpi와 xxhdpi가 늘어나는 추세 이기 때문에 xhdpi의 기준으로 선택하는 것도 하나의 방법이다.

 

 

1.6 기기에 독립적인 픽셀(DP)

전혀 다른 해상도에서 UI컨포넌트를 동일한 크기로 보여주는 것이 스마트폰에서는 중요하다고 1.5에서 설명을 했다. 이미지리소스는 자동 스케일링을 통해서 어떠한 해상도에서 동일한 크기를 표현 할 수 있지만, 레이아웃을 생성하거나 구성요소의 크기를 정의할때 기기 해상도에 관계 없이 독립적으로 원하는 크기를 사용할 필요가 있다. 예를 들어 UI컨포넌트에 픽셀의 크기를 줄 수 있지만 각각의 해상도에 따라 전혀 다른 크기가 나타나는 문제점이 발생한다.

안드로이드 SDK에서는 이런 문제를 해결 하기위해서 DP(DPI)이라는 단위를 제공한다. 안드로이드 애플리케이션 실행시 실행되는 기기의 해상도에 따라 DP를 픽셀로 스케일링된다. 이는 해상도에따라 달라지는 것이 아닌 독립적인 크기를 나타낼 수 있는 방법이다. 쉽게 말해 어떠한 해상도와 인치를 가져도 물리적으로 같은 크기를 보여준다. 픽셀은 해상도나 인치에 따라 물리적으로 다양한 크기를 보이게 되는데 이와는 대조적으로 같은 DP에 대해서는 물리적으로 같은 크기를 보여준다.

처음 DP개념을 접하는 경우 어렵지만 MDPI를 가진 화면에서 1DP = 1픽셀과 같다는 사실을 기억하면 좀 더 쉽게 이해가 될 것이다. 좀 더 쉽게 100DP에 대해서  각각의 DPI에 따른 픽셀 변환 하면 다음과 같다.

 

DPI 비율 픽셀
ldpi (120dpi) 0.75x 75px
mdpi (160dpi) 1x (기본 비율) 100px
hdpi (240dpi) 1.5x 150px
xhdpi (320dpi) 2x 200px
xxhdpi (480dpi) 3x 300px
xxxhdpi (640dpi) 4x 400px

 

<표 1.6.1> 100DP를 픽셀로 변환한 예제

MDPI에서는 1픽셀과 DP는 동일하기 때문에 100DP는 100픽셀이 된다. XHDPI에서는 기본비율의 2배이기때문에 200픽셀로 변환된다.

 

 

DP를 픽셀로 변환

 

레이아웃 구성시 DP단위를 사용하게 되는데 안드로이드 SDK에서 어떻게 DP를 픽셀로 변화하는지에 대한 코드를 통해서 확인 해보자. 현재기기의 해상도 정보를 가지고 있는 DisplayMetrics를 통해 현재 단말의 DPI정보로 DP사이즈를 픽셀로 변환 하는 코드를 작성할 수 있다. 픽셀은 물리적인 크기로 0.5픽셀을 표현하지 못하기 때문에 반올림 해주기 위해 0.5를 더한 후 정수형으로 캐스팅 한다.

 

<코드 1.6.1>

private int convertToPixels(float dp){

  DisplayMetrics metrics = getResources().getDisplayMetrics();

  int densityDpi = metrics.densityDpi;

  int pixels = (int) (dp  * densityDpi + 0.5f);

  return pixels;

}

 

코드를 통해서 다음과 같은 변환 공식을 세울 수 있으며 몇 가지 예를 들어 보자.

 

DP * ((단말의 DPI) / 160(기본비율)) = 픽셀

 

  • 예제1. xhdpi(320)에서의 50DP를 픽셀로 변환

50dp * (320/160) = 100px

 

  • 예제2. hdpi(240)에서의 120DP를 픽셀로 변환

120dp * (240/160) = 180px