-
Notifications
You must be signed in to change notification settings - Fork 0
✌ 제이콥과 브라우니의 안드로이드 컨벤션 ✌
JinHo Jeong edited this page Feb 2, 2025
·
3 revisions
- Kotlin 2.0.0v, Kotlin Coroutines, Flow
- MVI + Orbit
- Gradle Convention Plugin(build-logic), VersionCatalogs
- Dagger Hilt (KSP)
- Compose Navigation
- Firebase Analytics, Crashlytics, App Distribution
- Ktor + Kotlin Serialization
- DataStore Preference
- Material3, Lottie, Coil
- KtLint, DeteKt
- Data 계층 중 통신 모델 클래스는
Dto
로 끝나고, 내부에 저장하는 모델 클래스는Entity
로 끝나야 한다. - Domain 계층의 모델 클래스는 객체 이름 그대로 사용한다.
- UI 계층의 모델클래스는
UiModel
로 끝나야 한다.
- package 이름은 소문자로 작성합니다.
- 하지만 꼭 여러 단어로 된 이름을 사용해야 한다면 camelcase로 정의하며, 모듈 네이밍의 경우 대시
-
를 사용합니다.
- 출발지 Model에서 Extension을 통해 구현한다.
- 이름은 to + [도착지 모델]으로 구성한다.
// Do
class UserResponse {
fun toUser(): User = User(...)
}
- UseCase는 [현재 동사] + [명사/대상(선택사항)] + UseCase로 작성한다.
- UseCase 클래스는 단 하나의 invoke UseCase 함수를 가진다.
- UseCase는 Functional Naming Rule을 사용한다.
class GetLatestNewsWithAuthorsUseCase(
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository
) {
fun invoke() {...}
}
- 함수 이름은 camelCase로 작성하며 일반적으로 동사 또는 동사구입니다.
- PascalCase, underscore(
_
)는 사용하지 않습니다. - 단 Composable 함수는 class와 같은 이름 형식을 사용한다.
- 상수 이름은
UPPER_SNAKE_CASE
로 작성하며 일반적으로 명사 또는 명사구입니다. - 상수 값은
object
내부 또는 최상위 선언으로만 정의할 수 있습니다. 상수의 요구사항을 충족하지만class
의 내부에 정의된 값은 상수가 아닌 이름을 사용해야 합니다. - 상수는
const
를 사용해야 합니다.
const val NUMBER = 5
val NAMES = listOf("Alice", "Bob")
val AGES = mapOf("Alice" to 35, "Bob" to 32)
val COMMA_JOINER = Joiner.on(',') // Joiner is immutable
val EMPTY_ARRAY = arrayOf()
- 상수가 아닌 이름은 camelCase로 작성하며 일반적으로 명사 또는 명사구입니다.
val variable = "var"
val nonConstScalar = "non-const"
val mutableCollection: MutableSet = HashSet()
val mutableElements = listOf(mutableInstance)
- 개념적으로 동일한 두 개의 속성이 있지만 하나는 공용 API의 일부이고 다른 하나는 구현 세부 정보인 경우 접두사로 underscore(
_
)를 사용합니다.
private val _elementList = mutableListOf<Element>()
val elementList: List<Element>
get() = _elementList
- Composable 함수의 이름은 첫글짜가 대문자인 PascalCase로 사용한다.
- 동사로 시작하면 안되고 무조건 명사로만 이루어져야 한다
// DO
@Composable
fun FancyButton(text: String, onClick: () -> Unit) {
@Composable
fun BackButtonHandler(onBackPressed: () -> Unit) {
// DON'T
@Composable
fun fancyButton(text: String, onClick: () -> Unit) {
@Composable
fun RenderFancyButton(text: String, onClick: () -> Unit) {
- 반환타입이 있을시 일반 함수와 같은 이름 형식을 갖는다.
// DO
@Composable
fun defaultStyle(): Style {
// DON'T
@Composable
fun Style(): Style {
- 반환 값을 remember하는 경우 이 이름 컨벤션을 따라간다
// DO
@Composable
fun rememberCoroutineScope(): CoroutineScope {
- CompositionLocal이 들어가면 안되고 Local를 접미사로 사용하면 안된다
// DO
val LocalTheme = staticCompositionLocalOf<Theme>()
// DON'T
val ThemeLocal = staticCompositionLocalOf<Theme>()
- 빈 블록 또는 블록 형식 구문은 K&R 스타일이어야 합니다.
try {
doSomething()
} catch (e: Exception) {} // WRONG!
try {
doSomething()
} catch (e: Exception) {
} // Okay
- 표현식으로 사용되는
if/else
조건문에서는 전체 표현식이 한 줄에 들어가는 경우에만 중괄호를 생략할 수 있습니다.
val value = if (string.isEmpty()) 0 else 1 // Okay
val value = if (string.isEmpty()) // WRONG!
0
else
1
-
else if
사용을 지양하고,when
으로 변환하는 것을 지향한다. -
else
의 사용을 지양하고,if + return
으로 치환하는 것을 지향한다.
- 함수 서명이 한 줄에 들어가지 않으면 각 매개변수 선언을 한 줄에 하나씩 표시합니다.
- 이 형식으로 정의된 매개변수에서는 단일 들여쓰기를 사용해야 합니다. 닫는 괄호 및 반환 유형은 추가 들여쓰기 없이 한 줄에 하나씩 입력됩니다.
fun <T> Iterable<T>.joinToString(
separator: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = ""
): String {
// …
}
- 한 줄에 들어가지 않을 경우 연산자 뒤에서 줄바꿈하고 들여쓰기를 사용합니다.
private val defaultCharset: Charset? =
EncodingRegistry.getInstance().getDefaultCharsetForPropertiesFiles(file)
- 각 클래스는 다음과 같은 순서를 따른다.
- 속성 선언과 초기화 블록
- 보조 생성자
- 메서드 선언
- Companion object
class MyClass {
// 속성 선언
private val property1: String
// 초기화 블록
init {
property1 = "Hello"
property2 = 42
}
// 보조 생성자
constructor(param1: String) { ... }
// 메소드 선언
fun method1() { ... }
// Companion object
companion object { ... }
// Nested classes
class NestedClass { ... }
}
- Builder등 여러 함수를 chaining으로 사용하면서 줄바꿈이 필요한 경우,
.
전에 줄바꿈한다.
ImageLoader
.load(user.getProfileUrl())
.placeholder(R.drawable.img_user_placeholder)
.fitCenter()
.into(binding.ivUser)
- Default값이 없는 parameter
- Modifier
- Default값이 있는 parameter
- Lambda
fun Test(
val text: String,
val modifier: Modifier = Modifier,
val minLine: Int = 1,
val content: @Composable () -> Unit
)
- Composable에 State를 그대로 넘기지 않고 필요할시 이렇게 넘기기.
//DO
@Composable
fun Main() {
val (text, onTextChange) = remember {
mutableStateOf("")
}
Test(text, onTextChange)
}
@Composable
fun Test(
text: String,
onTextChange: (String) -> Unit
) {}
// DON'T
@Composable
fun Main() {
val text = remember {
mutableStateOf("")
}
Test(text)
}
@Composable
fun Test(
text: State<String>
) {}
- 여러개 State를 넘길때는 class로 묶어서 넘기기.
// Before
@Composable
fun VerticalScroller(
scrollPosition: Int,
scrollRange: Int,
onScrollPositionChange: (Int) -> Unit,
onScrollRangeChange: (Int) -> Unit
) { }
// After
@Stable
interface VerticalScrollerState {
var scrollPosition: Int
var scrollRange: Int
}
@Composable
fun VerticalScroller(
verticalScrollerState: VerticalScrollerState
) {
- 유틸 클래스는 사용하는 클래스가 3개이상일 때 사용을 고려한다.
- 그 이전까지는 사용 클래스 내에서 Private 함수로 Extension을 만들어 사용한다.
- 1줄에 100자를 넘지 않도록 작성한다.
- 코드간의 간격은 2줄이상 간격이 발생하지 않도록 한다.(최대 1줄 줄바꿈)
- 파라미터 개수와 상관없이 100자 이상이면 개행한다.
- 파라미터가 2개 이상이면 이름을 명시한다.
private fun main() {
println(Hello(name = "jaycob", text = "world"))
}
@Immutable와 @Stable를 통해 recomposition 횟수를 줄이기
Compose
- https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md
- https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md
Android
Kotlin
Architecture