λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
Kotlin

μ΄νŽ™ν‹°λΈŒ μ½”ν‹€λ¦° 2μž₯ - 가독성

by 주발2 2025. 6. 14.
λ°˜μ‘ν˜•


μ•„μ΄ν…œ 11. 가독성을 λͺ©ν‘œλ‘œ μ„€κ³„ν•˜λΌ

λ‘œλ²„νŠΈ λ§ˆν‹΄μ˜ 클린 μ½”λ“œ ... "κ°œλ°œμžκ°€ μ½”λ“œ μž‘μ„±ν•˜λŠ” λ°λŠ” 1λΆ„ κ±Έλ¦¬μ§€λ§Œ, μ½λŠ” λ°λŠ” 10뢄이 κ±Έλ¦°λ‹€."

κ°€λ…μ„±μ΄λž€, μ½”λ“œλ₯Ό 읽고 μ–Όλ§ˆλ‚˜ λΉ λ₯΄κ²Œ 이해할 수 μžˆλŠ”μ§€λ₯Ό μ˜λ―Έν•œλ‹€.

// A
if (persion != null && person.isAdult) {
  view.showPerson(person)
} else {
  view.showError()
}

// B
person?.takeIf { it.isAdult }
  ?.let(view::showPerson)
  ?: view.showError()

Aκ°€ 읽고 μ΄ν•΄ν•˜κΈ°κ°€ μ‰¬μš΄λ°, 기본적으둜 '인지 λΆ€ν•˜'λ₯Ό μ€„μ΄λŠ” λ°©ν–₯으둜 μ½”λ“œλ₯Ό μž‘μ„±ν•˜λΌκ³  ν•œλ‹€.

100% 곡감이 λ˜μ§€λŠ” μ•ŠλŠ”λ‹€. ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ„ 잘 읡히고, 잘 μ‚¬μš©ν•œλ‹€λ©΄ μΆ©λΆ„νžˆ 읽고 μ΄ν•΄ν•˜κΈ° μ‰¬μš°λ©°, μ±…μ—μ„œ λ§ν•˜λŠ” '인지 λΆ€ν•˜' μΈ‘λ©΄μ—μ„œλ„ 였히렀 더 쀄일 수 μžˆμ§€ μ•Šμ„κΉŒ..? μƒκ°ν•œλ‹€.

 

가독성은 'λ‡Œκ°€ ν”„λ‘œκ·Έλž¨μ˜ μž‘λ™ 방식을 μ΄ν•΄ν•˜λŠ” κ³Όμ •'을 더 짧게 λ§Œλ“œλŠ” 것

λŒ€ν‘œμ μœΌλ‘œ ν•¨μˆ˜ μΆ”μΆœ, 좔상화 λ“±μ˜ μž‘μ—…μ΄ μžˆμ„ 것 κ°™λ‹€.
students
    .filter { it.result >= 50 }
    .joinToString(separator = "\n") { "${it.name} ${it.surname}, ${it.result}" }
    .let(::print)


var obj = FileInputStream("/file.gz")
    .let(::BufferedInputStream)
    .let(::ZipInputStream)
    .let(::ObjectInputStream)
    .readObject() as SomeObject

 

let을 μ‚¬μš©ν•˜κΈ° 쒋은 2κ°€μ§€ μ˜ˆμ‹œλŠ”?

  • 연산을 μ•„κ·œλ¨ΌνŠΈ 처리 후에 ν˜ΈμΆœν•  λ–„
  • λ°μ½”λ ˆμ΄ν„°λ₯Ό μ‚¬μš©ν•΄ 객체λ₯Ό λž©ν• λ•Œ

μ•„μ΄ν…œ 12. μ—°μ‚°μž μ˜€λ²„λŸ¬λ“œλ₯Ό ν•  λ•ŒλŠ” μ˜λ―Έμ— 맞게 μ‚¬μš©ν•˜λΌ

μ˜λ―Έμ— λ§žμ§€ μ•Šκ²Œ μ‚¬μš©ν•œ μ˜ˆμ‹œ (νŒ©ν† λ¦¬μ–Ό κ΅¬ν˜„)

fun Int.factorial(): Int = (1..this).product()

fun Iterable<Int>.product(): Int = fold(1) { acc, i -> acc * i}

operator fun Int.not() = factorial()

fun main() {
    println(!6) // 720

    println(6.not()) // 720
}

μœ„ κ΅¬ν˜„ λ°©μ‹μ˜ 문제점?

  • ν•¨μˆ˜ 이름과 μ‹€μ œ λ™μž‘μ΄ μΌμΉ˜ν•˜μ§€ μ•ŠλŠ”λ‹€.
  • not은 논리 연산을 μœ„ν•œ 것인데, νŒ©ν† λ¦¬μ–Ό 계산에 μ‚¬μš©λ˜μ–΄ ν˜Όλž€μ„ 쀄 수 μžˆλ‹€.

μ½”ν‹€λ¦° μ—°μ‚°μžμ˜ νŠΉμ§•

  • λͺ¨λ“  μ—°μ‚°μžλŠ” ν•¨μˆ˜μ˜ 별칭이닀.
  • 각 μ—°μ‚°μžμ˜ μ˜λ―ΈλŠ” μΌκ΄€λ˜κ²Œ μœ μ§€λ˜μ–΄μ•Ό ν•œλ‹€.

μ—°μ‚°μžμ˜ μ˜λ―Έκ°€ λͺ…ν™•ν•˜μ§€ μ•Šμ€ 경우,

tripledHello = 3 * { print("Hello") }

μœ„ μ½”λ“œλŠ” ν•¨μˆ˜λ₯Ό 3번 λ°˜λ³΅ν•˜λŠ” "μƒˆλ‘œμš΄ ν•¨μˆ˜ 생성"을 μ˜λ―Έν•˜λŠ”μ§€, ν•¨μˆ˜λ₯Ό μ¦‰μ‹œ 3번 ν˜ΈμΆœν•˜λŠ” μ§€ λͺ…ν™•ν•˜μ§€ μ•Šλ‹€.

 

μœ„μ˜ 경우 infix ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜μ—¬ μ˜λ„λ₯Ό λͺ…ν™•νžˆ ν•  수 μžˆλ‹€.

infix fun Int.timesRepeated(operation: () -> Unit) = {
    repeat(this) { operation() }
}

fun main() {
    val tripledHello = 3 timesRepeated { print("Hello") }
}

 

Infix ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” λŒ€ν‘œμ μΈ 예제 (Map)

val days = mapOf("Monday" to "μ›”μš”μΌ", "Tuesday" to "ν™”μš”μΌ")

μ•„μ΄ν…œ 13. Unit? 을 λ¦¬ν„΄ν•˜μ§€ 말라

μ±„μš© 인터뷰 κ³Όμ •μ—μ„œ ν•„μžμ˜ μΉœκ΅¬λŠ” β€œν•¨μˆ˜μ—μ„œ Unit을 λ¦¬ν„΄ν•œλ‹€λ©΄, κ·Έ μ΄μœ λŠ” λ¬΄μ—‡μΌκΉŒμš”?” λΌλŠ” μ§ˆλ¬Έμ„ λ°›μ•˜λ‹€.

 

Boolean이 true/false λ₯Ό κ°–λŠ” κ²ƒμ²˜λŸΌ, Unit?은 Unit, nullμ΄λΌλŠ” 값을 κ°€μ§ˆ 수 μžˆλ‹€.

fun keyIsCorrect(key: String): Boolean = key.length > 5
fun verifyKey(key: String): Unit? = if (key.length > 5) null else Unit

if (!keyIsCorrect(key)) return
verifyKey(key) ?: return

μ§€κΈˆκΉŒμ§€ μ—¬λŸ¬ μ½”λ“œλ₯Ό λ³΄λ©΄μ„œ Unit?을 μ‰½κ²Œ 읽을 수 μžˆλŠ” κ²½μš°λŠ” 거의 보지 λͺ»ν–ˆκ³ , μ˜€ν•΄λ₯Ό 뢈러 μΌμœΌν‚€κΈ° 쉽닀.

기본적으둜 Unit?을 λ¦¬ν„΄ν•˜κ±°λ‚˜, 이λ₯Ό 기반으둜 μ—°μ‚°ν•˜μ§€ μ•ŠλŠ” 것이 μ’‹λ‹€.

 

Unit vs Void vs Nothing


μ•„μ΄ν…œ 14. λ³€μˆ˜ νƒ€μž…μ΄ λͺ…ν™•ν•˜μ§€ μ•Šμ€ 경우 ν™•μ‹€ν•˜κ²Œ μ§€μ •ν•˜λΌ

코틀린은 κ°œλ°œμžκ°€ νƒ€μž…μ„ μ§€μ •ν•˜μ§€ μ•Šμ•„λ„ νƒ€μž…μ„ μ§€μ •ν•΄μ„œ λ„£μ–΄ μ£ΌλŠ” ꡉμž₯히 μˆ˜μ€€ 높은 νƒ€μž… μΆ”λ‘  μ‹œμŠ€ν…œμ„ κ°–μΆ”κ³  μžˆλ‹€.

μ΄λŠ” 개발 μ‹œκ°„μ„ 쀄여쀄 뿐만 μ•„λ‹ˆλΌ, μœ ν˜•μ΄ λͺ…ν™•ν•  λ•Œ μ½”λ“œκ°€ μ§§μ•„μ Έμ„œ 가독성 λ˜ν•œ ν–₯μƒλœλ‹€.

ν•˜μ§€λ§Œ μœ ν˜•μ΄ λͺ…ν™•ν•˜μ§€ μ•Šμ„ λ•Œ λ‚¨μš©ν•˜λŠ” 것은 μ’‹μ§€ μ•Šλ‹€.

// Bad
val data = getSomeData()

// Good
val data: UserData = getSomeData()

가독성을 μœ„ν•΄ μ½”λ“œλ₯Ό 섀계할 λ•ŒλŠ” μ‚¬λžŒμ—κ²Œ μ€‘μš”ν•œ 정보λ₯Ό μˆ¨κ²¨μ„œλŠ” μ•ˆ λœλ‹€.

 

μš”μ¦˜ λŒ€λΆ€λΆ„ IDEμ—μ„œ νƒ€μž…μ„ λ³΄μ—¬μ£ΌλŠ” (Inlay Hints) κΈ°λŠ₯을 μ œκ³΅ν•˜κΈ° λ•Œλ¬Έμ— λŒ€λΆ€λΆ„μ˜ μƒν™©μ—μ„œλŠ” ꡳ이 νƒ€μž…μ„ μ •μ˜ν•  ν•„μš”κ°€ μžˆμ„κΉŒ? ν•˜λŠ” 생각이긴 ν•˜λ‹€.
(GitHub와 같이 μ½”λ“œλ¦¬λ·° ν•  λ•Œ μ›Ήμ‚¬μ΄νŠΈ μƒμ—μ„œλŠ” νƒ€μž…μ΄ 확인이 λΆˆκ°€λŠ₯ν•΄μ„œ 쑰금 λΆˆνŽΈν•œ 점은 μžˆμ—ˆμ§€λ§Œ, μ΄λŸ¬ν•œ 경우 μ œμ™Έ..)

νƒ€μž…μ΄ nullableμ΄κ±°λ‚˜, μ˜λ„μ™€λŠ” λ‹€λ₯Έ 경우, μ—¬λŸ¬ 연산이 ν¬ν•¨λ˜μ–΄ νƒ€μž… 좔둠이 νž˜λ“  경우 λ“± λͺ…ν™•ν•œ νƒ€μž… μ •μ˜κ°€ ν•„μš”ν•œ κ²½μš°μ—λ§Œ 선언해도 μΆ©λΆ„ν•˜μ§€ μ•Šμ„κΉŒ μ‹Άλ‹€.

μ•„μ΄ν…œ 15. λ¦¬μ‹œλ²„λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ°Έμ‘°ν•˜λΌ

μƒλž΅ν•  수 μžˆλŠ” κ²½μš°μ—λ„ thisλ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ‚¬μš©ν•˜μ—¬ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” κ²½μš°κ°€ μžˆλ‹€.

λ˜ν•œ μ—¬λŸ¬ 개의 λ¦¬μ‹œλ²„κ°€ μžˆλŠ” μƒν™©μ—μ„œλ„ λ¦¬μ‹œλ²„λ₯Ό λͺ…μ‹œμ μœΌλ‘œ 적어 μ£ΌλŠ” 것이 μ’‹λ‹€.

// block: (T) -> R  일반적인 ν•¨μˆ˜ μ •μ˜
val block: (Int) -> Int = ...
block(3)

// block: T.() -> R  객체 Tλ₯Ό receiver 둜 ν™œμš©ν•œ Lambda with receiver
val block: Int.() -> Int = ...
100.block(3)

 

λ¦¬μ‹œλ²„(Receiver): 객체 μ™ΈλΆ€μ˜ λžŒλ‹€ μ½”λ“œ 블둝을 마치 ν•΄λ‹Ή 객체 λ‚΄λΆ€μ—μ„œ μ‚¬μš©ν•˜λŠ” 것 처럼 μž‘μ„±ν•  수 있게 ν•΄μ£ΌλŠ” μž₯치

block : T.() -> R

μœ„ λžŒλ‹€ 블둝은 객체 Tλ₯Ό receiver둜 μ΄μš©ν•˜μ—¬ 객체 R을 λ°˜ν™˜ν•œλ‹€.
μœ„μ—μ„œ 객체 Tλ₯Ό λ¦¬μ‹œλ²„λΌ λΆ€λ₯΄κ³ , λ¦¬μ‹œλ²„λ₯Ό μ‚¬μš©ν•˜λŠ” λžŒλ‹€λ₯Ό Lambda with receiver 라고 λΆ€λ₯Έλ‹€.

μ°Έκ³ : https://jaeyeong951.medium.com/kotlin-lambda-with-receiver-5c2cccd8265a

μ•„μ΄ν…œ 16. ν”„λ‘œνΌν‹°λŠ” λ™μž‘μ΄ μ•„λ‹ˆλΌ μƒνƒœλ₯Ό λ‚˜νƒ€λ‚΄μ•Ό ν•œλ‹€

μ½”ν‹€λ¦°μ˜ ν”„λ‘œνΌν‹°μ—λŠ” λ§Žμ€ κΈ°λŠ₯듀이 μ‘΄μž¬ν•œλ‹€.

var name: String? = null
    get() = field?.uppercase()
    set(value) {
        if (!value.isNullOrBlank()) {
            field = value
        }
    }

 

μ½”ν‹€λ¦°μ—μ„œλŠ” ν•„λ“œμ— λŒ€ν•œ μ ‘κ·Όμž λ©”μ„œλ“œ(getter, setter)λ₯Ό μžλ™μœΌλ‘œ μƒμ„±ν•œλ‹€. λ•Œλ¬Έμ— ν•„λ“œ λŒ€μ‹  ν”„λ‘œνΌν‹°λΌλŠ” μš©μ–΄λ₯Ό μ‚¬μš©ν•˜λŠ”λ°, μ›ν• κ²½μš° μ ‘κ·Όμž λ©”μ„œλ“œλ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ„ μ–Έν•  μˆ˜λ„ μžˆλ‹€.

 

Backing Field

backing fieldλŠ” ν”„λ‘œνΌν‹°μ˜ 값을 μ €μž₯ν•˜κΈ° μœ„ν•œ ν•„λ“œμ΄λ‹€.

 

backing fieldλŠ” ν”„λ‘œνΌν‹°μ˜ 값을 μ €μž₯ν•˜κΈ° μœ„ν•œ ν•„λ“œλ‹€. μ½”ν‹€λ¦°μ—μ„œλŠ” ν•„λ“œλ₯Ό λ°”λ‘œ μ„ μ–Έν•  수 μ—†κ³  ν”„λ‘œνΌν‹°λ‘œ μ„ μ–Έν•˜λ©΄ 기본적으둜 backing fieldκ°€ μƒμ„±λœλ‹€.

class KotlinProperty {
    var counter = 0
        set(value) {
            if (value >= 0) field = value
        }
}

// Java
public class KotlinProperty {
    private int counter = 0;

    public int getCounter() {
        return counter;
    }

    public void setCounter(int value) {
        if (value >= 0) {
            this.counter = value;
        }
    }
}

 

μ–΄λ–€ 것을 ν”„λ‘œνΌν‹°λ‘œ ν•΄μ•Ό ν• μ§€ νŒλ‹¨ν•  수 μžˆλŠ” κ°„λ‹¨ν•œ μ§ˆλ¬Έμ€, "이 ν”„λ‘œνΌν‹°λ₯Ό ν•¨μˆ˜λ‘œ μ •μ˜ν•  경우 μ ‘λ‘μ‚¬λ‘œ get/set을 뢙일 것인가? λ§Œμ•½ μ•„λ‹ˆλΌλ©΄, 이λ₯Ό ν”„λ‘œνΌν‹°λ‘œ λ§Œλ“œλŠ” 것은 μ’‹μ§€ μ•Šλ‹€."

쑰금 더 ꡬ체적으둜 ν”„λ‘œνΌν‹° λŒ€μ‹  ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 쒋은 경우,

  • μ—°μ‚° λΉ„μš©μ΄ λ†’κ±°λ‚˜ λ³΅μž‘λ„κ°€ 큰 경우
  • λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ ν¬ν•¨ν•˜λŠ” 경우
  • λ³€ν™˜μ˜ 경우 (toString(), toInt())
  • getterμ—μ„œ ν”„λ‘œνΌν‹°μ˜ μƒνƒœ 변경이 μΌμ–΄λ‚˜μ•Ό ν•˜λŠ” 경우

μ•„μ΄ν…œ 17. 이름 μžˆλŠ” μ•„κ·œλ¨ΌνŠΈλ₯Ό μ‚¬μš©ν•˜λΌ 

val text = (1..10).joinToString("|")

 

μœ„ μ½”λ“œμ—μ„œ joinToString ν•¨μˆ˜μ˜ μ•„κ·œλ¨ΌνŠΈμ— | κ°€ μ˜λ―Έν•˜λŠ” λ°”λŠ”, ν•΄λ‹Ή ν•¨μˆ˜λ₯Ό λͺ¨λ₯Έλ‹€λ©΄ μ΄ν•΄ν•˜κΈ° νž˜λ“€ 수 μžˆλ‹€.

 

val text = (1..10).joinToString(separator = "|")

λ”°λΌμ„œ μœ„ 처럼 μ•„κ·œλ¨ΌνŠΈλ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ‚¬μš©ν•˜μ—¬ μž‘μ„±ν•  수 μžˆλ‹€.

 

κ·Έλ ‡λ‹€λ©΄, μœ„μ™€ 같은 이름 μžˆλŠ” μ•„κ·œλ¨ΌνŠΈλ₯Ό μ–Έμ œ μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?

  • default μ•„κ·œλ¨ΌνŠΈμ˜ 경우,
  • 같은 νƒ€μž…μ˜ νŒŒλΌλ―Έν„°κ°€ λ§Žμ€ 경우,
  • ν•¨μˆ˜ νƒ€μž…μ˜ νŒŒλΌλ―Έν„° (ν•¨μˆ˜ νƒ€μž… νŒŒλΌλ―Έν„°λŠ” λ§ˆμ§€λ§‰ μœ„μΉ˜μ— λ°°μΉ˜ν•˜λŠ” 것이 μ’‹λ‹€.)

λŒ€ν‘œμ μœΌλ‘œ μœ„μ™€ 같은 경우 이름 μžˆλŠ” μ•„κ·œλ¨ΌνŠΈλ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹κ² λ‹€.

 

κ·Έλ ‡λ‹€λ©΄, 이름 μžˆλŠ” μ•„κ·œλ¨ΌνŠΈλ₯Ό μ‚¬μš©ν•˜λ©΄ 무슨 μž₯점을 κ°€μ§ˆκΉŒ?

  • 이름을 기반으둜 값이 무엇을 λ‚˜νƒ€λ‚΄λŠ”μ§€ μ•Œ 수 μžˆλ‹€.
  • νŒŒλΌλ―Έν„° μž…λ ₯ μˆœμ„œμ™€ 상관 μ—†μœΌλ―€λ‘œ μ•ˆμ „ν•˜λ‹€.

이름 μžˆλŠ” μ•„κ·œλ¨ΌνŠΈλŠ” κ°œλ°œμžκ°€ μ½”λ“œλ₯Ό 읽을 λ•Œλ„ νŽΈλ¦¬ν•˜κ²Œ ν™œμš©λ˜λ©°, μ½”λ“œμ˜ μ•ˆμ „μ„±λ„ ν–₯μƒμ‹œν‚¬ 수 μžˆλ‹€.

@kotlin.internal.InlineOnly
public inline fun repeat(times: Int, action: (Int) -> Unit) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}

// μ‚¬μš© μ˜ˆμ‹œ
fun main() {
    repeat(5) { index ->
        println("Hello! This is message #${index + 1}")
    }
}

 

λ§Œμ•½ ν•¨μˆ˜ νƒ€μž…μ˜ νŒŒλΌλ―Έν„°κ°€ λ§ˆμ§€λ§‰ μœ„μΉ˜κ°€ μ•„λ‹Œ 경우, μ•„λž˜ 처럼 가독성이 μ’‹μ§€ μ•Šλ‹€.

@kotlin.internal.InlineOnly
public inline fun repeat(action: (Int) -> Unit, times: Int) {
    contract { callsInPlace(action) }

    for (index in 0 until times) {
        action(index)
    }
}


repeat(action = { println(it) }, times = 5)

μ•„μ΄ν…œ 18. μ½”λ”© μ»¨λ²€μ…˜μ„ μ§€μΌœλΌ

  • IDE의 formatter ν™œμš©
  • 정적 뢄석 도ꡬ (ktlint, sonarqube λ“±) ν™œμš©

https://kotlinlang.org/docs/coding-conventions.html

 

Coding conventions | Kotlin

 

kotlinlang.org

 

 

λ°˜μ‘ν˜•

λŒ“κΈ€

πŸ”HALO