-
Notifications
You must be signed in to change notification settings - Fork 2
안드로이드 코드 컨벤션
이 문서는 [PRND 컨벤션 스타일](https://github.com/PRNDcompany/android-style-guide/tree/main)을 기반으로 해서 만들어졌습니다.
• Boolean의 경우 if (a?.b?.isTraded ?: false)
보다는 if (a?.b?.isTraded == true)
와 같은 방식으로 구현한다.****
- user(x) member(o)
- eventFriend(x) companion(o)
- conference(x) event(o)
- Career(x) Activity(o)
Computed Property VS Function
- 행위를 나타내는 개념이면 function
- 상태나 값 등 정보를 가져오는 개념이면 Computed Property
- invoke() 함수는 nullable이 아닐 때에는 생략해서 사용한다.
val foo: () -> Unit
val bar: (() -> Unit)?
foo.invoke() // X
foo() // O
bar?.invoke() // O
- package 이름은 소문자로 작성한다.
ex) package kr.co.prnd.domain
- underscore(
_
)는 사용하지 않는다.
// WRONG!
package kr.co.prnd.domain_module
- 예외적으로 불가피하게 연결된 단어를 붙여서 사용해야 하는 경우에는 camelCase로 처리한다.
ex) package com.example.myProject
• [Kotlin의 코딩컨벤션](https://kotlinlang.org/docs/coding-conventions.html#naming-rules) 에서는 **camelCase를 허용하고 있고, [안드로이드의 코딩컨벤션](https://developer.android.com/kotlin/style-guide#package_names) 에서는 **항상 소문자로만 작성하도록 나와 있으나 팀 내부의 규칙에 따라 카멜 케이스를 허용할 수 있다
- ViewModel을 observe()할 때 모아놓는 함수 이름
setupXXX()
- 서버에서 데이터를 불러올 때 함수 이름
fetchXXX()
- 서버에 저장할 때 함수 이름
saveXXX()
- 서버에 업데이트할 때 함수 이름
updateXXX()
- Return이 있는 데이터를 불러올 때 함수 이름
getXXX()
- 특정 객체를 찾는 함수 이름
findXXX()
- 복수형을 가져올 때는 뒤에 s를 붙인다
getBrands() // O
getBrandList() // X
- Raw 값으로부터 enum을 찾을 때 함수 이름은
find()
로 한다.
enum class Color {
RED, BLUE, GREEN;
fun find(rawColor: String): Color = when (rawColor) {
"red" -> RED
"blue" -> BLUE
"green" -> GREEN
else -> throw IllegalArgumentException("invalid color: $rawColor")
}
}
- function을 1개만 가진 경우,
fun interface OnXXXListener
- function을 2개 이상 가진 경우,
interface XXXListener
- publisher가 이벤트만 전달하고 listener가 전적인 책임을 처리할 때
- 이벤트를 handle하는 주체가 listen하고 있는 곳 일 때
fun onClick()
fun onFocusChange()
fun onScrollChange()
fun onAnimationStart()
fun onTextChange()
- 생성자, 함수에서 Parameter를 정의할 때 한 줄로 정의 가능하면 한 줄, 그렇지 않으면 각 parameter별로 개행한다.
- 한 줄에 들어가는 when 분기는 중괄호를 사용하지 않는다.
when (value) {
0 -> return
// ...
}
- 여러 개의 조건을 동시에 사용하는 경우
>
를 포함한 블록은 내려서 작성한다.
when (value) {
foo -> // ...
bar,
baz
-> return
}
모든 네이밍에 있어 CamelCase로 작성한다
// Right!
XmlHttpRequest
XxxDao
// Wrong!
XMLHTTPRequest
XxxDAO
- Context류의 parameter를 가장 앞에 위치한다. (Context, Activity, Fragment, View등)
- Callback류의 parameter를 가장 뒤에 위치한다. (XXXListener, XXXCallback, XXXSubject등)
public Car getCar(Context context, int hashId); public void loadCar(Context context, int hashId, CarListener listener);
- Key-Value로 활용되는 컴포넌트들의 Key는
static final String KEY_XXX
로 정의한다. - Key로 정의된 이름의 String값은 동일하게 맞춰준다.
- Deeplink에서 사용되는 Query Parameter의 경우
aaa://bb?key1=value1&key2=value2
와 같은 방식으로 전달되기 때문에 query key와 맞춰준다.
public static final String KEY_HASH_ID = "KEY_HASH_ID"; public static final String KEY_TRADE = "KEY_TRADE"; public static final String QUERY_REFERRER = "referrer"; // deeplink
public static final String KEY_HASH_ID = "KEY_HASH_ID";
public static final String KEY_TRADE = "KEY_TRADE";
public static final String QUERY_REFERRER = "referrer"; // deeplink
- Activity를 실행할 때에는, 해당 Activity에
startActivity()
를 구현하고 이를 사용한다.
public static void startActivity(Context context, @DrawableRes int photoResourceId) {
Intent intent = new Intent(context, PhotoDetailActivity.class);
intent.putExtra(KEY_PHOTO_RESOURCE_ID, photoResourceId);
context.startActivity(intent);
}
- BroadcastReceiver를 실행할 때에는, 해당 Broadcast에 sendBroadcast() 를 구현하고 이를 사용한다.
class XxxBroadcastReceiver: BroadcastReceiver() {
companion object {
private const val ACTION = "com.example.examples.XxxBroadcastReceiver"
fun sendBroadcast(context: Context) {
context.sendBroadcast(android.content.Intent(ACTION))
}
}
}
- Fragment에
KEY_
를 넣어주는 경우, 해당 Fragment에newInstance()
를 구현하고 이를 사용한다. - Fragment 생성자의 Parameter로 넘기지 않는다. [Best Practice to Instantiate Fragments with Arguments in Android](https://gunhansancar.com/best-practice-to-instantiate-fragments-with-arguments-in-android/)
public static UserFragment newInstance(User user) {
UserFragment fragment = new UserFragment();
Bundle args = new Bundle();
args.putParcelable(KEY_USER, user);
fragment.setArguments(args)
return fragment;
}
- 많은 operator의 연산으로 줄바꿈이 필요한 경우, operator 전에 줄바꿈한다.
int longName = anotherVeryLongVariable + anEvenLongerOne - thisRidiculousLongOne
+ theFinalOne;
- 연산자의 경우는 줄바꿈이 필요한 위치부터 줄바꿈한다.
- Builder/RxJava 등 여러 함수를 chaining으로 사용하면서 줄바꿈이 필요한 경우,
.
전에 줄바꿈한다.
ImageLoader.load(user.getProfileUrl())
.placeholder(R.drawable.img_user_placeholder)
.fitCenter()
.into(binding.ivUser);
- 특정기능을 수행하거나 상태를 관리하거나 분리되어 동작을 수행하는 클래스에 대한 사용처별 이름을 정의한다.
-
public static void AAA
등으로 쓰이는 여러곳에서 사용되는 util성 기능을 보아둔 클래스 -
aa.bb.cc.util
패키지에 모두 모아둔다. - 예)
DateFormatUtil
,PixelUtil
,BitmapUtil
등
- 특정 패키지나 기능에서 한정되어 사용되는
public static void AAA
클래스 - 공통으로 쓰이지 않고 특정 기능의 코드를 분리하기 위한 용도로 사용한다.
- 예)
NotificationChannelHelper
등
- 항상 내부에서 instance로 만들어서 관리되는 용도
- 내부적으로 state 혹은 information을 가지고 있어서 호출한 곳에서의 상태에 따라서 관리되는 값을 변경하고 반영하는 작업을 해준다.
- 예)
RegisterStepManager
,RegisterCarInfoConfirmManager
등
- 데이터 소스 레이어와 데이터 레이어 간 Model 변경은 {layer}/mapper/XxxMapper.kt 파일로 관리한다.
fun ProductResponse.toData(): Product {
...
}
-
if (isChecked == false)
와 같은 코드는 명백한 Lint warning이므로 사용하지 않는다.
if (isUnchekced)
- 조건문에
!
를 넣는 것 대신 아래와 같은 규칙으로 코드를 작성한다.
- 조건문에서 체크되는 boolean 변수/함수는 항상 긍정문으로 작성한다.
if(isChecked())
- 이미 부정문으로 작성되어 있는 함수가 있다면 이를 재사용하여 함수를 작성한다.
if(isUnchekced())
private boolean isUnchekced(){
return !isChecked();
}
- try/catch를 사용하지 않고, sealed class로 결과를 감싸서 관리, 반환한다.
- 그렇지 않을 경우, nullable한 값을 반환한다.
-
import xx.xx.xx;
는 사용하지 않는다. ([Avoid static imports](https://carlosbecker.com/posts/avoid-static-imports/))
enum class Color {
RED, ORANGE, GREEN;
}
val color = Color.RED // O
val color = RED // X
- 라이브러리를 추가할때 꼭 외부 라이브러리의 라이센스 고지를 추가한다.
- 라이브러리의 버전은 +로 적지 않고 명시한다.
implementation 'gun0912.ted:tedpermission-rx2:2.2.0' <- O //implementation 'gun0912.ted:tedpermission-rx2:2.+' <- X
아래 사항들을 제외한 이외의 설정들은 자율에 맡긴다.
불필요한 code change를 줄이기 위해 trailing comma를 사용한다. 예를 들면, trailing comma를 쓰지 않은 클래스에 새로운 필드를 추가하려면 관련없는 위쪽 라인에 ,
를 추가해주어야 하는데 이러한 일을 피하기 위함이다.
- Preferences | Editor | Code Style | Kotlin | Other
- Use trailing comma 체크
// 설정 전
data class User(
val name: String,
val email: String
)
// 설정 후
data class User(
val name: String,
val email: String,
)
https://week-year.tistory.com/135
- Color 명시 → 디자이너가 정해준 이름 → snake case 로 명명
- 아이콘 : ic_
- 이미지 : img_
- 배경 : bg_
- WHAT은 축약해서 작성한다. ex) tv_login_logo
- textView → tv
- imageView → iv
- fragmentLayout → fl
- viewPager → vp
- editText → et
- recyclerView → rv
- bottomNavigationView → bnv
- linearLayout → ll
-
data
- 리포지토리에서 반환하는 타입에 따라 패키지를 나눔
- 반환하는 타입에서는 dto패키지 말고 다른 패키지를 안만듦
-
presentation
- ui
- login
- uistate(패키지)
- UserUiState
- XxxUiState
- viewmodel
- activity
- uistate(패키지)
- main
- mypage
- friends
- 명함
- login
- util
- 일반적인 유틸리티성 클래스가 아닌, BindingAdapter와 같은 공통으로 사용되는 기타 클래스를 관리한다.
- ui