Skip to content

컨텍스트 파라미터

컨텍스트 파라미터(Context parameters)는 컨텍스트 리시버(context receivers)라고 불리는 이전 실험적 기능을 대체합니다. 주요 차이점은 컨텍스트 파라미터 설계 문서에서 확인할 수 있습니다. 컨텍스트 리시버에서 컨텍스트 파라미터로 마이그레이션하려면 관련 블로그 포스트에 설명된 대로 IntelliJ IDEA의 지원 기능을 사용할 수 있습니다.

컨텍스트 파라미터를 사용하면 함수와 프로퍼티가 주변 컨텍스트에서 암시적으로 사용 가능한 의존성(dependencies)을 선언할 수 있습니다.

컨텍스트 파라미터를 사용하면 서비스나 의존성 같이 여러 함수 호출 간에 공유되고 거의 변경되지 않는 값을 수동으로 전달할 필요가 없습니다.

프로퍼티와 함수에 컨텍스트 파라미터를 선언하려면 context 키워드 뒤에 이름: 타입 형식으로 선언된 파라미터 목록을 추가합니다. 다음은 UserService 인터페이스에 대한 의존성을 가진 예시입니다:

kotlin
// UserService는 컨텍스트에서 필요한 의존성을 정의합니다 
interface UserService {
    fun log(message: String)
    fun findUserById(id: Int): String
}

// 컨텍스트 파라미터가 있는 함수를 선언합니다
context(users: UserService)
fun outputMessage(message: String) {
    // 컨텍스트의 log를 사용합니다
    users.log("Log: $message")
}

// 컨텍스트 파라미터가 있는 프로퍼티를 선언합니다
context(users: UserService)
val firstUser: String
    // 컨텍스트의 findUserById를 사용합니다    
    get() = users.findUserById(1)

fun main() {
    val users = object : UserService {
        override fun log(message: String) {
            println(message)
        }

        override fun findUserById(id: Int): String {
            return "User $id"
        }
    }

    context(users) {
        outputMessage("Looking up the first user")
        println(firstUser)
        // User 1
    }
}

컨텍스트 파라미터 이름으로 _를 사용할 수 있습니다. 이 경우 파라미터 값은 해석(resolution)에는 사용될 수 있지만, 블록 내부에서 이름으로 접근할 수는 없습니다:

kotlin
// "_"를 컨텍스트 파라미터 이름으로 사용합니다
context(_: UserService)
fun logWelcome() {
    // 해석(Resolution)을 통해 UserService에서 적절한 log 함수를 여전히 찾아냅니다
    outputMessage("Welcome!")
}

컨텍스트 파라미터 해석

Kotlin은 호출 지점(call site)의 현재 스코프에서 일치하는 컨텍스트 값을 검색하여 컨텍스트 파라미터를 해결(resolve)합니다. Kotlin은 타입을 기준으로 일치 여부를 판단합니다. 동일한 스코프 레벨에 호환되는 값이 여러 개 존재하면 컴파일러가 모호성(ambiguity) 오류를 보고합니다:

kotlin
// UserService는 컨텍스트에서 필요한 의존성을 정의합니다
interface UserService {
    fun log(message: String)
}

// 컨텍스트 파라미터가 있는 함수를 선언합니다
context(users: UserService)
fun outputMessage(message: String) {
    users.log("Log: $message")
}

fun main() {
    // UserService 구현 
    val serviceA = object : UserService {
        override fun log(message: String) = println("A: $message")
    }

    // UserService 구현
    val serviceB = object : UserService {
        override fun log(message: String) = println("B: $message")
    }

    // 호출 지점에서 serviceA와 serviceB 모두 예상되는 UserService 타입과 일치합니다
    context(serviceA, serviceB) {
        // 이는 모호성 오류를 발생시킵니다
        outputMessage("This will not compile")
    }
}
Experimental

컨텍스트 인자 명시적으로 전달하기

오버로드(overload)가 컨텍스트 파라미터에 의해서만 구분되는 경우, 일치하는 컨텍스트 값이 여러 개 존재하면 호출 시 모호함이 발생할 수 있습니다.

모호성을 해결하려면 호출 지점에서 명시적인 컨텍스트 인자(context argument)를 전달하세요:

kotlin
class EmailSender
class SmsSender

context(emailSender: EmailSender)
fun sendNotification() {
    println("Sent email notification")
}

context(smsSender: SmsSender)
fun sendNotification() {
    println("Sent SMS notification")
}

context(defaultEmailSender: EmailSender, defaultSmsSender: SmsSender)
fun notifyUser() {
    // EmailSender 컨텍스트 파라미터가 있는 오버로드를 선택합니다
    sendNotification(emailSender = defaultEmailSender)

    // SmsSender 컨텍스트 파라미터가 있는 오버로드를 선택합니다
    sendNotification(smsSender = defaultSmsSender)
}

명시적인 컨텍스트 인자를 사용하여 일부 함수 호출의 중첩(nesting)을 줄일 수도 있습니다:

  • 단일 호출의 경우, 명시적인 컨텍스트 인자를 사용하여 코드를 더 읽기 쉽게 만드세요.
  • 여러 호출이 동일한 컨텍스트 인자를 사용하는 경우, context() 함수를 사용하세요.

이 기능은 실험적(Experimental)입니다. 사용하려면 빌드 파일에 다음 컴파일러 옵션을 추가하세요:

kotlin
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xexplicit-context-arguments")
    }
}
xml
<build>
    <plugins>
        <plugin>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-maven-plugin</artifactId>
            <configuration>
                <args>
                    <arg>-Xexplicit-context-arguments</arg>
                </args>
            </configuration>
        </plugin>
    </plugins>
</build>

제한 사항

컨텍스트 파라미터는 지속적으로 개선되고 있으며, 현재 몇 가지 제한 사항은 다음과 같습니다:

  • 생성자에는 컨텍스트 파라미터를 선언할 수 없습니다.
  • 컨텍스트 파라미터가 있는 프로퍼티는 백킹 필드(backing field)나 초기화 구문(initializer)을 가질 수 없습니다.
  • 컨텍스트 파라미터가 있는 프로퍼티는 위임(delegation)을 사용할 수 없습니다.

이러한 제한 사항에도 불구하고, 컨텍스트 파라미터는 간소화된 의존성 주입, 향상된 DSL 설계 및 스코프 기반 작업을 통해 의존성 관리를 단순화합니다.