Lambda: 개념, 문법 및 Kotlin에서의 활용

저자: IT Sectr 게시일: 2026-06-23 읽는 시간: 7 분

Lambda는 컴팩트한 표현식 { 인수 -> 본문 } 으로 표현되는 익명 함수입니다. Kotlin에서 람다는 고차 함수에 동작을 전달하는 기본적인 방법으로, 별도의 함수 선언 없이 간결한 문법을 제공합니다. Kotlin 문서(2026)에 따르면, 일반적인 프로젝트에서 70% 이상의 고차 함수가 람다와 함께 호출됩니다. Lambda는 최소한의 문법적 잡음으로 함수형 스타일의 코드를 작성할 수 있게 해줍니다.

핵심 포인트

  • Lambda — { args -> body } 문법의 익명 함수, 이름이 없음
  • 암시적 it 람다의 단일 인수를 대체하여 작성을 간소화
  • Trailing lambda — 가독성을 위해 마지막 람다를 호출 괄호 밖으로 이동
  • 클로저(closure) — 람다는 외부 스코프의 변수에 접근 가능
  • 함수형 타입 (T) -> R 이 람다의 시그니처를 정의하고 컴파일러가 검증

Lambda란 무엇인가?

Lambda(람다 표현식)는 선언 대신 표현식으로 정의되는 익명 함수입니다. 이 용어는 알론조 처치의 람다 계산법(1936)에서 유래했습니다. 프로그래밍에서 람다는 값으로 전달되고, 변수에 저장되며, 나중에 호출될 수 있는 코드 블록입니다. Kotlin에서 람다는 항상 중괄호 { } 로 둘러싸이며, 인수는 -> 화살표로 본문과 구분됩니다.

람다는 일반 함수와 달리 이름과 fun 키워드가 없습니다. 이를 통해 추가 선언으로 코드를 복잡하게 만들지 않고 사용 지점에서 직접 동작을 선언할 수 있습니다. Kotlin 컴파일러는 컨텍스트에서 인수 타입을 추론하므로, 많은 경우 타입을 생략할 수 있습니다. 람다는 함수형 타입 (T) -> R 의 문법적 설탕입니다. 모든 람다에는 해당 함수형 타입이 있습니다.

Kotlin에서 람다의 주요 용도는 고차 함수에 전달하는 것입니다. 표준 라이브러리는 모든 곳에서 람다를 사용합니다: filter { it > 0 }, map { it.uppercase() }, forEach { println(it) }. 람다는 이벤트 리스너, 콜백, apply 및 run을 통한 객체 설정자에도 사용되며, 익명 클래스에 비해 코드를 더 선언적으로 만듭니다.

Kotlin에서 람다 표현식의 문법

Kotlin에서 람다 표현식의 완전한 형태: { 인수 -> 람다_본문 }. 인수는 쉼표로 구분하여 나열되고, 그 뒤에 화살표와 본문(하나 이상의 표현식)이 옵니다. 본문에 여러 줄이 포함된 경우 마지막 표현식이 반환값이 됩니다. 함수가 마지막 인수로 람다를 받는 경우, 호출 괄호 밖으로 이동할 수 있습니다.

kotlin
val sum: (Int, Int) -> Int = { a, b -> a + b }

// 컨텍스트에서 추론된 타입
val sumShort = { a: Int, b: Int -> a + b }

// Lambda 호출
val result = sum(5, 3)  // 8
println(result)

변수 sum은 명시적 함수형 타입 (Int, Int) -> Int 로 선언되고 람다 { a, b -> a + b } 로 초기화됩니다. sumShort의 경우 타입이 람다 내부에 지정되고 변수 타입이 추론됩니다. 두 방식 모두 동등합니다. 람다 호출은 일반 함수 호출과 다르지 않습니다 — sum(5, 3) 문법을 사용합니다. 인수가 없는 람다는 { body } 로 작성되고, 하나의 인수가 있는 경우 { it -> it * 2 } 또는 간단히 { it * 2 } 로 작성됩니다.

암시적 it과 인수 생략

람다가 정확히 하나의 인수를 받을 때 Kotlin은 암시적 이름 it을 제공합니다. 이렇게 하면 왼쪽에 인수를 선언할 필요가 없어집니다. 개발자는 람다 본문만 작성하고 it을 사용하여 단일 매개변수에 접근합니다. 이 문법은 컬렉션 처리 체인에서 특히 편리하며, it은 현재 요소를 가리킵니다.

kotlin
val numbers = listOf(1, 2, 3, 4, 5)

// 명시적 매개변수 이름
val squaredExplicit = numbers.map { n -> n * n }

// 암시적 it
val squared = numbers.map { it * it }

// it으로 필터링
val even = numbers.filter { it % 2 == 0 }

println(squared) // [1, 4, 9, 16, 25]

예제에서 map { it * it } 은 n -> 을 선언하는 대신 it을 사용합니다. Kotlin은 단일 람다 매개변수에 대해 자동으로 it을 할당합니다. 람다가 두 개 이상의 인수를 받는 경우 it이 생성되지 않으며 — 모든 매개변수를 명시적으로 선언해야 합니다. 중첩된 람다의 가독성을 높이려면 매개변수에 의미 있는 이름을 부여하고 짧은 한 줄 조건자에서만 it을 사용하는 것이 좋습니다.

Trailing lambda와 여러 줄 람다

Trailing lambda는 마지막 람다 인수를 함수 호출 괄호 밖으로 이동할 수 있는 Kotlin의 문법 기능입니다. 이렇게 하면 코드가 내장된 언어 구조처럼 보입니다. 람다가 유일한 인수인 경우 괄호를 완전히 생략할 수 있습니다. 이 접근 방식은 표준 라이브러리와 DSL에서 적극적으로 사용됩니다.

kotlin
// 표준 호출
thread(block = {
    println("스레드에서 안녕하세요")
})

// Trailing lambda 버전
thread {
    println("스레드에서 안녕하세요")
}

// trailing lambda로 반복
repeat(3) { index ->
    println("반복 $index")
}

// 여러 줄 람다로 실행
val config = run {
    val host = "localhost"
    val port = 8080
    "$host:$port"
}

thread 함수는 마지막 인수로 Runnable을 받습니다. trailing lambda 덕분에 람다를 괄호 밖에 전달할 수 있어 시각적으로 코드 블록처럼 보입니다. repeat 함수는 반복 횟수와 인덱스가 있는 람다를 받습니다. run은 여러 줄 람다가 마지막 표현식으로 반환되는 값을 계산하는 고차 함수의 예입니다. run() 주변의 괄호는 람다가 유일한 인수인 경우 생략할 수 있습니다.

람다에서 변수 캡처(클로저)

변수 캡처(클로저)는 람다가 외부 스코프에 선언된 변수에 접근하는 기능입니다. Kotlin에서 람다는 var 변수를 읽고 수정할 수 있으며, 주변 컨텍스트의 val 변수도 읽을 수 있습니다. 캡처된 변수는 람다와 함께 전체 수명 주기 동안 저장되어 상태를 가진 함수를 만들 수 있게 합니다.

kotlin
fun makeCounter(): () -> Int {
    var count = 0
    return {
        count++  // 캡처 횟수
    }
}

fun main() {
    val counter = makeCounter()
    println(counter()) // 0
    println(counter()) // 1
    println(counter()) // 2
}

makeCounter 함수는 로컬 변수 count를 선언하고 람다 { count++ } 를 반환합니다. makeCounter가 실행을 완료했음에도 반환된 람다는 클로저를 통해 count에 대한 참조를 유지합니다. counter() 를 호출할 때마다 현재 값을 증가시키고 반환합니다. 클로저 메커니즘은 커링, 함수 팩토리 및 메모이제이션의 기초가 됩니다. 가변 변수를 캡처하면 동시 실행 시 예기치 않은 동작이 발생할 수 있음을 기억하는 것이 중요합니다.

자주 묻는 질문

람다에서 return을 사용할 수 있나요?

Lambda는 무조건적인 return을 지원하지 않습니다 — 레이블 없는 return은 외부 함수로 제어를 반환합니다. 람다를 종료하려면 return@label을 사용하세요. 여기서 label은 람다가 전달된 고차 함수의 이름입니다.

람다와 익명 함수의 차이는 무엇인가요?

익명 함수는 fun(a: Int): Int { return a * 2 } 로 작성되며 레이블 없는 return을 지원합니다. 람다 { a -> a * 2 } 는 문법이 더 짧지만 조기 종료를 위해 return@label이 필요합니다.

화살표 없이 람다를 인수로 전달하는 방법은?

람다가 인수를 받지 않으면 화살표가 생략됩니다: { body() }. 하나의 인수를 받지만 사용하지 않는 경우, 밑줄로 매개변수를 무시하여 { _ -> body() } 라고 작성합니다.

람다는 항상 새 객체를 생성하나요?

네, inline이 아닌 함수에 전달된 모든 람다는 익명 클래스의 인스턴스를 생성합니다. inline 함수의 경우 람다는 객체를 생성하지 않고 호출 지점에 인라인됩니다. 캡처하지 않는 람다는 싱글톤 객체로 캐시됩니다.

람다 내에서 직접 return을 사용할 수 없는 이유는?

Kotlin에서 return은 fun으로 선언된 가장 가까운 함수에 속하는 점프 연산자입니다. 람다는 함수(fun)가 아니라 표현식입니다. 따라서 레이블 없이 람다 내부의 return은 외부 포함 함수에서의 반환으로 처리됩니다.

요약

  • Lambda — { args -> body } 문법의 익명 함수, 값으로 고차 함수에 전달됨
  • 암시적 it 단일 인수를 대체하여 짧은 조건자에서 코드를 더 간결하게 만듦
  • Trailing lambda 마지막 람다를 호출 괄호 밖으로 이동하여 코드 블록처럼 읽음
  • 클로저 포함 함수가 완료된 후에도 람다가 외부 컨텍스트의 변수에 접근할 수 있게 함
  • 함수형 타입 (T) -> R 이 컴파일 타임 검증으로 람다 시그니처를 정의함
  • Inline 함수 람다에 대한 익명 클래스 생성을 제거하여 루프 성능 향상
  • 여러 줄 람다 마지막 표현식의 값을 반환하여 별도 함수 선언 없이 복잡한 계산 지원

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기