콘텐츠로 이동
Bluetape4k 문서1.11

Opt-in 범위

최신 안정판 Bluetape4k 1.11.0 릴리스 기준

marker는 API 제공자가 선언에 붙이고, @OptIn은 사용자가 계약을 받아들인 위치에 붙입니다. 이 둘을 구분해야 upgrade 때 어떤 위험을 누가 승인했는지 추적할 수 있습니다.

@OptIn(BluetapeExperimentalApi::class)
fun evaluateDraft(): Result = experimentalPlan()

함수 하나에서만 불안정한 API를 쓴다면 함수에 붙이는 것이 기본입니다. 호출과 승인 이유가 가까이 있어서 code review와 upgrade 점검이 쉽습니다.

class 전체가 같은 계약을 공유할 때는 class 수준이 자연스럽습니다.

@OptIn(BluetapeDelicateApi::class)
class LegacyCodecAdapter {
fun encode(value: Any): ByteArray = KafkaCodecs.Jdk.encode(value)
fun decode(bytes: ByteArray): Any = KafkaCodecs.Jdk.decode(bytes)
}

file 수준 opt-in은 해당 파일의 모든 선언에 영향을 줍니다. 같은 adapter나 compatibility bridge만 모아 둔 파일처럼 경계가 분명할 때 사용합니다.

@file:OptIn(BluetapeObsoleteApi::class)

Gradle compiler option으로 source set 전체를 opt-in할 수도 있습니다.

kotlin {
compilerOptions {
optIn.add("io.bluetape4k.annotations.BluetapeBetaApi")
}
}

이 설정은 개별 사용 지점을 source에서 지웁니다. 제한된 실험 모듈 전체가 같은 정책을 의도적으로 받아들이는 경우가 아니라면 사용하지 않는 편이 낫습니다. 특히 Internal이나 Obsolete를 전역으로 허용하면 새 호출이 추가돼도 review에서 잘 드러나지 않습니다.

Wrapper는 계약을 숨기거나 전파합니다

섹션 제목: “Wrapper는 계약을 숨기거나 전파합니다”

public wrapper가 marked API를 호출할 때는 두 선택지 중 하나를 분명히 해야 합니다.

안정된 계약으로 감쌀 수 있다면 wrapper 내부에서 opt-in하고 입력 검증, 실패 동작, 호환성 정책을 wrapper가 책임집니다.

fun decodeTrustedPayload(bytes: ByteArray): Order {
require(bytes.isNotEmpty())
return decodeDelicate(bytes)
}

wrapper도 같은 위험을 그대로 노출한다면 marker를 전파합니다.

@BluetapeExperimentalApi
fun experimentalFacade(): Result = experimentalPlan()

내부에서 @OptIn만 붙였다고 wrapper가 저절로 안정되는 것은 아닙니다. 안정된 계약을 실제로 제공하지 못한다면 marker를 숨기면 안 됩니다.

Java 호출자는 별도로 문서화합니다

섹션 제목: “Java 호출자는 별도로 문서화합니다”

annotation은 binary에 남지만 Kotlin compiler의 opt-in 진단은 Java caller에게 강제되지 않습니다. Java에 공개하는 API라면 KDoc와 JavaDoc, migration 문서에도 제한을 적습니다. marker만 붙여 놓고 Java 사용자가 같은 경고를 받을 것으로 기대하면 안 됩니다.

버전을 올릴 때 @OptIn 검색 결과를 검토합니다. API가 안정화됐는지, 대체 API가 생겼는지, 허용 범위가 불필요하게 넓어졌는지를 확인합니다. file이나 compiler 수준 opt-in은 개별 함수 수준보다 먼저 살펴보는 편이 좋습니다.