kotlin

[Kotlin] Scope function

caporatang 2025. 9. 8. 22:54
반응형

코틀린의 스코프 함수(Scope Function) 정리

스코프 함수는 코틀린 표준 라이브러리에 포함된 함수로, 특정 객체의 컨텍스트 내에서 코드 블록을 실행할 수 있게 해준다. 이를 통해 코드를 더 간결하고 가독성 높게 만들 수 있으며, 특히 null 처리나 객체 초기화 시에 유용하게 사용된다.


1. 스코프 함수란?

스코프 함수는 람다를 사용하여 일시적인 영역(scope)을 만들고, 그 안에서 객체의 이름 없이 프로퍼티나 함수에 접근할 수 있게 해주는 함수다.

예를 들어, null이 아닐 때만 객체의 프로퍼티를 출력하는 코드는 다음과 같이 개선할 수 있다.

기존 코드

fun printPerson(person: Person?) {
    if (person != null) {
        println(person.name)
        println(person.age)
    }
}

스코프 함수(let) 적용 후

fun printPersonScope(person: Person?) {
    // person이 null이 아니면 let 블록을 실행
    person?.let {
        println(it.name)
        println(it.age)
    }
}

let은 확장 함수로 구현되어 있으며, 람다를 인자로 받아 그 결과를 반환한다. 이처럼 스코프 함수는 람다를 활용해 코드를 간결하게 만들고, 메서드 체이닝에 활용하는 함수를 의미한다.


2. 스코프 함수의 분류

스코프 함수는 크게 5가지(let, run, apply, also, with)가 있으며, 두 가지 기준에 따라 분류할 수 있다.

  1. 반환 값

    • 람다의 결과 반환: let, run, with
    • 객체 자신(context object) 반환: apply, also
  2. 컨텍스트 객체 참조 방식

    • it으로 참조 (argument): let, also
    • this로 참조 (receiver): run, apply, with
함수 컨텍스트 객체 반환 값 특징
let it 람다 결과 non-null 변수에 대한 코드 실행, 변수 scope 제한
run this 람다 결과 객체 초기화와 결과 계산을 동시에 할 때
apply this 객체 자신 객체 설정 및 초기화 (Test Fixture 등)
also it 객체 자신 객체에 대한 부가 작업 (로깅 등)
with this 람다 결과 비확장 함수, 객체를 인자로 받아 사용

itthis 의 차이와 원인

  • this: 생략이 가능하지만, 다른 이름으로 변경할 수 없다.
  • it: 생략이 불가능하지만, 람다 파라미터처럼 다른 이름으로 변경할 수 있다. (person.let { p -> p.age })

이러한 차이는 각 스코프 함수가 받는 람다의 시그니처가 다르기 때문에 발생한다.

  • let은 일반 함수 (T) -> R를 받기 때문에, 파라미터를 it으로 받는다.
  • run은 확장 함수 T.() -> R를 받기 때문에, T 자기 자신을 this로 참조한다.

3. 언제 어떤 스코프 함수를 사용해야 할까?

3-1) let

  • 하나 이상의 함수를 call chain 결과로 호출할 때
    val strings = listOf("APPLE", "CAR")
    strings.map { it.length }
        .filter { it > 3 }
        .let(::println) // 최종 결과를 println의 인자로 넘김
  • non-null 값에 대해서만 코드를 실행시킬 때
    val length = str?.let {
        println(it.uppercase())
        it.length
    }
  • 일회성으로 제한된 영역에 지역 변수를 만들 때
    val numbers = listOf("one", "two", "three", "four")
    val modifiedFirstItem = numbers.first()
        .let { firstItem ->
            if (firstItem.length >= 5) firstItem else "!$firstItem"
        }.uppercase()
    println(modifiedFirstItem)

3-2) run

  • 객체 초기화와 반환 값의 계산을 동시에 해야 할 때

    // Person 객체를 생성 후 DB에 저장하고, 저장된 인스턴스를 반환받음
    val person1 = Person("김태일", 100).run(personRepository::save)
    
    // 객체에 추가적인 처리를 한 후 로직을 실행할 때
    val person2 = Person("김태일", 100).run {
        hobby = "독서"
        personRepository.save(this) // 저장된 person2 객체가 반환됨
    }

3-3) apply

  • 객체 설정을 할 때 (특히 Test Fixture 생성 시 유용)
    fun createPerson(name: String, age: Int, hobby: String): Person {
        return Person(name = name, age = age)
            .apply {
                this.hobby = hobby // this는 Person 객체, 반환값도 Person 객체
            }
    }

3-4) also

  • 객체에 대한 부가적인 작업(side effect)이 필요할 때 (로깅 등)
    mutableListOf("one", "two", "three")
        .also { println("four 추가 이전 지금 값: $it") } // it은 리스트 자신
        .add("four")

3-5) with

  • 특정 객체를 다른 객체로 변환해야 할 때 (non-extension function)
    with는 확장 함수가 아니기 때문에, 인자로 객체를 직접 받는다.
    return with(person) {
        PersonDto(
            name = name, // this.name
            age = age    // this.age
        )
    }

4. 스코프 함수와 가독성

스코프 함수는 코드를 간결하게 만들지만, 남용하면 오히려 가독성을 해칠 수 있다.

이펙티브 코틀린 예시

// 1. if-else 사용
if (person != null && person.isAdult) {
    view.showPerson(person)
} else {
    view.showError()
}

// 2. 스코프 함수 사용
person?.takeIf { it.isAdult }
    ?.let(view::showPerson)
    ?: view.showError()

두 번째 코드는 간결하지만, 코틀린에 익숙하지 않은 사람에게는 if-else 구문이 더 명확하고 디버깅하기 쉬울 수 있다. 사용 빈도가 낮은 관용구를 조합하면 복잡성이 증가할 수 있다.

따라서 스코프 함수는 팀의 컨벤션에 맞춰 적절하게 사용하는 것이 중요하겠다..~
우리팀에서는 이미 많이 써놨던데..


최종 요약

  1. 코틀린의 스코프 함수는 일시적인 영역을 만들어 코드를 더 간결하게 하거나, 메서드 체이닝에 활용된다.
  2. 스코프 함수의 종류에는 let / run / also / apply / with 가 있으며, 반환 값과 컨텍스트 객체 참조 방식에 따라 구분된다.
  3. 스코프 함수를 사용한 코드는 사람에 따라 가독성을 다르게 느낄 수 있으므로, 함께 프로덕트를 만들어 가는 팀끼리 컨벤션을 잘 정하는 것이 중요하겠다..!.
반응형

'kotlin' 카테고리의 다른 글

[Kotlin] lateinit, by lazy  (0) 2025.11.03
[Kotlin] 코틀린의 이것저것 문법  (0) 2025.09.07
[Kotlin] 컬렉션을 함수형으로 다루기  (1) 2025.08.28
[Kotlin] 람다 (Lambda)  (0) 2025.08.20
[Kotlin] 다양한 함수  (1) 2025.08.18