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.xxx, R.dimen.xxx, R.color.xxx들은 사용할 수 있도록 함
◦
(R.id.xxx, R.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로 한다.
기타
예시
•
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 기능을 쓰지 못한다고 해서 테스트 코드 작성에 치명적인 이슈는 되지 않음
출처 및 참고 :