Skip to content

minsoopark/android-best-practices-kor

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Android ๊ฐœ๋ฐœ ๋ชจ๋ฒ” ์‚ฌ๋ก€

๋‹ค์Œ์€ Futurice์˜ Android ๊ฐœ๋ฐœ์ž๋“ค๋กœ๋ถ€ํ„ฐ ํ•™์Šตํ•œ ๋‚ด์šฉ๋“ค์ด๋‹ค. ์ด ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉด์„œ ์ด๋ฏธ ํ–ˆ๋˜ ์ž‘์—…์„ ๋˜ํ’€์ด(Reinventing the wheel)ํ•˜์ง€ ์•Š๋„๋ก ํ•˜์ž. iOS๋‚˜ Windows Phone ๊ฐœ๋ฐœ์—๋„ ๊ด€์‹ฌ์ด ์žˆ๋‹ค๋ฉด, iOS Good Practices ํ˜น์€ Windows App Development Best Practices ๋ฌธ์„œ๋“ค๋„ ํ™•์ธํ•ด๋ณด์ž.

Android Arsenal

์š”์•ฝ

Gradle์„ ์‚ฌ์šฉํ•˜์ž. ์ด๋Š” ๊ถŒ์žฅ๋˜๋Š” ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์ด๋‹ค.

gradle.properties์— ๋น„๋ฐ€๋ฒˆํ˜ธ๋‚˜ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ๋“ค์„ ๋„ฃ์–ด๋‘์ž.

HTTP ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ๋ง๊ณ , Volley๋‚˜ OkHttp ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์‚ฌ์šฉํ•˜์ž.

JSON ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ๋ฐ์—๋Š” Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ž.

65,000 ๋ฉ”์†Œ๋“œ ์ˆ˜ ์ œํ•œ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Guava๋Š” ํ”ผํ•˜๊ณ  ๋ช‡ ๊ฐ€์ง€์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค๋งŒ์„ ์‚ฌ์šฉํ•˜์ž.

UI ํ™”๋ฉด์„ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ์— Fragment๋“ค์„ ์‚ฌ์šฉํ•˜์ž.

Fragment๋“ค์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์€ Activity๋“ค์ด ๋งก๋„๋ก ํ•˜์ž.

๋ ˆ์ด์•„์›ƒ XML๋“ค ๋˜ํ•œ ์ฝ”๋“œ์ด๋‹ค. ๊ทธ ๊ฒƒ๋“ค์„ ์ž˜ ๊ด€๋ฆฌํ•˜์ž.

๋ ˆ์ด์•„์›ƒ XML์—์„œ ์ค‘๋ณต๋œ ์†์„ฑ๋“ค์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด style์„ ์‚ฌ์šฉํ•˜์ž.

ํ•˜๋‚˜์˜ style์ด ๋ฐฉ๋Œ€ํ•ด์ง€๋Š” ๊ฒƒ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์—ฌ๋Ÿฌ๊ฐ€์ง€ style ํŒŒ์ผ๋“ค์„ ์‚ฌ์šฉํ•˜์ž.

colors.xml์„ ์งง๊ฒŒ, ์ค‘๋ณต ์—†์ด ์œ ์ง€ํ•˜๊ณ , ํŒ”๋ ›ํŠธ์ฒ˜๋Ÿผ ์ •์˜ํ•ด๋‘์ž.

dimens.xml ๋˜ํ•œ ์ค‘๋ณต ์—†์ด, ์ผ๋ฐ˜ ์ƒ์ˆ˜๋กœ ์ •์˜ํ•˜์ž.

ViewGroup์— ๊ณ„์ธต์„ ๊นŠ๊ฒŒ ํ˜•์„ฑํ•˜์ง€ ์•Š๋„๋ก ํ•˜์ž.

WebView์— ํด๋ผ์ด์–ธํŠธ ์ธก ํ”„๋กœ์„ธ์‹ฑ์„ ํ”ผํ•˜๊ณ , ์—ฌ๋Ÿฌ ๋ˆ„์ˆ˜๋“ค์— ์œ ์˜ํ•˜์ž.

์œ ๋‹› ํ…Œ์ŠคํŠธ์—๋Š” Robolectric๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , UI ํ…Œ์ŠคํŠธ์—๋Š” Robotium์„ ์‚ฌ์šฉํ•˜์ž.

์—๋ฎฌ๋ ˆ์ดํ„ฐ๋กœ๋Š” Genymotion๋ฅผ ์‚ฌ์šฉํ•˜์ž.

ํ•ญ์ƒ ProGuard ํ˜น์€ DexGuard๋ฅผ ์‚ฌ์šฉํ•˜์ž.


Android SDK

Android SDK๋ฅผ ํ™ˆ ๋””๋ ‰ํ† ๋ฆฌ๋‚˜ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋…๋ฆฝ์ ์ธ ์œ„์น˜์— ๋‘์ž. ๋ช‡๋ช‡ IDE๋“ค์€ ์„ค์น˜์‹œ์— SDK๋ฅผ ํ•ด๋‹น IDE์™€ ๊ฐ™์€ ๊ฒฝ๋กœ์— ํฌํ•จ์‹œํ‚ค๋Š”๋ฐ, ์ด๋Š” IDE๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œ(ํ˜น์€ ์žฌ์„ค์น˜)ํ•˜๊ฑฐ๋‚˜ IDE๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ๋ถˆํŽธํ•˜๋‹ค. ๋˜ํ•œ IDE๊ฐ€ root ์•„๋ž˜์— ์žˆ์ง€ ์•Š๊ณ  user ์•„๋ž˜์—์„œ ๋™์ž‘ํ•  ๊ฒฝ์šฐ, SDK๋ฅผ sudo ๊ถŒํ•œ์„ ์š”๊ตฌํ•˜๋Š” ์‹œ์Šคํ…œ ๋ ˆ๋ฒจ์˜ ๋””๋ ‰ํ† ๋ฆฌ์— ๋‘์ง€ ์•Š๋„๋ก ํ•˜์ž.

๋นŒ๋“œ ์‹œ์Šคํ…œ

๊ธฐ๋ณธ ์˜ต์…˜์€ Gradle์ด๋‹ค. Ant๋Š” ์ƒ๋‹นํžˆ ์ œํ•œ์ ์ด๊ณ  ๋‚ด์šฉ์ด ์žฅํ™ฉํ•˜๋‹ค. Gradle์„ ์‚ฌ์šฉํ•˜๋ฉด, ๋‹ค์Œ ํ•ญ๋ชฉ๋“ค์ด ๊ฐ„๋‹จํ•ด์ง„๋‹ค.

  • ์•ฑ์˜ ๊ฐ๊ธฐ ๋‹ค๋ฅธ Flavor๋“ค๊ณผ Varient๋“ค์„ ๋นŒ๋“œํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Task๋“ค์„ ๊ฐ„๋‹จํ•œ ์Šคํฌ๋ฆฝํŠธ์ฒ˜๋Ÿผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.
  • ์—ฌ๋Ÿฌ Dependency๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ  ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Keystore๋“ค์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ฆˆํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ธฐํƒ€ ๋“ฑ๋“ฑ

Android์˜ Gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ƒˆ๋กœ์šด ํ‘œ์ค€ ๋นŒ๋“œ ์‹œ์Šคํ…œ์œผ๋กœ์„œ ๊ตฌ๊ธ€์— ์˜ํ•ด ํ™œ๋ฐœํ•˜๊ฒŒ ๊ฐœ๋ฐœ๋˜๊ณ  ์žˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

๋‘ ๊ฐ€์ง€ ๋งŽ์ด ์“ฐ์ด๋Š” ์˜ต์…˜๋“ค์ด ์žˆ๋‹ค: ๋‚ก์€ Ant & Eclipse ADT ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ, ์ƒˆ๋กœ์šด Gradle & Android Studio ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•˜์ž. ๋งŒ์•ฝ ๋‚ก์€ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ๋ ˆ๊ฑฐ์‹œ๋กœ ํŒ๋‹จํ•˜๊ณ  ์ƒˆ๋กœ์šด ๊ตฌ์กฐ๋กœ ํฌํŒ…ํ•˜๋Š” ์ž‘์—…์„ ์‹œ์ž‘ํ•˜์ž.

Old structure:

old-structure
โ”œโ”€ assets
โ”œโ”€ libs
โ”œโ”€ res
โ”œโ”€ src
โ”‚  โ””โ”€ com/futurice/project
โ”œโ”€ AndroidManifest.xml
โ”œโ”€ build.gradle
โ”œโ”€ project.properties
โ””โ”€ proguard-rules.pro

New structure:

new-structure
โ”œโ”€ library-foobar
โ”œโ”€ app
โ”‚  โ”œโ”€ libs
โ”‚  โ”œโ”€ src
โ”‚  โ”‚  โ”œโ”€ androidTest
โ”‚  โ”‚  โ”‚  โ””โ”€ java
โ”‚  โ”‚  โ”‚     โ””โ”€ com/futurice/project
โ”‚  โ”‚  โ””โ”€ main
โ”‚  โ”‚     โ”œโ”€ java
โ”‚  โ”‚     โ”‚  โ””โ”€ com/futurice/project
โ”‚  โ”‚     โ”œโ”€ res
โ”‚  โ”‚     โ””โ”€ AndroidManifest.xml
โ”‚  โ”œโ”€ build.gradle
โ”‚  โ””โ”€ proguard-rules.pro
โ”œโ”€ build.gradle
โ””โ”€ settings.gradle

์ฃผ๋œ ์ฐจ์ด์ ์€ Gradle์—์„œ ์˜จ ๊ฐœ๋…์ธ๋ฐ, ์ƒˆ๋กœ์šด ๊ตฌ์กฐ๊ฐ€ 'source sets' (main, androidTest)๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ถ„๋ฆฌ์‹œ์ผœ๋‘”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด src์— paid์™€ free๋ผ๋Š” ๊ฐ๊ธฐ ๋‹ค๋ฅธ Flavor์— ํ•ด๋‹นํ•˜๋Š” ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๊ฐ–๋Š” 'paid'๋ผ๋Š” ์†Œ์Šค ์…‹๊ณผ 'free'๋ผ๋Š” ์†Œ์Šค ์…‹์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ตœ์ƒ์œ„ ๋ ˆ๋ฒจ app์„ ๊ฐ–๋Š” ๊ฒƒ์€ ์•ฑ๊ณผ ์•ฑ์—์„œ ์ฐธ์กฐ๋œ ๋‹ค๋ฅธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ”„๋กœ์ ํŠธ๋“ค(e.g., library-foobar)์„ ๊ตฌ๋ณ„ํ•˜๋Š” ๋ฐ์— ์œ ์šฉํ•˜๋‹ค. settings.gradle์€ app/build.gradle์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋Š” ์ด๋Ÿฌํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ”„๋กœ์ ํŠธ๋“ค์„ ๋ณด๊ด€ํ•œ๋‹ค.

Gradle ์„ค์ •

์ผ๋ฐ˜์ ์ธ ๊ตฌ์กฐ. Google's guide on Gradle for Android๋ฅผ ํ™•์ธํ•˜์ž.

์ž‘์€ Task๋“ค. ์Šคํฌ๋ฆฝํŠธ๋“ค(shell, Python, Perl, etc) ๋Œ€์‹ , Gradle์˜ Task๋“ค์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. Gradle's documentation์—์„œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์„ ํ™•์ธํ•˜์ž.

๋น„๋ฐ€๋ฒˆํ˜ธ ์•ฑ์˜ build.gradle์— ๋ฆด๋ฆฌ์ฆˆ ๋นŒ๋“œ๋ฅผ ์œ„ํ•œ signingConfigs ์ •์˜๊ฐ€ ํ•„์š”ํ•  ๊ฒƒ์ด๋‹ค. ๋‹ค์Œ์€ ํ”ผํ•˜์ž.

์•„๋ž˜ ๋ฐฉ๋ฒ•์ฒ˜๋Ÿผ ์ž‘์—…ํ•˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค. ์ด๋Š” ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์—์„œ ๋‚˜ํƒ€๋‚  ๊ฒƒ์ด๋‹ค.

signingConfigs {
    release {
        storeFile file("myapp.keystore")
        storePassword "password123"
        keyAlias "thekey"
        keyPassword "password789"
    }
}

๋Œ€์‹ , gradle.properties ํŒŒ์ผ์„ ๋งŒ๋“ค์ž. ์ด๋Š” ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์— ์ถ”๊ฐ€๋˜์–ด์„  ์•ˆ๋œ๋‹ค:

KEYSTORE_PASSWORD=password123
KEY_PASSWORD=password789

์œ„ ํŒŒ์ผ์€ gradle์— ์ž๋™์œผ๋กœ ์ž„ํฌํŠธ๋˜์–ด, build.gradle์— ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค:

signingConfigs {
    release {
        try {
            storeFile file("myapp.keystore")
            storePassword KEYSTORE_PASSWORD
            keyAlias "thekey"
            keyPassword KEY_PASSWORD
        }
        catch (ex) {
            throw new InvalidUserDataException("You should define KEYSTORE_PASSWORD and KEY_PASSWORD in gradle.properties.")
        }
    }
}

jar ํŒŒ์ผ ์ž„ํฌํŠธ ๋Œ€์‹  Maven์„ ์„ ํ˜ธํ•˜์ž. ํ”„๋กœ์ ํŠธ์— jar ํŒŒ์ผ์„ ๋ช…์‹œ์ ์œผ๋กœ ํฌํ•จ์‹œํ‚ฌ ๊ฒฝ์šฐ, ์ด๋“ค์€ 2.1.1์ฒ˜๋Ÿผ ํŠน์ •ํ•˜๊ฒŒ ๊ณ ์ •๋œ ๋ฒ„์ „์ด ๋œ๋‹ค. jar๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ , ์—…๋ฐ์ดํŠธํ•˜๋Š” ๊ฒƒ์€ ๊ท€์ฐฎ์€ ์ผ์ด๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ Maven์€ ์ด ๋ฌธ์ œ๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ํ•ด๊ฒฐํ•ด์ค„ ๊ฒƒ์ด๊ณ , ๋˜ํ•œ ์ด๋Š” Android Gradle ๋นŒ๋“œ์—์„œ ์žฅ๋ ค๋˜๋Š” ๋ฐฉ์‹์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์ž๋ฉด ์ด๋ ‡๋‹ค:

dependencies {
    compile 'com.squareup.okhttp:okhttp:2.2.0'
    compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
}

Maven์˜ ๋™์  ์˜์กด์„ฑ ํ•ด๊ฒฐ์„ ํ”ผํ•˜๋ผ 2.1.+๊ณผ ๊ฐ™์ด ๋™์ ์œผ๋กœ ๋ฒ„์ „์„ ์ •ํ•˜๋Š” ๋ฐฉ์‹์€ ๋ถˆ์•ˆ์ •ํ•˜๊ณ , ๋นŒ๋“œ ์‚ฌ์ด์— ๋ฏธ๋ฌ˜ํ•˜๊ณ  ์ดํ•ดํ•˜๊ธฐ ์–ด๋ ค์šด ์ฐจ์ด๋ฅผ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์–ด ํ”ผํ•˜๋„๋ก ํ•˜์ž. 2.1.1์ฒ˜๋Ÿผ ์ •์ ์œผ๋กœ ๊ณ ์ •๋œ ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ณด๋‹ค ์•ˆ์ •์ ์ด๊ณ , ์˜ˆ์ธก ๊ฐ€๋Šฅํ•˜๊ณ , ๋ฐ˜๋ณต์ ์ธ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ์— ๋„์›€์ด ๋  ๊ฒƒ์ด๋‹ค.

IDE์™€ ํ…์ŠคํŠธ ์—๋””ํ„ฐ

ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐ์— ์šฉ์ดํ•œ ์—๋””ํ„ฐ๋ผ๋ฉด ๋ฌด์—‡์ด๋“  ์‚ฌ์šฉํ•ด๋„ ์ข‹๋‹ค. ์—๋””ํ„ฐ๋Š” ๊ฐœ์ธ์ ์ธ ์„ ํƒ์ด๊ณ , ๊ทธ ์—๋””ํ„ฐ๊ฐ€ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์™€ ํ”„๋กœ์ ํŠธ ๋นŒ๋“œ ์‹œ์Šคํ…œ์— ๋”ฐ๋ผ ๊ธฐ๋Šฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์€ ๊ฐœ๋ฐœ์ž์˜ ๋ชซ์ด๋‹ค.

ํ˜„์žฌ ๊ฐ€์žฅ ์ถ”์ฒœํ•˜๋Š” IDE๋Š” Android Studio์ด๋‹ค. Google์ด ๊ฐœ๋ฐœํ–ˆ๊ณ , Gradle์— ๊ฐ€์žฅ ๋ฐ€์ ‘ํ•˜๋ฉฐ, ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ๋‹ค๊ฐ€ ์•ˆ์ •ํ™” ๋‹จ๊ณ„์— ๋“ค์–ด๊ฐ€ Android ๊ฐœ๋ฐœ์— ์ž˜ ๋งž์ถ”์–ด์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

์›ํ•œ๋‹ค๋ฉด Eclipse ADT๋ฅผ ์‚ฌ์šฉํ•ด๋„ ์ข‹์ง€๋งŒ, ๋นŒ๋“œํ•˜๋Š” ๋ฐ์— ๋‚ก์€ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ์™€ Ant๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด์— ๋Œ€ํ•œ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค. Vim, Sublime Text, Emacs๊ฐ™์€ ํ”Œ๋ ˆ์ธ ํ…์ŠคํŠธ ์—๋””ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” Gradle๊ณผ adb๋ฅผ ์ปค๋งจ๋“œ๋ผ์ธ์—์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. Eclipse์˜ Gradle ์‚ฌ์šฉ์ด ์ œ๋Œ€๋กœ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ์ปค๋งจ๋“œ๋ผ์ธ์œผ๋กœ ๋นŒ๋“œํ•˜๊ฑฐ๋‚˜ Android Studio๋กœ ์˜ฎ๊ธฐ์ž. ADT ํ”Œ๋Ÿฌ๊ทธ์ธ์ด deprecate๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ด ๊ฒƒ์ด ๊ฐ€์žฅ ์ข‹์€ ์˜ต์…˜์ผ ๊ฒƒ์ด๋‹ค.

๋ฌด์—‡์„ ์‚ฌ์šฉํ•˜๋“ , ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋นŒ๋“œ์˜ ๊ณต์‹์ ์ธ ๋ฐฉ๋ฒ•์ธ Gradle๊ณผ ์ƒˆ๋กœ์šด ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅด๊ณ , ํŠน์ • ์—๋””ํ„ฐ๋ฅผ ๋”ฐ๋ฅด๋Š” ์„ค์ • ํŒŒ์ผ์„ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜๋Š” ๊ฒƒ๋งŒ ๋ช…์‹ฌํ•˜์ž. ์˜ˆ๋ฅผ ๋“ค๋ฉด, Ant์˜ build.xml ํŒŒ์ผ๋“ค์€ ์ถ”๊ฐ€ํ•˜์ง€ ์•Š๋„๋ก ํ•œ๋‹ค. ํŠนํžˆ Ant์˜ ๋นŒ๋“œ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์žˆ๋‹ค๋ฉด build.gradle์„ ์ตœ์‹ ์˜ ์ƒํƒœ๋กœ ๊ธฐ๋Šฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์„ ์žŠ์ง€๋ง์ž. ๋˜ํ•œ ๋‹ค๋ฅธ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ์นœ์ ˆํ•ด์ง€์ž. ๊ทธ๋“ค์˜ ์„ค์ •์„ ๋ฐ”๊พธ๋„๋ก ๊ฐ•์š”ํ•˜์ง€ ์•Š์•„์•ผํ•œ๋‹ค.

๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

**Jackson**์€ Object๋ฅผ JSON์œผ๋กœ, ํ˜น์€ ๊ทธ ๋ฐ˜๋Œ€๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” Java ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. Gson์ด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ์— ๋งŽ์ด ์“ฐ์ด๊ธด ํ•˜์ง€๋งŒ, ์ŠคํŠธ๋ฆฌ๋ฐ, ์ธ๋ฉ”๋ชจ๋ฆฌ ํŠธ๋ฆฌ ๋ชจ๋ธ, ์ „ํ†ต์ ์ธ JSON-POJO ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ๊ณผ ๊ฐ™์€ ์—ฌ๋Ÿฌ ๋Œ€์•ˆ๋“ค์„ ์ง€์›ํ•˜๋Š” Jackson์ด ๋” ๊ณ ์„ฑ๋Šฅ์ผ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ ๋ช…์‹ฌํ•˜์ž. Jackson์ด GSON๋ณด๋‹ค ๋” ํฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๊ธฐ ๋•Œ๋ฌธ์—, 65,000 ๋ฉ”์†Œ๋“œ ์ˆ˜ ์ œํ•œ์— ๋ถ€๋”ชํžŒ ๊ฒฝ์šฐ๋ผ๋ฉด GSON์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋‚˜์„ ์ˆ˜๋„ ์žˆ๋‹ค. ๋‹ค๋ฅธ ๋Œ€์•ˆ์œผ๋กœ๋Š” Json-smart๊ณผ Boon JSON์ด ์žˆ๋‹ค.

๋„คํŠธ์›Œํ‚น, ์บ์‹ฑ, ์ด๋ฏธ์ง€. ๋ฐฑ์—”๋“œ ์„œ๋ฒ„๋กœ์˜ ์š”์ฒญ ์ฒ˜๋ฆฌ์— ๋Œ€ํ•ด ํด๋ผ์ด์–ธํŠธ๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๊ฒ€์ฆ๋œ ํ•ด๊ฒฐ์ฑ…์ด ์žˆ๋‹ค. Volley ํ˜น์€ Retrofit์„ ์‚ฌ์šฉํ•˜์ž. Volley๋Š” ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์บ์‹ฑํ•˜๋Š” ๋„์šฐ๋ฏธ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. Retrofit์„ ์„ ํƒํ•œ๋‹ค๋ฉด, ์ด๋ฏธ์ง€ ๋กœ๋”ฉ๊ณผ ์บ์‹ฑ์—๋Š” Picasso๋ฅผ, ํšจ์œจ์ ์ธ HTTP ์š”์ฒญ์—๋Š” OkHttp๋ฅผ ๊ณ ๋ คํ•ด๋ณด์ž. ์ด ๋ชจ๋“  ์„ธ๊ฐ€์ง€์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์€ ๊ฐ™์€ ํšŒ์‚ฌ์—์„œ ๊ฐœ๋ฐœ๋˜์–ด ์„œ๋กœ ์ƒํ˜ธ๋ณด์™„์ด ๋งค์šฐ ์šฉ์ดํ•˜๋‹ค. OkHttp can also be used in connection with Volley.

RxJava ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” Reactive Programming์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ์ด๋Š” ๋งค์šฐ ๊ฐ•๋ ฅํ•˜๊ณ  ์œ ๋งํ•œ ํŒจ๋Ÿฌ๋‹ค์ž„์œผ๋กœ, ๋„ˆ๋ฌด ๋‹ค๋ฅธ ์ ์ด ๋งŽ์•„ ํ˜ผ๋ž€์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋‹ค. ๋ชจ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์•„ํ‚คํ…ํŠธ๋“ค์—๊ฒŒ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๊ธฐ ์ „ ์ฃผ์˜ํ•  ๊ฒƒ์„ ์ถ”์ฒœํ•œ๋‹ค. RxJava๋ฅผ ์ด์šฉํ•œ ๋ช‡ ๊ฐ€์ง€ ํ”„๋กœ์ ํŠธ๊ฐ€ ์žˆ๋Š”๋ฐ, ํ•„์š”ํ•˜๋‹ค๋ฉด ์ด ์‚ฌ๋žŒ๋“ค์—๊ฒŒ์„œ ๋„์›€์„ ๊ตฌํ•˜์ž: Timo Tuominen, Olli Salonen, Andre Medeiros, Mark Voit, Antti Lammi, Vera Izrailit, Juha Ristolainen. ์ž‘์„ฑ๋œ ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋„ ์žˆ๋‹ค: [1], [2], [3], [4].

Rx์— ๋Œ€ํ•œ ๊ฒฝํ—˜์ด ์—†๋‹ค๋ฉด, API ์‘๋‹ต ์ฒ˜๋ฆฌ์—๋งŒ ์ ์šฉํ•ด๋ณด์ž. ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ํด๋ฆญ ์ด๋ฒคํŠธ๋‚˜ ๊ฒ€์ƒ‰ ํƒ€์ดํ•‘ ์ด๋ฒคํŠธ์™€ ๊ฐ™์€ ๊ฐ„๋‹จํ•œ UI ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ์— ์ ์šฉํ•ด๋ณผ ์ˆ˜๋„ ์žˆ๋‹ค. Rx ๊ธฐ์ˆ ์— ์ž์‹ ๊ฐ์ด ์ƒ๊ฒจ ๋ชจ๋“  ์„ค๊ณ„์— ์ ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋ชจ๋“  ๊นŒ๋‹ค๋กœ์šด ๋ถ€๋ถ„๋“ค์— Javadocs๋ฅผ ์ž‘์„ฑํ•˜์ž. RxJava์— ์ต์ˆ™ํ•˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ ์ง€, ๋ณด์ˆ˜ํ•˜๋Š” ๋ฐ์— ์–ด๋ ค์›€์ด ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ๋ช…์‹ฌํ•ด์•ผ ํ•œ๋‹ค. ๊ทธ๋“ค์˜ Rx์™€ ์ฝ”๋“œ ์ดํ•ด์— ๋Œ€ํ•ด ์ตœ์„ ์„ ๋‹คํ•ด ๋„์›€์„ ์ฃผ์ž.

**Retrolambda**๋Š” Android ํ˜น์€ ๋‹ค๋ฅธ pre-JDK8 ํ”Œ๋žซํผ์—์„œ Lambda ํ‘œํ˜„ ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋Š” Java ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํŠนํžˆ RxJava์™€ ๊ฐ™์ด ๊ธฐ๋Šฅ ์œ„์ฃผ ์Šคํƒ€์ผ์˜ ์ฝ”๋“œ๋ฅผ ๋”์šฑ ํƒ€์ดํŠธํ•˜๊ณ  ์ฝ๊ธฐ ์ข‹๊ฒŒ ๋งŒ๋“ค์–ด์ค€๋‹ค. ์‚ฌ์šฉํ•˜๋ ค๋ฉด, JDK8์„ ์„ค์น˜ํ•˜๊ณ  ์ด๋ฅผ Android Studio ํ”„๋กœ์ ํŠธ ๋Œ€ํ™”์ƒ์ž์—์„œ SDK ๊ฒฝ๋กœ๋กœ ์„ค์ •ํ•œ ํ›„, JAVA8_HOME๊ณผ JAVA7_HOME ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•œ ๋’ค ํ”„๋กœ์ ํŠธ root์˜ build.gradle์„ ์ด๋ ‡๊ฒŒ ์„ค์ •ํ•œ๋‹ค:

dependencies {
    classpath 'me.tatarka:gradle-retrolambda:2.4.1'
}

๊ทธ๋ฆฌ๊ณ  ๊ฐ๊ฐ ๋ชจ๋“ˆ๋“ค์˜ build.gradle์— ์•„๋ž˜ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•˜์ž.

apply plugin: 'retrolambda'

android {
    compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

retrolambda {
    jdk System.getenv("JAVA8_HOME")
    oldJdk System.getenv("JAVA7_HOME")
    javaVersion JavaVersion.VERSION_1_7
}

Android Studio๋Š” Java8 lambda์˜ ์ฝ”๋“œ ์ง€์›์„ ์ œ๊ณตํ•œ๋‹ค. ๋งŒ์•ฝ lambda๊ฐ€ ์ฒ˜์Œ์ด๋ผ๋ฉด, ๋‹ค์Œ ํ•ญ๋ชฉ๋“ค์„ ๋”ฐ๋ผ ์‹œ์ž‘ํ•ด๋ณด์ž:

  • ํ•˜๋‚˜์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ–๋Š” ๋ชจ๋“  ์ธํ„ฐํŽ˜์ด์Šค๋“ค์€ "lambda์™€ ๋ฐ€์ ‘"ํ•˜๊ณ , ๋”์šฑ ํƒ€์ดํŠธํ•œ ๋ฌธ๋ฒ•์œผ๋กœ ๋ฌถ์ผ ์ˆ˜ ์žˆ๋‹ค.
  • ๋งŒ์•ฝ ํŒŒ๋ผ๋ฉ”ํ„ฐ๋“ค์ด ์˜์‹ฌ์Šค๋Ÿฝ๋‹ค๋ฉด, ์ผ๋ฐ˜ ์ต๋ช… ๋‚ด๋ถ€ ํด๋ž˜์Šค๋ฅผ ์ž‘์„ฑํ•˜๊ณ  Android Studio๊ฐ€ lambda๋กœ ๋ฌถ์–ด์ฃผ๋„๋ก ํ•ด๋ณด์ž.

Dex ๋ฉ”์†Œ๋“œ ์ œํ•œ์„ ์œ ์˜ํ•˜๊ณ , ๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์„ ํ”ผํ•˜์ž. Android ์•ฑ๋“ค์ด dex ํŒŒ์ผ๋กœ ํŒจํ‚ค์ง•๋  ๋•Œ, 65,536๊ฐœ์˜ ์ฐธ์กฐ ๋ฉ”์†Œ๋“œ ์ˆ˜ ์ œํ•œ์„ ๊ฐ–๋Š”๋‹ค[1] [2] [3]. ์ œํ•œ๋œ ๋ฉ”์†Œ๋“œ ์ˆ˜๋ฅผ ๋„˜์–ด์„œ๋ฉด ์ปดํŒŒ์ผ์‹œ Fatal error๋ฅผ ๋ณด๊ฒŒ๋  ๊ฒƒ์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, ์ตœ์†Œํ•œ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์‚ฌ์šฉํ•˜๊ณ , dex-method-counts ํˆด์„ ์‚ฌ์šฉํ•˜์—ฌ ์ œํ•œ๋œ ์ˆ˜๋ณด๋‹ค ์ ๊ฒŒ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•˜์ž. ํŠนํžˆ Guava ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ”ผํ•˜์ž. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” 13,000๊ฐœ๊ฐ€ ๋„˜๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

Activity์™€ Fragment

Fragment์™€ Activity๋ฅผ ์ด์šฉํ•˜์—ฌ Android์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์žฅ ์ข‹์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์„ค๊ณ„ํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ปค๋ฎค๋‹ˆํ‹ฐ๋‚˜ Futurice ๊ฐœ๋ฐœ์ž๋“ค ์‚ฌ์ด์—์„œ๋„ ํ•ฉ์˜๋œ ๋ฐ”๊ฐ€ ์—†๋‹ค. Square๋Š” Fragment์˜ ์šฐํšŒ ๋Œ€์•ˆ์œผ๋กœ a library for building architectures mostly with Views๋ฅผ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์ด ๋˜ํ•œ ์ปค๋ฎค๋‹ˆํ‹ฐ ์‚ฌ์ด์—์„œ ๋„๋ฆฌ ์ถ”์ฒœํ• ๋งŒํ•œ ๋ฐฉ์‹์€ ์•„๋‹ˆ๋ผ๊ณ  ์ƒ๊ฐ๋œ๋‹ค.

Android API์˜ ํžˆ์Šคํ† ๋ฆฌ๋กœ ์ธํ•ด, ๋ง‰์—ฐํžˆ Fragment๊ฐ€ ํ™”๋ฉด์ƒ์˜ UI ์กฐ๊ฐ์ด๋ผ๊ณ  ๋– ์˜ฌ๋ฆด ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค. ์ฆ‰, Fragment๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ UI์™€ ์—ฐ๊ด€๋˜์–ด ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. Activity ๋˜ํ•œ ๋ง‰์—ฐํ•˜๊ฒŒ ๊ทธ๋“ค์˜ ๋ผ์ดํ”„์‚ฌ์ดํด๊ณผ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋ฐ์— ์ค‘์š”ํ•œ ์ปจํŠธ๋กค๋Ÿฌ๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ๋‹ค์Œ ์—ญํ• ๋“ค์—์„œ ์ฐจ์ด๋ฅผ ์‰ฝ๊ฒŒ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋‹ค: Activity๋Š” UI ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•˜๊ณ (delivering transitions between screens), Fragment๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ์˜ ์—ญํ• ์„ ์ˆ˜ํ–‰ํ•œ๋‹ค. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” Fragment ํ˜น์€ Activity, ๋˜๋Š” View ์…‹ ์ค‘ ํ•˜๋‚˜๋งŒ์„ ์ด์šฉํ•œ ๊ตฌ์กฐ๋ฅผ ์„ ํƒํ•จ์— ์žˆ์–ด์„œ ๊ฒฐํ•จ์ด ์žˆ๋‹ค๋Š” ์ ์„ ํŒŒ์•…ํ•˜๊ณ  ์ •ํ™•ํ•œ ๊ทผ๊ฑฐ๋ฅผ ๊ฐ–๋Š” ๊ฒฐ์ •์„ ํ•˜์—ฌ ์กฐ์‹ฌ์Šค๋Ÿฝ๊ฒŒ ์‹œ์ž‘ํ•˜๊ธฐ๋ฅผ ๊ถŒํ•œ๋‹ค. ๋‹ค์Œ์€ ์ฃผ์˜ํ•ด์•ผ ํ•  ๊ฒƒ๋“ค์— ๋Œ€ํ•œ ์กฐ์–ธ์ธ๋ฐ, ์ ๋‹นํžˆ ๊ฑธ๋Ÿฌ์„œ ์ˆ˜์šฉํ•˜์ž:

  • Nested fragments๋ฅผ ๋„๋ฆฌ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ํ”ผํ•ด์•ผ ํ•˜๋Š”๋ฐ, matryoshka bugs๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ค‘์ฒฉ๋œ Fragment๋Š” ๊ผญ ํƒ€๋‹นํ•œ ๊ฒฝ์šฐ(์˜ˆ๋ฅผ ๋“ค๋ฉด, ์ˆ˜ํ‰์œผ๋กœ ์Šฌ๋ผ์ด๋”ฉํ•˜๋Š” ViewPager ๋‚ด๋ถ€์˜ Fragment๋“ค)๋‚˜ ์ž˜ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ์„ ๋งŒํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜์ž.
  • Activity์— ๋„ˆ๋ฌด ๋งŽ์€ ์ฝ”๋“œ๋ฅผ ๋„ฃ๋Š” ๊ฒƒ์„ ํ”ผํ•ด์•ผ ํ•œ๋‹ค. ๊ฐ€๋Šฅํ•˜๋ฉด ์–ธ์ œ๋“ ์ง€ ๊ฐ€๋ฒผ์šด ์ปจํ…Œ์ด๋„ˆ๋กœ์„œ ์œ ์ง€ํ•˜๊ณ , ์ฃผ๋กœ ๋ผ์ดํ”„์‚ฌ์ดํด๊ณผ ๋‹ค๋ฅธ ์ค‘์š”ํ•œ Android์™€์˜ ์ธํ„ฐํŽ˜์ด์‹ฑ API๋ฅผ ์œ„ํ•ด์„œ๋งŒ ์กด์žฌํ•˜๋„๋ก ํ•˜์ž. ์ˆœ์ˆ˜ Activity ๋ณด๋‹ค๋Š” ๋‹จ์ผ Fragment๋กœ ๊ตฌ์„ฑ๋œ Activity๊ฐ€ ์ข‹๋‹ค - UI ์ฝ”๋“œ๋ฅผ Activity์˜ Fragment์— ๋„ฃ์ž. ์ด๋Š” ๋‹ค๋ฅธ ์ž˜ ๊ตฌ์„ฑ๋œ ๋ ˆ์ด์•„์›ƒ, ํ˜น์€ ์—ฌ๋Ÿฌ Fragment๋กœ ๊ตฌ์„ฑ๋œ ํƒ€๋ธ”๋ › ํ™”๋ฉด์œผ๋กœ ์˜ฎ๊ธธ ํ•„์š”๊ฐ€ ์žˆ์„ ๋•Œ์— ์žฌ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ๋งŒ๋“ ๋‹ค. ์ •ํ™•ํ•œ ๊ทผ๊ฑฐ๊ฐ€ ์—†๋Š” ๊ฒฐ์ •์ด๋ผ๋ฉด Fragment์™€ ์ƒํ˜ธ ์ž‘์šฉํ•˜์ง€ ์•Š๋Š” Activity๋Š” ํ”ผํ•˜์ž.
  • ์•ฑ์˜ ๋‚ด๋ถ€์  ๋™์ž‘์ด Intent์— ๊ฐ•ํ•˜๊ฒŒ ์˜์กด์ ์ธ Android ๋ ˆ๋ฒจ์˜ API๋ฅผ ๋‚จ์šฉํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ์ด๋Š” ๋ฒ„๊ทธ์™€ ๋ ‰์„ ์œ ๋ฐœํ•˜์—ฌ Android OS๋‚˜ ๋‹ค๋ฅธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋“ค์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋งŒ์•ฝ ์•ฑ์ด ๋‹น์‹ ์˜ ํŒจํ‚ค์ง€ ์‚ฌ์ด์—์„œ ๋‚ด๋ถ€์ ์ธ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜์„ ์œ„ํ•ด Intent๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด, ์•ฑ์ด OS ๋ถ€ํŒ… ๋ฐ”๋กœ ํ›„์— ์‹คํ–‰๋˜์—ˆ์„ ๋•Œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ƒ์—์„œ ๋ช‡ ์ดˆ๊ฐ„์˜ ๋ ‰์„ ๋ฐœ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค๊ณ  ์•Œ๋ ค์ ธ ์žˆ๋‹ค.

Java ํŒจํ‚ค์ง€ ์„ค๊ณ„

Android ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์œ„ํ•œ Java ์„ค๊ณ„ ๊ฐ„๋‹จํžˆ Model-View-Controller ๊ฐ„๋žตํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค. Android์—์„œ๋Š”, Fragment and Activity are actually controller classes ๋ผ๊ณ  ์„ค๋ช…๋˜๋Š”๋ฐ, ๋‹ค๋ฅธ ๋ฉด์—์„œ Fragment์™€ Activity๋“ค์€ ๋ช…์‹œ์ ์œผ๋กœ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค, ์ฆ‰ View ์ด๊ธฐ๋„ ํ•˜๋‹ค.

๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, Fragment(ํ˜น์€ Activity)๋ฅผ ์ •ํ™•ํžˆ Conteroller๋‚˜ View ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ๊ทธ์— ํ•ด๋‹นํ•˜๋Š” fragments ํŒจํ‚ค์ง€์— ๋‘๋Š” ๊ฒƒ์ด ๋‚ซ๋‹ค. Activity๋Š” ์ด์ „ ์„น์…˜์—์„œ์˜ ์กฐ์–ธ์— ๋”ฐ๋ผ ์ตœ์ƒ์œ„ ํŒจํ‚ค์ง€์— ๋‘˜ ์ˆ˜ ์žˆ๋‹ค. 2, 3๊ฐœ ์ด์ƒ์˜ Activity๋“ค์„ ๊ณ„ํšํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, activities ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค์–ด ๋‘์ž.

๋‹ค๋ฅธ ๊ฒฝ์šฐ์—๋Š”, API ์‘๋‹ต์— ๋Œ€ํ•œ JSON ํŒŒ์„œ์— ์˜ํ•ด ์ฑ„์›Œ์ง„ POJO๋“ค์„ ๋‹ด๋Š” models ํŒจํ‚ค์ง€, ์ปค์Šคํ…€ View, Notification, Action bar view, Widget ๋“ฑ์„ ๋‹ด๋Š” views ํŒจํ‚ค์ง€๋ฅผ ๋‘์–ด ์„ค๊ณ„๊ฐ€ ์ผ๋ฐ˜์ ์ธ MVC๋กœ ํ‘œํ˜„๋  ์ˆ˜ ์žˆ๋‹ค. Adapter๋“ค์€ ๋ฐ์ดํ„ฐ์™€ View๋“ค ์‚ฌ์ด์— ์กด์žฌํ•˜๋Š” gray matter๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ getView()๋ฅผ ํ†ตํ•ด View๋“ค์„ ์ถ”์ถœํ•˜๋Š” ๋ฐ์— ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— views ํŒจํ‚ค์ง€ ์•ˆ์— adapters๋ผ๋Š” ์„œ๋ธŒํŒจํ‚ค์ง€๋กœ ๋‘˜ ์ˆ˜ ์žˆ๋‹ค.

Controller ํด๋ž˜์Šค๋“ค์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ „์ฒด์—, Android ์‹œ์Šคํ…œ์— ๊ฐ€๊น๊ฒŒ ์กด์žฌํ•œ๋‹ค. ์ด๋“ค์€ managers ํŒจํ‚ค์ง€์— ๋‘˜ ์ˆ˜ ์žˆ๋‹ค. "DateUtils"์™€ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ํด๋ž˜์Šค๋“ค์€ utils ํŒจํ‚ค์ง€์—, ๋ฐฑ์—”๋“œ์™€ ์ธํ„ฐ๋ž™์…˜ํ•˜๋Š” ์—ญํ• ์„ ๋งก๋Š” ํด๋ž˜์Šค๋“ค์€ network ํŒจํ‚ค์ง€์— ๋‘์ž.

์ข…ํ•ฉ์ ์œผ๋กœ, ๋ฐฑ์—”๋“œ์™€ ๊ฐ€๊นŒ์šด ๊ฒƒ๋ถ€ํ„ฐ ์œ ์ €์™€ ๊ฐ€๊นŒ์šด ์ˆœ์„œ๋Œ€๋กœ ์ •๋ ฌํ•ด๋ณด๋ฉด ์ด๋ ‡๋‹ค:

com.futurice.project
โ”œโ”€ network
โ”œโ”€ models
โ”œโ”€ managers
โ”œโ”€ utils
โ”œโ”€ fragments
โ””โ”€ views
   โ”œโ”€ adapters
   โ”œโ”€ actionbar
   โ”œโ”€ widgets
   โ””โ”€ notifications

๋ฆฌ์†Œ์Šค

์ด๋ฆ„ ์ •ํ•˜๊ธฐ. type_foo_bar.xml๊ณผ ๊ฐ™์ด ํƒ€์ž…์„ ์ ‘๋‘์–ด๋กœ ๋‘๋Š” ์ปจ๋ฒค์…˜์„ ๋”ฐ๋ฅด์ž. ์˜ˆ์‹œ: fragment_contact_details.xml, view_primary_button.xml, activity_main.xml.

๋ ˆ์ด์•„์›ƒ XML์„ ์ฒด๊ณ„ํ™”ํ•˜๊ธฐ. ๋ ˆ์ด์•„์›ƒ XML์„ ์–ด๋–ค ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์ง€ ํ™•์‹ค์น˜ ์•Š๋‹ค๋ฉด, ๋‹ค์Œ ์ปจ๋ฒค์…˜์ด ๋„์›€์ด ๋  ๊ฒƒ์ด๋‹ค.

  • ํ•œ ์†์„ฑ๋‹น ํ•œ ์ค„, ๋“ค์—ฌ์“ฐ๊ธฐ๋Š” 4์นธ์˜ ์ŠคํŽ˜์ด์Šค
  • android:id๋ฅผ ํ•ญ์ƒ ์ฒซ ์†์„ฑ์œผ๋กœ
  • android:layout_**** ์†์„ฑ๋“ค์„ ์œ—์ชฝ์—
  • style ์†์„ฑ์€ ๋งจ ์•„๋ž˜์—
  • ํƒœ๊ทธ๋ฅผ ๋‹ซ๋Š” />๋Š” ์ •๋ ฌ๊ณผ ์ƒˆ ์†์„ฑ ์ถ”๊ฐ€๋ฅผ ์œ„ํ•ด ๋…๋ฆฝ์ ์ธ ์ค„์—
  • Rather than hard coding android:text์— ํ•˜๋“œ์ฝ”๋”ฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค, Android Studio์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ Designtime attributes๋ฅผ ๊ณ ๋ คํ•˜์ž.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="@string/name"
        style="@style/FancyText"
        />

    <include layout="@layout/reusable_part" />

</LinearLayout>

๊ฐ€์žฅ ์ค‘์š”ํ•œ ๊ฒƒ์€, android:layout_****๋ฅผ ๋ ˆ์ด์•„์›ƒ XML์— ๋‘๊ณ  android:****๋ฅผ ์Šคํƒ€์ผ XML์— ์ •์˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด ๊ทœ์น™์€ ์˜ˆ์™ธ๊ฐ€ ์žˆ์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ ์ž˜ ๋™์ž‘ํ•œ๋‹ค. ์ด๋Š” ๋ ˆ์ด์•„์›ƒ(์œ„์น˜, ์—ฌ๋ฐฑ, ํฌ๊ธฐ)๊ณผ ๋‚ด์šฉ์— ๊ด€ํ•œ ์†์„ฑ์„ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— ๋‘๊ณ , ์ƒ์„ธํ•œ ๋ชจ์–‘(์ƒ‰, ์•ˆ์ชฝ ์—ฌ๋ฐฑ, ํฐํŠธ)์— ๋Œ€ํ•œ ๋‚ด์šฉ์€ ์Šคํƒ€์ผ ํŒŒ์ผ์— ๋‘๊ธฐ ์œ„ํ•จ์ด๋‹ค.

์˜ˆ์™ธ๋Š” ์ด๋Ÿฐ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค:

  • android:id๋Š” ์ •ํ™•ํžˆ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— ๋‘์–ด์•ผ ํ•œ๋‹ค. should obviously be in the layout files
  • LinearLayout์˜ android:orientation ์†์„ฑ์€ ์ผ๋ฐ˜์ ์œผ๋กœ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— ์žˆ๋Š” ๊ฒƒ์ด ํƒ€๋‹นํ•˜๋‹ค.
  • android:text๋Š” ๋‚ด์šฉ์„ ์ •์˜ํ•˜๋Š” ์†์„ฑ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— ๋‘์–ด์•ผ ํ•œ๋‹ค.
  • ๊ฐ€๋” ์ผ๋ฐ˜์ ์ธ ์Šคํƒ€์ผ๋กœ android:layout_width์™€ android:layout_height ์†์„ฑ๋“ค์„ ๋‘์–ด์•ผ ๋ง์ด ๋  ๊ฒƒ ๊ฐ™์ง€๋งŒ, ๊ธฐ๋ณธ์ ์œผ๋กœ ์ด๋“ค์€ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— ๋ณด์—ฌ์ง„๋‹ค.

์Šคํƒ€์ผ์„ ์‚ฌ์šฉํ•˜์ž. View์— ์ค‘๋ณต๋˜๋Š” ๋ชจ์–‘์ด ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์€ ๋งค์šฐ ์ผ๋ฐ˜์ ์ธ ์ผ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๊ฑฐ์˜ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ๋“ค์ด ์Šคํƒ€์ผ์„ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค. ์ ์–ด๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋Œ€๋ถ€๋ถ„์˜ ํ…์ŠคํŠธ ๋‚ด์šฉ๋“ค์€ ์ผ๋ฐ˜ ์Šคํƒ€์ผ์„ ๊ฐ€์ ธ์•ผํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค๋ฉด ์ด๋ ‡๋‹ค:

<style name="ContentText">
    <item name="android:textSize">@dimen/font_normal</item>
    <item name="android:textColor">@color/basic_black</item>
</style>

TextView์— ์ ์šฉํ•ด๋ณด๋ฉด:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/price"
    style="@style/ContentText"
    />

์•„๋งˆ ๋ฒ„ํŠผ๋“ค์—๋„ ๊ฐ™์€ ์ผ์„ ํ•ด์ฃผ์–ด์•ผ ํ•˜๊ฒ ์ง€๋งŒ, ๋ฉˆ์ถ”์ง€ ๋ง์ž. ๊ณ„์† ์ง„ํ–‰ํ•˜๋ฉด์„œ ์—ฐ๊ด€๋˜์–ด์žˆ๊ณ  ์ค‘๋ณต๋œ android:**** ์†์„ฑ๋“ค์„ ์ผ๋ฐ˜ ์Šคํƒ€์ผ๋กœ ๋ฌถ์ž.

ํฐ ์Šคํƒ€์ผ ํŒŒ์ผ์€ ๋‹ค๋ฅธ ํŒŒ์ผ๋“ค๋กœ ๋‚˜๋ˆ„์ž. ๋‹จ ํ•˜๋‚˜์˜ styles.xml ํŒŒ์ผ์„ ๊ฐ€์งˆ ํ•„์š”๋Š” ์—†๋‹ค. Android SDK๋Š” ๋ฐ•์Šค ์™ธ๋ถ€์˜ ํŒŒ์ผ๋“ค๋„ ์ง€์›ํ•˜๋Š”๋ฐ, styles๋ผ๋Š” ์ด๋ฆ„์—” ์ „ํ˜€ ๋งˆ๋ฒ•๊ฐ™์€ ๋ฌด์–ธ๊ฐ€๊ฐ€ ์—†์ด ํŒŒ์ผ ์•ˆ์— <style> XML ํƒœ๊ทธ๊ฐ€ ์žˆ๋‹ค๋ฉด ์ƒ๊ด€์—†๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ํŒŒ์ผ ์ด๋ฆ„์€ styles.xml, styles_home.xml, styles_item_details.xml, styles_forms.xml ๋“ฑ๊ณผ ๊ฐ™์ด ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋นŒ๋“œ ์‹œ์Šคํ…œ์—์„œ ์˜๋ฏธ๋ฅผ ๊ฐ–๋Š” ๋ฆฌ์†Œ์Šค ๋””๋ ‰ํ† ๋ฆฌ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ res/values ์•ˆ์˜ ํŒŒ์ผ๋ช…์€ ์ž„์˜๋กœ ์„ค์ • ๊ฐ€๋Šฅํ•˜๋‹ค.

colors.xml๋Š” ์ƒ‰ ํŒ”๋ ›ํŠธ์ด๋‹ค. colors.xml์—๋Š” ์ƒ‰ ์ด๋ฆ„๊ณผ RGBA ๊ฐ’์„ ๋งคํ•‘ํ•ด๋†“๋Š” ์ผ ์™ธ์— ๋” ํ•ด์•ผํ•  ์ผ์€ ์—†๋‹ค. ๊ฐ๊ฐ ๋‹ค๋ฅธ ๋ฒ„ํŠผ ํƒ€์ž…๋“ค์— RGBA ๊ฐ’๋“ค์„ ์ •์˜ํ•˜์ง€ ์•Š๋„๋ก ํ•˜์ž.

์•„๋ž˜์˜ ๋ฐฉ์‹์€ ํ”ผํ•˜์ž:

<resources>
    <color name="button_foreground">#FFFFFF</color>
    <color name="button_background">#2A91BD</color>
    <color name="comment_background_inactive">#5F5F5F</color>
    <color name="comment_background_active">#939393</color>
    <color name="comment_foreground">#FFFFFF</color>
    <color name="comment_foreground_important">#FF9D2F</color>
    ...
    <color name="comment_shadow">#323232</color>

์ด๋Ÿฌํ•œ ํ˜•์‹์œผ๋กœ RGBA ๊ฐ’๋“ค์„ ๋ฐ˜๋ณตํ•˜๊ธฐ ์‰ฌ์šด๋ฐ, ์ด๋Š” ๊ธฐ๋ณธ ์ƒ‰๊น”์„ ํ•„์š”์— ๋”ฐ๋ผ ๋ณ€๊ฒฝํ•˜๊ธฐ ๋ณต์žกํ•˜๊ฒŒ ๋งŒ๋“ ๋‹ค. ๋˜ํ•œ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์˜ ์ •์˜๋Š” "button" ํ˜น์€ "comment" ์ฒ˜๋Ÿผ ํŠน์ • ๋ฌธ๋งฅ์— ๊ด€๊ณ„๋˜์–ด์žˆ์–ด colors.xml์ด ์•„๋‹Œ ์Šคํƒ€์ผ์— ๋” ์ ํ•ฉํ•˜๋‹ค.

๋Œ€์‹ , ์ด๋ ‡๊ฒŒ ํ•˜์ž:

<resources>

    <!-- grayscale -->
    <color name="white"     >#FFFFFF</color>
    <color name="gray_light">#DBDBDB</color>
    <color name="gray"      >#939393</color>
    <color name="gray_dark" >#5F5F5F</color>
    <color name="black"     >#323232</color>

    <!-- basic colors -->
    <color name="green">#27D34D</color>
    <color name="blue">#2A91BD</color>
    <color name="orange">#FF9D2F</color>
    <color name="red">#FF432F</color>

</resources>

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋””์ž์ด๋„ˆ์—๊ฒŒ ์ด ํŒ”๋ ›ํŠธ๋ฅผ ์š”์ฒญํ•ด๋ณด์ž. ์ด๋ฆ„์ด ๊ผญ "green", "blue" ์ฒ˜๋Ÿผ ์ƒ‰์˜ ์ด๋ฆ„์ผ ํ•„์š”๋Š” ์—†๋‹ค. "brand_primary", "brand_secondary", "brand_negative" ๊ฐ™์€ ์ด๋ฆ„๋“ค์ด ๋”์šฑ ๋ฐ›์•„๋“ค์ด๊ธฐ ์‰ฝ๋‹ค. ์ด๋ ‡๊ฒŒ ์ƒ‰์˜ ํ˜•์‹์„ ์ง€์ •ํ•˜๊ฒŒ ๋˜๋ฉด ์ƒ‰ ๊ฐ’๋“ค์„ ๋ฆฌํŒฉํ† ๋งํ•˜๊ธฐ ์‰ฌ์›Œ์ง€๊ณ , ์–ผ๋งˆ๋‚˜ ๋งŽ์€ ์ƒ‰๋“ค์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋Š”์ง€ ๋ช…์‹œ์ ์œผ๋กœ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๋ณดํ†ต ์‹ฌ๋ฏธ๊ฐ์„ ์ค‘์š”์‹œํ•˜๋Š” ์•ฑ์—์„œ๋Š”, ์‚ฌ์šฉ๋˜๋Š” ์ƒ‰์˜ ์ข…๋ฅ˜๋ฅผ ์ค„์ด๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

dimens.xml์„ colors.xml์ฒ˜๋Ÿผ ๋‹ค๋ฃจ์ž. ๊ธฐ๋ณธ์ ์œผ๋กœ ์ƒ‰๊ณผ ๊ฐ™์€ ๋ชฉ์ ์„ ์œ„ํ•ด ์ผ๋ฐ˜์ ์ธ ์—ฌ๋ฐฑ๊ณผ ํฐํŠธ ํฌ๊ธฐ ๋“ฑ์˜ "ํŒ”๋ ›ํŠธ"๋ฅผ ์ •์˜ํ•˜์ž. ๋‹ค์Œ์€ dimens.xml ํŒŒ์ผ์˜ ์ข‹์€ ์˜ˆ์‹œ์ด๋‹ค:

<resources>

    <!-- font sizes -->
    <dimen name="font_larger">22sp</dimen>
    <dimen name="font_large">18sp</dimen>
    <dimen name="font_normal">15sp</dimen>
    <dimen name="font_small">12sp</dimen>

    <!-- typical spacing between two views -->
    <dimen name="spacing_huge">40dp</dimen>
    <dimen name="spacing_large">24dp</dimen>
    <dimen name="spacing_normal">14dp</dimen>
    <dimen name="spacing_small">10dp</dimen>
    <dimen name="spacing_tiny">4dp</dimen>

    <!-- typical sizes of views -->
    <dimen name="button_height_tall">60dp</dimen>
    <dimen name="button_height_normal">40dp</dimen>
    <dimen name="button_height_short">32dp</dimen>

</resources>

๋ฌธ์ž์—ด๋“ค์ด ์ผ๋ฐ˜์ ์œผ๋กœ ๋‹ค๋ฃจ์–ด์ง€๋“ฏ์ด, ๋ ˆ์ด์•„์›ƒ, ๋ฐ”๊นฅ์ชฝ/์•ˆ์ชฝ ์—ฌ๋ฐฑ์— ํ•˜๋“œ์ฝ”๋”ฉ๋œ ๊ฐ’๋“ค ๋Œ€์‹  spacing_****์„ ์‚ฌ์šฉํ•˜์ž. ์ด๋Š” ์Šคํƒ€์ผ๊ณผ ๋ ˆ์ด์•„์›ƒ์„ ์ฒด๊ณ„ํ™”ํ•˜๊ณ  ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์„ ์‰ฝ๊ฒŒ ํ•ด์คŒ๊ณผ ๋™์‹œ์— ์ผ๊ด€๋œ ๋ฃฉ์•คํ•„(Look-and-feel)์„ ์ œ๊ณตํ•œ๋‹ค.

strings.xml

strings.xml์˜ ๋ฌธ์ž์—ด๋“ค์€ ๋„ค์ž„์ŠคํŽ˜์ด์Šค์˜ ํ˜•ํƒœ์™€ ๋น„์Šทํ•˜๊ฒŒ ์ด๋ฆ„์„ ์ง“๊ณ , 2๊ฐœ ์ด์ƒ์˜ Key์— ๊ฐ’์„ ์ค‘๋ณตํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๋‘๋ ค์›Œํ•˜์ง€ ์•Š๋„๋ก ํ•˜์ž. ์–ธ์–ด๋Š” ๋ณต์žกํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋„ค์ž„์ŠคํŽ˜์ด์Šค๊ฐ€ ๋ฌธ๋งฅ์„ ๊ฐ–๊ณ  ์• ๋งคํ•จ์„ ์—†์• ๋Š” ๊ฒƒ์ด ํ•„์ˆ˜์ด๋‹ค.

์ž˜๋ชป๋œ ์˜ˆ

<string name="network_error">Network error</string>
<string name="call_failed">Call failed</string>
<string name="map_failed">Map loading failed</string>

์ข‹์€ ์˜ˆ

<string name="error.message.network">Network error</string>
<string name="error.message.call">Call failed</string>
<string name="error.message.map">Map loading failed</string>

๋ฌธ์ž์—ด ๊ฐ’์„ ๋ชจ๋‘ ๋Œ€๋ฌธ์ž๋กœ ์“ฐ์ง€ ์•Š๋„๋ก ํ•œ๋‹ค. ์ผ๋ฐ˜์ ์ธ ํ…์ŠคํŠธ ์ปจ๋ฒค์…˜์„ ๋”ฐ๋ฅด๋˜(e.g., ์ฒซ ๊ธ€์ž๋งŒ ๋Œ€๋ฌธ์ž๋กœ), ๋งŒ์•ฝ ๋ฌธ์ž์—ด์„ ๋ชจ๋‘ ๋Œ€๋ฌธ์ž๋กœ ํ‘œ์‹œํ•ด์•ผ ํ•œ๋‹ค๋ฉด, TextView์˜ textAllCaps ์†์„ฑ์„ ์ด์šฉํ•˜์ž.

์ž˜๋ชป๋œ ์˜ˆ

<string name="error.message.call">CALL FAILED</string>

์ข‹์€ ์˜ˆ

<string name="error.message.call">Call failed</string>

๊นŠ์€ View ๊ณ„์ธต์„ ํ”ผํ•˜์ž. ๊ฐ€๋” View๋ฅผ ํŽธ์„ฑํ•˜๊ธฐ ์œ„ํ•ด LinearLayout์„ ํ•˜๋‚˜ ๋” ์ถ”๊ฐ€ํ•˜๋ คํ•  ๊ฒƒ์ด๋‹ค. ์ด ์ƒํ™ฉ์€ ์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ ์ผ์œผํ‚จ๋‹ค:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

    <RelativeLayout
        ...
        >

        <LinearLayout
            ...
            >

            <LinearLayout
                ...
                >

                <LinearLayout
                    ...
                    >
                </LinearLayout>

            </LinearLayout>

        </LinearLayout>

    </RelativeLayout>

</LinearLayout>

๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์—์„œ ๋ช…์‹œ์ ์œผ๋กœ ์ด๋Ÿฐ ํ˜•ํƒœ๋ฅผ ๋ณด์ง€ ๋ชปํ–ˆ๋‹ค ํ•˜๋”๋ผ๋„, ๊ฒฐ๊ตญ ๋‹ค๋ฅธ View์— ๋˜ ๋‹ค๋ฅธ View๋“ค์„ ์ฑ„์šธ ๋•Œ(Java์—์„œ) ์ด๋ ‡๊ฒŒ ๋๋‚˜๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

๋‘ ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ํ”„๋กœ์„ธ์„œ๊ฐ€ ๋‹ค๋ฃจ์–ด์•ผ ํ•  UI ํŠธ๋ฆฌ๊ฐ€ ๋ณต์žกํ•ด์ง€๋ฉด์„œ ํผํฌ๋จผ์Šค ๋ฌธ์ œ๋ฅผ ๊ฒฝํ—˜ํ•˜๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค. ๋˜ ํ•˜๋‚˜์˜ ์‹ฌ๊ฐํ•œ ๋ฌธ์ œ๋Š” StackOverflowError์˜ ๊ฐ€๋Šฅ์„ฑ์ด๋‹ค.

๊ทธ๋Ÿฌ๋ฏ€๋กœ, ์ตœ๋Œ€ํ•œ View ๊ณ„์ธต์„ ์ˆ˜ํ‰ํ•˜๊ฒŒ ์œ ์ง€ํ•˜์ž: RelativeLayout๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•, optimize your layouts ๋ฐฉ๋ฒ•๊ณผ <merge> tag๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•˜์ž.

WebView์™€ ๊ด€๋ จ๋œ ๋ฌธ์ œ๋“ค์— ์œ ์˜ํ•˜์ž. ๋‰ด์Šค ๊ธฐ์‚ฌ์™€ ๊ฐ™์€ ์›นํŽ˜์ด์ง€๋ฅผ ๋ณด์—ฌ์ฃผ์–ด์•ผ ํ•  ๋•Œ, ๋ฐฑ์—”๋“œ ํ”„๋กœ๊ทธ๋ž˜๋จธ๋“ค์—๊ฒŒ "์ˆœ์ˆ˜ํ•œ" HTML์„ ์š”์ฒญํ•˜์ง€ ์•Š๊ณ  HTML์„ ์ •๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ํด๋ผ์ด์–ธํŠธ๋‹จ์—์„œ ํ”„๋กœ์„ธ์‹ฑํ•˜๋Š” ๊ฒƒ์„ ํ”ผํ•˜์ž. ApplicationContext๊ฐ€ ์•„๋‹Œ Activity๋กœ์˜ ์ฐธ์กฐ๋ฅผ ์œ ์ง€ํ•  ๋•Œ, WebViews can also leak memory. ๊ฐ„๋‹จํ•œ ํ…์ŠคํŠธ์™€ ๋ฒ„ํŠผ์„ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” WebView๋ฅผ ํ”ผํ•˜๊ณ  TextView์™€ Button์„ ์‚ฌ์šฉํ•˜๋„๋ก ํ•œ๋‹ค.

ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ

Android SDK์˜ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๋Š” ์—ฌ์ „ํžˆ ๋ฏธํกํ•œ๋ฐ, ํŠนํžˆ UI ํ…Œ์ŠคํŠธ์— ๊ด€ํ•ด์„œ๋Š” ๋”๋”์šฑ ๊ทธ๋ ‡๋‹ค. Android Gradle์€ ํ˜„์žฌ extension of JUnit with helpers for Android์„ ์‚ฌ์šฉํ•˜์—ฌ ๋งŒ๋“ค์–ด์ง„ JUnit ํ…Œ์ŠคํŠธ๋“ค์„ ์‹คํ–‰ํ•˜๋Š” connectedAndroidTest๋ผ๋Š” ํ…Œ์ŠคํŠธ Task๋ฅผ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ๋‹ค. ์ด๋Š” ๊ธฐ๊ธฐ ํ˜น์€ ์—๋ฎฌ๋ ˆ์ดํ„ฐ์— ์—ฐ๊ฒฐ๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•ด์•ผ ํ•  ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๊ณต์‹ ๊ฐ€์ด๋“œ์ธ [1] [2]๋ฅผ ๋”ฐ๋ผ ํ…Œ์ŠคํŠธํ•˜์ž.

View๊ฐ€ ์•„๋‹Œ ์œ ๋‹› ํ…Œ์ŠคํŠธ์—๋Š” Robolectric๋ฅผ ์‚ฌ์šฉํ•˜์ž. ์ด๋Š” ๊ฐœ๋ฐœ ์†๋„์˜ ๋งŒ์กฑ์„ ์œ„ํ•ด "๊ธฐ๊ธฐ์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์€" ํ…Œ์ŠคํŠธ์˜ ์ œ๊ณต์„ ์ถ”๊ตฌํ•˜๋Š” ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์ด๋‹ค. ํŠนํžˆ ๋ชจ๋ธ๊ณผ View ๋ชจ๋ธ๋“ค์˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ์— ์ ํ•ฉํ•˜๋‹ค. ํ•˜์ง€๋งŒ, UI ํ…Œ์ŠคํŠธ์—์„œ์˜ Robolectric๋ฅผ ์‚ฌ์šฉํ•œ ํ…Œ์ŠคํŠธ๋Š” ๋ถ€์ •ํ™•ํ•˜๊ณ  ๋ถˆ์™„์ „ํ•˜๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜, ๋Œ€ํ™” ์ƒ์ž ๋“ฑ์— ๊ด€ํ•œ UI ์š”์†Œ๋“ค์˜ ํ…Œ์ŠคํŠธ์—์„œ๋Š” ๋ฌธ์ œ๋ฅผ ๋ณด์ผ ๊ฒƒ์ด๊ณ , ์ด๋Š” "์–ด๋‘  ์†์—์„œ ๊ฑท๋Š” ๊ฒƒ"(์กฐ์ž‘ํ•  ๋งŒํ•œ ํ™”๋ฉด์„ ๋ณด์ง€ ์•Š๊ณ  ํ…Œ์ŠคํŠธ)์ฒ˜๋Ÿผ ๋งค์šฐ ๋ณต์žกํ•  ๊ฒƒ์ด๋‹ค.

Robotium์€ UI ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์„ ์‰ฝ๊ฒŒ ํ•ด์ค€๋‹ค. UI์˜ ์—ฐ๊ฒฐ๋œ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐ์— Robotium์ด ํ•„์š”ํ•˜์ง€ ์•Š์ง€๋งŒ, View๋ฅผ ๊ฐ€์ ธ์™€ ๋ถ„์„ํ•˜๊ณ , ํ™”๋ฉด์„ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” Robotium์˜ ๋งŽ์€ ๋„์šฐ๋ฏธ๋“ค์ด ์œ ์šฉํ•˜๊ฒŒ ์ž‘์šฉํ•  ๊ฒƒ์ด๋‹ค. ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋Š” ์ด์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ํ‘œํ˜„๋œ๋‹ค:

solo.sendKey(Solo.MENU);
solo.clickOnText("More"); // searches for the first occurence of "More" and clicks on it
solo.clickOnText("Preferences");
solo.clickOnText("Edit File Extensions");
Assert.assertTrue(solo.searchText("rtf"));

์—๋ฎฌ๋ ˆ์ดํ„ฐ

์ „๋ฌธ์ ์œผ๋กœ Android ์•ฑ์„ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, Genymotion emulator์˜ ๋ผ์ด์„ผ์Šค๋ฅผ ๊ตฌ๋งคํ•˜์ž. Genymotion ์—๋ฎฌ๋ ˆ์ดํ„ฐ๋Š” ์ผ๋ฐ˜์ ์ธ AVD ์—๋ฎฌ๋ ˆ์ดํ„ฐ์— ๋น„ํ•ด ๋น ๋ฅธ FPS๋กœ ์‹คํ–‰๋œ๋‹ค. ์•ฑ์„ ๋ฐ๋ชจํ•˜๊ณ , ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ ํ’ˆ์งˆ, GPS ํฌ์ง€์…˜์„ ์‹œํ—˜ํ•ด๋ณด๋Š” ํˆด๋“ค์„ ์ œ๊ณตํ•œ๋‹ค. ๋˜ํ•œ ์—ฐ๊ฒฐ๋œ ํ…Œ์ŠคํŠธ์— ์ด์ƒ์ ์œผ๋กœ ์ž‘๋™ํ•œ๋‹ค. ๋งŽ์€(์ „๋ถ€๋Š” ์•„๋‹˜) ๋‹ค๋ฅธ ๊ธฐ๊ธฐ๋“ค์„ ์ ‘ํ•  ๋•Œ, Genymotion์˜ ๋ผ์ด์„ผ์Šค๋Š” ์‹ค์ œ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๊ธฐ๊ธฐ๋ฅผ ๊ตฌ๋งคํ•˜๋Š” ๊ฒƒ ๋ณด๋‹ค ํ›จ์”ฌ ์ €๋ ดํ•˜๋‹ค.

์ฃผ์˜ํ•  ์ : Genymotion ์—๋ฎฌ๋ ˆ์ดํ„ฐ๋Š” Google Play Store์™€ Maps๊ณผ ๊ฐ™์€ ๋ชจ๋“  Google ์„œ๋น„์Šค๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ์ง€๋Š” ์•Š๋‹ค. ์‚ผ์„ฑ์˜ ํŠน์ • API๋ฅผ ํ…Œ์ŠคํŠธํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ์‹ค์ œ ์‚ผ์„ฑ ๊ธฐ๊ธฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Proguard ์„ค์ •

ProGuard๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ Android ํ”„๋กœ์ ํŠธ์˜ ํŒจํ‚ค์ง•๋œ ์ฝ”๋“œ๋ฅผ ์ถ•์†Œํ•˜๊ณ , ๋‚œ๋…ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค.

ProGuard๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋Š”์ง€ ์•„๋‹Œ์ง€๋Š” ํ•ด๋‹น ํ”„๋กœ์ ํŠธ ์„ค์ •์— ๋‹ฌ๋ ค์žˆ๋‹ค. ๋ณดํ†ต ๋ฆด๋ฆฌ์ฆˆ apk๋ฅผ ๋นŒ๋“œํ•  ๋•Œ gradle์„ ์„ค์ •ํ•˜๊ณ  ProGuard๋ฅผ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋‹ค.

buildTypes {
    debug {
        minifyEnabled false
    }
    release {
        signingConfig signingConfigs.release
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}

์–ด๋–ค ์ฝ”๋“œ๊ฐ€ ๋ณด์กด๋˜์–ด์•ผ ํ•˜๊ณ  ์–ด๋–ค ์ฝ”๋“œ๊ฐ€ ๋ฒ„๋ ค์ง€๊ฑฐ๋‚˜ ๋‚œ๋…ํ™”๋˜์–ด์•ผํ•  ์ง€ ๊ฒฐ์ •ํ•˜๊ธฐ ์œ„ํ•ด, ์ฝ”๋“œ์— ํ•œ ๊ฐœ ์ด์ƒ์˜ ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ๋ฅผ ์„ค์ •ํ•ด์•ผํ•œ๋‹ค. ์ด ์—”ํŠธ๋ฆฌ ํฌ์ธํŠธ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ main ๋ฉ”์†Œ๋“œ, applets, midlets, Activity์™€ ๊ฐ™์€ ๊ฒƒ๋“ค์ด๋‹ค. Android ํ”„๋ ˆ์ž„์›Œํฌ๋Š” SDK_HOME/tools/proguard/proguard-android.txt์—์„œ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ณธ ์„ค์ •์„ ์‚ฌ์šฉํ•œ๋‹ค. ์œ„์˜ ์„ค์ •์„ ์‚ฌ์šฉํ•˜์—ฌ, my-project/app/proguard-rules.pro์˜ ์‚ฌ์šฉ์žํ™”๋œ ํŠน์ • ํ”„๋กœ์ ํŠธ ProGuard ๊ทœ์น™์€ ๊ธฐ๋ณธ ์„ค์ •์— ์ถ”๊ฐ€๋กœ ์„ค์ •๋  ๊ฒƒ์ด๋‹ค.

ProGuard์™€ ๊ด€๋ จ๋œ ์ผ๋ฐ˜์ ์ธ ๋ฌธ์ œ๋Š” ์–ด๋– ํ•œ Warning๋„ ์—†์ด ๋นŒ๋“œ ์ปค๋งจ๋“œ๊ฐ€ ์„ฑ๊ณตํ–ˆ๋Š”๋ฐ๋„ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์‹œ์ž‘์‹œ์— ClassNotFoundException๋‚˜ NoSuchFieldException์™€ ๋น„์Šทํ•œ ์˜ˆ์™ธ๋กœ ํฌ๋ž˜์‹œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋Š” ๋‘ ๊ฐ€์ง€ ์ค‘ ํ•˜๋‚˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค:

  1. ProGuard๊ฐ€ ํด๋ž˜์Šค, Enum, ๋ฉ”์†Œ๋“œ, ํ•„๋“œ ํ˜น์€ ์–ด๋…ธํ…Œ์ด์…˜๋“ค์„ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค๊ณ  ์—ฌ๊ฒจ ์ œ๊ฑฐํ•œ ๊ฒƒ์ด๋‹ค.
  2. ProGuard๊ฐ€ ํด๋ž˜์Šค, Enum, ํ•„๋“œ ์ด๋ฆ„๋“ค์ด ๊ฐ„์ ‘์ ์œผ๋กœ ๊ณ ์œ ์˜ ์ด๋ฆ„๋Œ€๋กœ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ (์˜ˆ๋ฅผ ๋“ค๋ฉด Java reflection์„ ํ†ตํ•ด) ์ด๋“ค์„ ๋‚œ๋…ํ™”(์ด๋ฆ„ ๋ณ€๊ฒฝ)ํ•œ ๊ฒƒ์ด๋‹ค.

๋ฌผ์Œ์˜ ๊ฐ์ฒด๊ฐ€ ์ œ๊ฑฐ๋˜์—ˆ๋Š”์ง€ ๋ณด๋ ค๋ฉด app/build/outputs/proguard/release/usage.txt์„ ํ™•์ธํ•˜์ž. ๋ฌผ์Œ์˜ ๊ฐ์ฒด๊ฐ€ ๋‚œ๋…ํ™”๋˜์—ˆ๋Š”์ง€ ๋ณด๋ ค๋ฉด app/build/outputs/proguard/release/mapping.txt์„ ํ™•์ธํ•˜์ž.

ProGuard๊ฐ€ ํ•„์š”ํ•œ ํด๋ž˜์Šค ํ˜น์€ ํด๋ž˜์Šค ๋ฉค๋ฒ„๋“ค์„ ๋ฒ—๊ฒจ๋‚ด๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด์„œ๋Š”, ProGuard ์„ค์ •์— keep ์˜ต์…˜์„ ์ถ”๊ฐ€ํ•˜์ž:

-keep class com.futurice.project.MyClass { *; }

ProGuard๊ฐ€ ํด๋ž˜์Šค ํ˜น์€ ํด๋ž˜์Šค ๋ฉค๋ฒ„๋“ค์˜ ๋‚œ๋…ํ™”์„ ๋ง‰๊ณ ์‹ถ๋‹ค๋ฉด, keepnames์„ ์ถ”๊ฐ€ํ•˜์ž:

-keepnames class com.futurice.project.MyClass { *; }

์˜ˆ์‹œ๋กœ this template's ProGuard config๋ฅผ ํ™•์ธํ•˜์ž. ๋˜ ๋‹ค๋ฅธ ์˜ˆ์‹œ๋กœ Proguard๋ฅผ ์ฝ์–ด๋ณด์ž.

ํ”„๋กœ์ ํŠธ์˜ ์ดˆ๊ธฐ์—, ๋ฆด๋ฆฌ์ฆˆ ๋นŒ๋“œ๋ฅผ ๋งŒ๋“ค์ž. ์ด๋Š” ProGuard ๊ทœ์น™๋“ค์ด ์ค‘์š”ํ•œ ๊ฒƒ๋“ค์„ ์ •ํ™•ํ•˜๊ฒŒ ๋ณด๊ด€ํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•จ์ด๋‹ค. ๋˜ํ•œ ์–ธ์ œ๋“ ์ง€ ์ƒˆ๋กœ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํฌํ•จ์‹œ์ผฐ์„ ๋•Œ, ๋ฆด๋ฆฌ์ฆˆ ๋นŒ๋“œ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ธฐ๊ธฐ์—์„œ apk๋ฅผ ํ…Œ์ŠคํŠธํ•ด๋ณด์ž. ๋ฆด๋ฆฌ์ฆˆ ๋นŒ๋“œ๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ์•ฑ์ด "1.0" ๋ฒ„์ „์ด ๋˜๊ธฐ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ์ง€ ๋ง๊ณ , ์ˆ˜ ์ฐจ๋ก€ ์˜์™ธ์˜ ๋ฌธ์ œ๋“ค์„ ๋ฐœ๊ฒฌํ•˜๊ณ  ์ˆ˜์ •ํ•˜๋Š” ์งง์€ ์‹œ๊ฐ„์„ ๊ฐ–์ž.

ํŒ. ๋ฐฐํฌ์‹œ์— mapping.txt ํŒŒ์ผ๋“ค์€ ๋งค ๋ฆด๋ฆฌ์ฆˆ๋งˆ๋‹ค ์ €์žฅํ•˜์ž. ๊ฐ ๋ฆด๋ฆฌ์ฆˆ ๋นŒ๋“œ๋งˆ๋‹ค mapping.txt ํŒŒ์ผ์„ ๋ณด๊ด€ํ•ด๋‘๋ฉด, ์‚ฌ์šฉ์ž๊ฐ€ ๋ฒ„๊ทธ๋ฅผ ๋งŒ๋‚˜๊ณ  ์•Œ์•„๋ณด๊ธฐ ํž˜๋“  ์Šคํƒ ํŠธ๋ ˆ์ด์Šค๋ฅผ ๋ณด๋‚ด์™”์„ ๋•Œ ๋ฌธ์ œ๋ฅผ ํ™•์‹คํ•˜๊ฒŒ ๋””๋ฒ„๊น…ํ•  ์ˆ˜ ์žˆ๋‹ค.

DexGuard. ๋ฆด๋ฆฌ์ฆˆ ์ฝ”๋“œ๋ฅผ ์ตœ์ ํ™”ํ•˜๊ณ , ํŠนํžˆ ์•Œ๊ธฐ ์–ด๋ ต๊ฒŒ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ํ•˜๋“œ์ฝ”์–ดํ•œ ํˆด์ด ํ•„์š”ํ•˜๋‹ค๋ฉด, ProGuard๋ฅผ ๋นŒ๋“œํ•œ ๊ฐ™์€ ํŒ€์—์„œ ๋งŒ๋“  ์ƒ์—… ์†Œํ”„ํŠธ์›จ์–ด์ธ DexGuard๋ฅผ ๊ณ ๋ คํ•ด๋ณด์ž. ์ด๋Š” 65,000 ๋ฉ”์†Œ๋“œ ์ˆ˜ ์ œํ•œ์„ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Dex ํŒŒ์ผ๋“ค์„ ์‰ฝ๊ฒŒ ๋‚˜๋ˆˆ๋‹ค.

Thanks to

Antti Lammi, Joni Karppinen, Peter Tackage, Timo Tuominen, Vera Izrailit, Vihtori Mรคntylรค, Mark Voit, Andre Medeiros, Paul Houghton and other Futurice developers for sharing their knowledge on Android development.

License

Futurice Oy Creative Commons Attribution 4.0 International (CC BY 4.0)

Translation

Translated to Korean (ko) by Minsoo Park.

Original content by Futurice Oy.

About

Do's and Don'ts for Android development, by Futurice developers

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Java 100.0%