Developing Myself Everyday

2장을 시작하며


코틀린은 간결성을 목표로 설계된 프로그래밍 언어가 아니라 가독성(readablitiy)을 좋게 하는 데 목표를 두고 설계된 프로그래밍 언어입니다. 

 

코틀린을 사용하면 깨끗하고 의미 있는 코드와 API를 쉽게 작성할 수 있으며, 우리가 원하는 것을 숨기거나 강조할 수 있게 하는 기능을 제공합니다.

 

이번 장에서는 이러한 기능을 사용하는 방법을 다룹니다. 

 

 

 

 

아이템 11 - "가독성을 목표로 설계하라"


프로그래밍은 쓰기보다 읽기가 더 중요합니다. 그렇기 때문에 항상 가독성을 생각하면서 코드를 작성해야 합니다.

 

인식 부하 감소

코드를 작성할 때에는 기본적으로 `인지 부하`를 줄이는 방식으로 작성해야 합니다. 우리의 뇌는 기본적으로 짧은 코드를 빠르게 읽을 수 있지만, 익숙한 코드는 더 빠르게 읽을 수 있습니다.

 

그렇기 때문에 코틀린에서는 숙련된 개발자만을 위한 코드를 작성하면 안됩니다.

 

 

극단적이 되지 않기

다만 위에서 말한 방식 때문에 극단적으로 코드를 쉽게 만들어야 한다는 말은 아닙니다. 

 

코드를 작성할 때, 정당한 이유가 필요한 경우에는 복잡성을 추가해도 됩니다. 경험이 적은 코틀린 개발자는 이러한 것을 이해하기 위한 비용을 지불할 만한 가치가 있습니다.

 

다만 이를 판단하는 것은 항상 논란이 발생할 수 있는 일입니다. 그렇기 때문에 균형을 맞추는 것이 중요합니다.

 

 

컨벤션

가독성에 대해서는 그 사람의 지식 수준에 따라 관점이 달라질 수 있습니다.

 

그렇기 때문에 이러한 것들을 토론하고 정의한 몇 가지 규칙이 있습니다. 이러한 컨벤션을 이제부터 알아봅니다.

 

 

 

 

아이템 12 - "연산자 오버로드를 할 때는 의미에 맞게 사용하라"


코틀린에서 연산자의 의미는 항상 같게 유지됩니다. 그렇기 때문에 이름만으로 연산자의 사용이 크게 제한됩니다.

 

다만, 이러한 연산자의 이름에 대한 관례를 충족하지 않을 때가 있습니다. 이러한 경우에는 `infix`를 활용한 확장 함수를 사용하는 것이 좋습니다.

 

연산자 오버로딩에 대해서는 `그 이름에 맞게 사용하라` 라는 말을 기억하며 됩니다. 의미가 명확하지 않다면, 연산자 오버로딩을 하지 않는 것이 좋습니다.

 

 

 

 

아이템 13 - "Unit?을 리턴하지 말라"


함수에서 Unit? 타입을 리턴한다면 해당 함수는 Unit을 반환하거나 Null을 반환한다는 것을 의미합니다. 이는 Boolean과 같이 2가지의 리턴 값을 가집니다.

 

그렇다고 Unit?을 Boolean의 사용 용도와 동일하게 사용해서는 안됩니다. Unit?을 반환하게 되면 무엇을 반환하는지 알기 어렵고 이해하기 어려워 집니다. 

// Unit? 을 이용하는 경우
fun verifyKey(key: String): Unit? = ...
// VerifyKey가 무엇을 반환하는지 예측하기가 어려움
verifyKey(key) ?: return

 

 

 

 

아이템 14 - "변수 타입이 명확하지 않는 경우 확실하게 지정하라"


코틀린에서는 변수의 타입을 지정하지 않아도 타입추론을 통하여 해당 변수의 타입을 알고 있습니다. 다만, 특정 상황에서는 변수의 타입을 지정하지 않아서 이해하기 어려운 코드를 만들어 버릴 수 있습니다.

 

아래와 같은 상황을 보겠습니다. 

val data = getSomeData()

 

해당 코드에서는 getSomeData() 함수의 리턴값을 data로 저장하고 있습니다. 코드를 읽을 때, getSomeData()로 넘어가서 함수를 살펴보지 않는 이상 우리는 data 변수의 타입을 알 수 없습니다. 그렇기 때문에 특정 상황에서는 변수의 타입을 확실히 지정하고 넘어가는 것이 좋습니다.

 

 

 

 

아이템 15 - "리시버를 명시적으로 참조하라"


리시버란 자신을 지징할 때 사용되는 `this`를 말합니다. 내부에서는 this를 생략할 수 있기 때문에 특별히 자신을 지정하지 않아도, 내부에 정의된 내용을 사용할 수 있습니다. 

 

다만, 여러 경우에서 리시버를 생략했을 때, 가독성이 떨어질 수 있습니다.

 

리시버가 여러 개일 경우

스코프 내부에 둘 이상의 리시버가 존재하는 경우, 이를 명시적으로 나타내지 않는다면 혼동될 수 있습니다.

class Node(val name: String) {
    fun makeChild(childName: String) =
        create("$name.$childName")
            .apply {
                print("Created ${this?.name} in ${this@Node.name}")
            }

    fun create(name: String): Node? = Node(name)
}

fun main() {
    val node = Node("Parent")
    node.makeChild("child")
}

 

 

그렇기 때문에 짧게 적을 수 있다는 이유만으로 리시버를 생략하지 마시기 바랍니다. 여러 개의 리시버가 있는 상황 등에는 리시버를 명시적으로 적어주는 것이 좋습니다.

 

 

 

 

아이템 16 - "프로퍼티는 동작이 아니라 상태를 나타내야 한다"


코틀린의 프로퍼티는 자바의 필드와는 매우 다른 개념입니다. 코틀린의 프로퍼티는 내부에 필드를 가지고 있으며 이를 자동으로 캡슐화해 줍니다. 

 

코틀린의 프로퍼티는 필드가 아니라 접근자를 나타내기에 함수처럼 사용할 수 있습니다. 다만, 프로퍼티는 상태를 나타내거나 설정하기 위한 목적으로 사용하는 것이 좋습니다. 

 

간단한 로직의 경우에는 프로퍼티의 get, set을 사용하여 로직을 포함할 수 있지만 아래의 경우에는 프로퍼티 대신 함수를 사용하는 것이 좋습니다.

  1. 연산 비용이 높거나 복잡도가 O(1)보다 큰 경우
  2. 비즈니스 로직(앱의 동작)을 포함하는 경우
  3. 결정적이지 않은 경우
  4. 변환의 경우
  5. 게터에서 프로퍼티의 상태 변경이 일어나야 하는 경우

 

반대로 상태를 추출/설정할 때는 프로퍼티를 사용해야 합니다. 특별한 이유가 없다면 함수를 사용하면 안됩니다.

 

 

 

 

아이템 17 - "이름 있는 아규먼트를 사용하라"


함수의 인자를 넣을 때, 의미를 명확하게 알기 어려울 때가 있습니다. 해당 값이 함수에서 어떤 역할을 하는지 알기 어렵다면 함수를 이해하기 어렵습니다.

 

이런 경우에는 이를 직접 지정해서 명확하게 만들어 줄 수 있습니다.

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

 

이렇게 아규먼트를 지정하는 것은 값이 무엇을 나타내는지 알 수 있는 것만 아니라 순서를 잘못 입력하는 등의 문제를 해결할 수도 있습니다.

 

 

만약 함수의 매개변수에 Default가 있다면 더욱 이름을 붙여서 사용하는 것이 좋습니다. 다양한 함수 타입의 파라미터가 있을 때도 마찬가지 입니다.

 

 

 

 

아이템 18 - "코딩 컨벤션을 지켜라"


코틀린은 매우 잘 정리된 코딩 컨벤션을 가지고 있습니다. 그렇기에 코딩 컨벤션을 잘 지키는 것이 좋습니다.

'스터디 > 이펙티브 코틀린' 카테고리의 다른 글

[이펙티브 코틀린] 1장 - 안정성  (0) 2023.12.04
profile

Developing Myself Everyday

@배준형

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!