Search
🏍️

Android 그라운드 룰

Android

기능 개발이 우선이다.
서로 존중하는 태도를 가진다.
이해가 때까지 충분히 설명한다.
리뷰를 남길 때 제안하는 형식으로 남긴다.
문서화를 잘하자. 논의사항, 고민을 단순히 기록하는 것도 문서화의 일종이다.
협업을 위해 PR 메시지에 스크린 샷을 추가한다.

Package

package 이름은 소문자로 작성한다.
package com.example.chongdae.domain
Kotlin
복사
underscore(_)는 사용하지 않는다.
// WRONG! package com.example.chongdae.domain_module
Kotlin
복사

Architecture

코드의 Architecture는 MVVM을 사용한다.

ViewModel

ViewModel(Presentation Layer)에는 안드로이드 프레임워크 관련 코드가 없어야함
다만 데이터에 해당되는 R.string.xxxR.dimen.xxxR.color.xxx들은 사용할 수 있도록 함
(R.id.xxxR.layout.xxx는 불가)

Model Mapping 정의/구조

Model은 각각의 layer에 만들어져서 mapping되도록 구성한다
예) User라는 개념의 model에 대한 각각 layer에서의 정의
Domain
data class User(){}
Kotlin
복사
Presentation
data class UserModel(){} fun User.toPresentation(): UserModel fun UserModel.toDomain(): User
Kotlin
복사
Data
data class UserEntity(){} fun UserEntity.toDomain(): User fun User.toData(): UserEntity
Kotlin
복사
Remote
data class UserResponse(){} fun UserResponse.toData(): UserEntity fun UserEntity.toRemote(): UserResponse
Kotlin
복사
단, 데이터를 갱신할때 parameter로 사용되는 Param개념은 뒤에 Param으로 붙이도록 변경 : 또한 Remote에서의 해당 이름은 xxxxParamRequest로 정의
Local
data class UserPref(){} fun UserPref.toData(): UserEntity fun UserEntity.toPref(): UserPref
Kotlin
복사
단, 데이터를 갱신할때 parameter로 사용되는 Param개념은 뒤에 Param으로 붙이도록 변경
또한 Remote에서의 해당 이름은 xxxxParamRequest로 정의

Listener 네이밍

interface OnXXXXListener 같은 형식을 따른다
on[명사][동사]()
on[명사][동사 과거형]()

Formatting

Ktlint hook을 적용하여 commit한다.

함수

비교 (Boolean)

엘비스 연산자는 최대한 지양하고, 조건문의 경우 명시적으로 비교연산자를 사용한다.
Boolean의 경우 if (a?.b?.isTraded ?: false) 보다는 if (a?.b?.isTraded == true) 와 같은 방식으로 구현한다.

함수 이름

축약어는 지양한다.
ViewModel을 observe()할때 모아놓는 함수 이름
observeXXX()
Kotlin
복사
서버에서 데이터를 불러올때 함수 이름
fetchXXX()
Kotlin
복사
서버에 저장할때 함수 이름
saveXXX()
Kotlin
복사
Return이 있는 데이터를 불러올때 함수 이름
getXXX()
Kotlin
복사
특정 객체를 찾는 함수이름
findXXX()
Kotlin
복사
복수형을 가져올때는 뒤에 s를 붙인다 (List, Array와 같은 자료형 지양)
getBrands() // O getBrandList() // X
Kotlin
복사

Resource

Layout

<WHAT>_<WHERE>
WHAT
Prefix
설명
activity_
Activity에서 쓰이는 layout
fragment_
Fragment에서 쓰이는 layout
dialog_
Dialog에서 쓰이는 layout
view_
CustomView에서 쓰이는 layout
item_
RecyclerView, GridView, ListView등에서 ViewHolder에 쓰이는 layout
layout_
<include/>로 재사용되는 공통의 layout
예시
activity_main: MainActivity의 layout
fragment_request: RequestFragment의 layout
dialog_contact: 문의안내 Dialog의 layout
view_rating: 커스텀으로 만든 RatingView의 layout
item_my_car: 내차량 목록에서 사용되는 각각의 item의 layout
layout_dealer_review: 재사용되는 딜러리뷰 layout
ID
<WHAT>_<DESCRIPTION>
View의 대문자를 축약하여 <WHAT>의 Prefix로 사용한다.
아래 이름규칙을 적용한다.
1.
Android의 View는 CamelCase의 대문자를 축약한 형태로 정한다.: TextView -> tv_
2.
만약 View의 이름이 Space, Switch와 같이 1개의 대문자만 존재한다면 모두 소문자인 아이디로 정한다.: Switch -> switch_
3.
CustomView는 전체View의 이름을 snake case이름으로 정한다.: MyCustomView -> my_custom_view(만약 1개의 xml에 같은 여러 CustomView가 존재한다면 <WHAT>_<DESCRIPTION>의 형태로 정한다.)
4.
아래표에 해당 View의 Prefix가 정의되어 있지 않다면 팀에서 상의해서 이름을 정한뒤 추가한다.
WHAT
View
Prefix
TextView
tv_
ImageView
iv_
CheckBox
cb_
RecyclerView
rv_
EditText
et_
ProgressBar
pb_
FrameLayout
fl_
NestedScrollView
nsv_
Space
space_
Switch
switch
AbcDeFgh
adf_
Abcdef
abcdef_
MyCustomView
my_custom_view
YourView
your_view
기타
해당 View를 특정기능과 상관없이 VISIBLE/GONE등의 View의 용도로 사용한다면 view_xxx로 사용하는것도 허용한다.
버튼기능을 위한 View는 ImageView, TextView로만 사용한다. (Button, ImageButton은 존재의 의미가 없음)
예시
iv_close: 닫기 ImageView
tv_select: 선택 TextView
rv_car_list: 자동차 목록 RecyclerView
view_etc_model: 기타 모델 화면 LinearLayout

Drawable

<WHAT>(_<WHERE>)_<DESCRIPTION>(_<SIZE>)
이미지가 여러군데에서 활용될 경우, <WHERE>는 생략 가능하다.
이미지의 크기가 1개밖에 없는 경우, <SIZE>는 생략 가능하다.
What
Prefix
설명
btn_
버튼으로 쓰이는 이미지
ic_
버튼이 아닌 화면에 보여지는 이미지
bg_
버튼이 아닌 화면에 보여지는 이미지
img_
실제사진이거나 아이콘형태가 아닌 일러스트형태의 이미지
div_
divider로 활용되는 이미지

Selector

배경이나 버튼에서 View의 상태에 따라서 drawable이 변해야 하는 경우에 대한 이름은 아래와 같다.
상태
Suffix
Normal
_normal
Pressed
_pressed
Focused
_focused
Disabled
_disabled
Selected
_selected
Background
배경색이 pressed상태에 따라서 white -> sky_blue로 변하는 경우는 bg_white_to_sky_blue.xml로 한다.
배경이 white색의 24dp로 테두리를 그리는 경우는 bg_white_radius_24dp.xml로 한다.
배경이 투명하며 배경의 선만을 sky_blue색의 8dp로 테두리를 그리는 경우는 bg_stroke_sky_blue_radius_8dp.xml로 한다.
기타
img_xxx의 경우 파일의 크기가 큰경우가 많으므로 tinypng에서 파일크기를 줄인뒤에 추가 해주어야 한다. (GitHub imgbot을 사용한다면 생략 가능)
대부분 용량이 큰 파일이어서 xxxhdpi에만 넣는다.
예시
btn_call_normal.png: 전화걸기 버튼 이미지
btn_call_pressed.png: 전화걸기 버튼 눌렸을때의 이미지
btn_call.xml: 전화걸기 버튼 이미지의 selector xml
ic_dealer_gift.png: 딜러가 보내준 기프티콘을 보여줄때 표시되는 이미지
img_splash_chart.png: 스플래시 화면에서 보여지는 차트 이미지

Dimension

<WHERE>_<DESCRIPTION>_<WHAT>
여러 군데에서 재사용되는 개념이라면 변수로 정의해서 @dimen/xxx와 같이 사용
그렇지 않다면 명시적으로 16dp와 같이 작성

Margin/Padding

대부분의 margin/padding은 아래 정의된 space_xxx로만 사용되도록 한다.
<dimen name="space_x_small">8dp</dimen> <dimen name="space_small">12dp</dimen> <dimen name="space_median">16dp</dimen> <dimen name="space_s_large">18dp</dimen> <dimen name="space_large">20dp</dimen> <dimen name="space_x_large">24dp</dimen>
XML
복사
그외에 특정 화면에서 위의 값을 따르지 않는경우, <WHERE>_<DESCRIPTION>_<WHAT>의 규칙으로 만든다.
<dimen name="register_car_item_car_model_start_padding">40dp</dimen> <dimen name="register_car_item_grade_start_padding">56dp</dimen> <dimen name="register_car_item_car_detail_start_padding">72dp</dimen>
XML
복사
2번이상 쓰이는경우는 dimen에 정의해주는 것을 강제하고 1번만 쓰이는경우에는 xml코드에 넣어도 괜찮은것으로 한다.
Height/Size
높이만 지정할때는 height, 1:1 비율로 같은 값이 들어갈때는 size로 한다.
<dimen name="toolbar_height">56dp</dimen> <dimen name="register_input_view_default_height">280dp</dimen> <dimen name="register_input_view_collapse_height">200dp</dimen> <dimen name="dealer_profile_image_size">48dp</dimen>
XML
복사

String

<WHERE>_<DESCRIPTION>
특정화면에서 쓰이는 텍스트 아니라 여러군데에서 공통으로 재사용될 텍스트라면 all_<DESCRIPTION>로 이름을 짓는다
예시
permission_dialog_camera_title: 카메라권한을 요구하는 Dialog의 제목
permission_dialog_camera_description: 카메라권한을 요구하는 Dialog의 설명내용
all_yes: 네
all_ok_understand: 여러 Dialog에서 네, 알겠습니다로 쓰이는 공통의 텍스트
문단
문단형태의 긴 문자열로 개행(\n)이 필요한 경우, \n을 다음줄의 앞에 쓴다.
<string name="sample">문단 첫번째줄 \n문단 두번째줄 \n문단 세번째줄</string>
XML
복사

Theme/Style

Theme는 theme.xml, Style은 style.xml에 추가한다.
1번만 쓰이는 경우에는 style을 만들지 않는다. (단, 앞으로 재사용될 가능성이 높은 경우에는 가능)
모든 style은 parent를 갖는다.
Naming
style의 이름은 parent의 이름패턴과 맞춘다
<style name=”Widget.HeyDealer.Button” parent=”@style/Widget.AppCompat.Button”> ... </style>
XML
복사
parent에서 일부 내용만 수정하고자 하는경우, parent이름 뒤에 달라진 내용의 내용을 추가해준다
<style name="Theme.HeyDealer.Transparent" parent="Theme.HeyDealer"> ... </style>
XML
복사
Base Style과 Theme의 경우는 앞에 Base를 붙인다.
<style name="Base.Theme" parent="..." /> <style name="Base.Theme.Transparent">...</style> <style name="HeyDealerTheme" parent="Base.Theme">...</style> <style name="HeyDealerTheme.Transparent" parent="Base.Theme.Transparent" /> <style name="Base.TextAppearance.HeyDealer" parent="...">...</style> <style name="Base.TextAppearance.HeyDealer.Headline">...</style> <style name="TextAppearance.HeyDealer.Headline1" parent="Base.TextAppearance.HeyDealer.Headline">...</style> <style name="TextAppearance.HeyDealer.Headline2" parent="Base.TextAppearance.HeyDealer.Headline">...</style>
XML
복사

Attribute

Attribute이름은 camelCase로 한다.
<attr name="numStars" format="integer" />
XML
복사
기존에 정의되어있는 android:xxx와 같은 동작을 유도하는 경우, 이 tag를 재사용한다.
<declare-styleable name="SpannedGridLayoutManager"> <attr name="android:orientation" /> ... </declare-styleable>
XML
복사

기타

android:xxxLeft/android:xxxRight 대신 android:xxxStart/android:xxxEnd로 사용한다.(기타 Left/Right로 사용하는 부분 모두)

Test

테스트 케이스 이름

JUnit5를 사용하는 경우에는 @DisplayName을 통해 테스트 케이스 이름을 정의하고 함수명은 중복되지 않으면서 최대한 유지보수가 필요없도록 만들어준다.
Do
@DisplayName("1 더하기 2는 3이어야 한다") @Test fun add() { /* Test Code */ } @DisplayName("1 더하기 2는 3이어야 한다") @Test fun test1() { /* Test Code */ }
Kotlin
복사
Do not
@DisplayName("1 더하기 2는 3이어야 한다") @Test fun `1 더하기 2는 3이어야 한다`() { /* Test Code */ }
Kotlin
복사
JUnit4를 사용하는 경우에는 선택의 여지가 없으므로 무조건 함수명으로 테스트 케이스 이름을 정의한다. 공백의 경우 underscore(_)를 사용한다.
@Test fun `1_더하기_2는_3이어야_한다`() { /**/ }
Kotlin
복사
클래스명이나 함수명은 변경될 수 있으므로 테스트 케이스 이름에는 클래스명이나 함수명을 넣는 것을 최대한 지양한다.
Do (recommended)
@Test fun `1_더하기_2는_3이어야_한다`() { /**/ }
Kotlin
복사
Do not (optional)
@Test fun `Calculator에서 add(1, 2)를 호출하면 3을 반환해야 한다`() { /**/ }
Kotlin
복사

테스트 클래스 이름

IDE의 기본을 따라 Calculator에 대한 테스트 클래스는 CalculatorTest로 한다.
// Calculator.kt class Calculator { /* ... */ }
Kotlin
복사
// CalculatorTest.kt class CalculatorTest { /* ... */ }
Kotlin
복사
top-level 함수만 모여있는 CalculatorExtension.kt 파일에 대한 테스트 클래스 이름은 CalculatorExtensionTest.kt로 한다.
// CalculatorExtension.kt fun Calculator.foo() { /* ... */ } fun Calculator.bar() { /* ... */ }
Kotlin
복사
// CalculatorExtensionTest.kt class CalculatorExtensionTest { /* ... */ }
Kotlin
복사
IDE에서는 CalculatorExtension.kt 파일에 대한 테스트 클래스를 기본적으로 CalculatorExtensionKtTest.kt로 인식한다. 그럼에도 불구하고 CalculatorExtensionTest.kt와 같이 사용하는 이유는 다음과 같다.
IDE 기능 때문에 Kt를 붙이는 것이 code smell로 여겨짐
IDE 기능을 쓰지 못한다고 해서 테스트 코드 작성에 치명적인 이슈는 되지 않음
출처 및 참고 :