새소식

Language/Kotlin

[Effective Kotlin] 2장 가독성 : Item 11번 ~14번

  • -

✅ 인식 부하 감소

가독성이란 코드를 읽고 얼마나 빠르게 이해할 수 있는지를 의미합니다. 사람에 따라 가독성에 대한 관점은 다르지만, 뇌가 프로그램의 작동 방식을 이해하는 과정을 더 짧게 만드는것을 가독성이 좋은 코드라고 말할 수 있습니다.

  • 숙력된 개발자만을 위한 코드는 좋은 코드가 아니다.
  • 일반적이지 않고 ‘굉장히 창의적인’ 구조는 유연하지 않고, 지원도 제대로 받지 못한다.

 

✅ 극단적이 되지 않기

  • let과 같이 잘 모르고 사용하면, 예상하지 못한 결과가 나올 수 있습니다. 누군가는 let을 절대로 사용하지 말라고 합니다. 그러나 let을 적제 적소에 잘 사용한다면 더 좋은 코드를 만들 수 있습니다.

 


  • 코틀린에서 각 연산자의 의미는 항상 같게 유지됩니다. + 연산자가 일반적인 의미로 사용되지 않는다면, 연산자를 볼 때마다 연산자를 개별적으로 이해해야 하는 문제가 발생합니다.
  • 연산자 오버로딩은 그 이름의 의미에 맞게 사용하는 것이 좋습니다. 연산자 의미가 명확하지 않다면, 연산자 오버로딩을 사용하지 않는 것이 좋습니다.
  • 연산자 의미가 명확하지 않다면, 이름이 있는 일반 함수를 사용하는 것을 권장합니다. 꼭 연산자 같은 형태로 사용하고 싶다면, infix 확장 함수 또는 톱레벨 함수를 활용하는 방법도 있습니다.

예시

class Book(val title: String, val author: String) {
    operator fun plus(other: Book): Int {
        return this.title.length + other.title.length
    }
}

fun main() {
    val book1 = Book("Kotlin Programming", "Jane Doe")
    val book2 = Book("The Great Gatsby", "F. Scott Fitzgerald")
    
    val totalLength = book1 + book2
    println("Total length of book titles: $totalLength")
}

이 예시에서 Book 클래스는 + 연산자를 오버로딩하여 두 책의 제목 길이를 더하는 기능을 구현합니다. 그러나 이는 + 연산자의 일반적인 의미와는 거리가 멉니다. 보통 + 연산자는 두 객체를 결합하거나 수치를 더할 때 사용합니다. 따라서, 책 제목의 길이를 더하는 것은 + 연산자의 직관적인 사용이라고 볼 수 없습니다.

 

이런 방식으로 연산자를 오버로딩하면 코드를 처음 보는 사람이나 나중에 코드를 유지보수할 때 혼란을 겪을 수 있습니다. 연산자 오버로딩 기능을 사용할 때는 연산자가 가진 원래의 의미와 기능을 고려하여, 코드의 가독성을 해치지 않도록 주의해야 합니다.

 

 

 


Kotlin에서 Unit 타입은 함수가 아무런 값을 반환하지 않을 때 사용됩니다.

Unit? (즉, Unit이 될 수도 있고 null이 될 수도 있는 타입)을 반환 타입으로 사용하는 것은 일반적으로 권장되지 않습니다.

 

✅  Unit? 리턴을 권장하지 않는 이유

  • 의미상 모순: Unit은 "아무것도 중요하지 않다"는 것을 의미하며, 함수가 특별한 값을 반환하지 않음을 나타냅니다. 반면에 null 가능성을 추가하면, "가끔은 중요할 수도 있다"는 모호한 메시지를 전달하게 됩니다. 이는 코드의 의도를 명확하게 전달하지 못하게 만듭니다.
  • 사용성 저하: Unit?를 반환하는 경우, 호출자는 항상 결과를 null 체크해야 합니다. 그러나 대부분의 경우 함수가 성공적으로 완료되었는지 여부만 중요하며, Unit? 타입은 이러한 간단한 확인을 복잡하게 만듭니다.

 

예시

fun process(input: String): Unit? {
    if (input.isEmpty()) {
        return null
    }
    println("Processing $input")
    // 더 이상의 반환 값 없음
}

fun main() {
    val result = process("")
    if (result == null) {
        println("Input was empty, processing skipped.")
    }
}

위의 예시에서 process 함수는 입력이 비어 있을 경우 null을 반환하고, 그렇지 않으면 입력을 처리합니다.

 

더 나은 접근 방식은 예외를 던지거나 Result 타입을 사용하는 것입니다.

fun process(input: String) {
    if (input.isEmpty()) {
        throw IllegalArgumentException("Input cannot be empty")
    }
    println("Processing $input")
}

fun main() {
    try {
        process("")
    } catch (e: IllegalArgumentException) {
        println("Failed to process input: ${e.message}")
    }
}

 

 

 


✅  변수 타입을 확실하게 지정하길 권장하는 이유

코틀린은 개발자가 타입을 지정하지 않아도 타입을 지정해서 넣어 주는 타입 추론 시스템을 가지고 있습니다.
이는 개발 시간 단축과 변수 타입이 명확한 경우 코드의 길이와 가독성을 높일 수 있습니다.

하지만 유형이 명확하지 않은 경우 타입 추론 시스템을 남용하면 가독성이 떨어질 수 있습니다.

물론 코드를 따라가며 어떤 타입을 리턴받는지 확인 할 수 있습니다. 그러나 이러한 동작은 개발 시간을 늘리며 코드를 따라 갈 수 없는 환경에서는 더욱 많은 시간을 사용하게 합니다.

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.