Kotlin 1.5.x 호환성 가이드

Kotlin 1.5.x 호환성 가이드

Keep the Language ModernComfortable Updates는 Kotlin 언어 설계의 기본 원칙 두 가지예요. 전자는 언어 진화를 막는 구조는 제거해야 한다는 뜻이고, 후자는 그런 제거를 미리 잘 알려서 코드 마이그레이션을 최대한 매끄럽게 해야 한다는 뜻이에요.

대부분의 언어 변경은 이미 업데이트 변경 로그나 컴파일러 경고 같은 다른 채널을 통해 안내됐지만, 이 문서는 그 변경들을 한자리에 모아서 Kotlin 1.4에서 1.5로 마이그레이션할 때 참고할 수 있는 완전한 레퍼런스를 제공해요.

출처: Kotlin 문서 — Compatibility guide for Kotlin 1.5.x

본문

기본 용어

이 문서에서는 몇 가지 종류의 호환성을 다뤄요.

  • source(소스): 소스 비호환 변경은 예전에는 (경고나 오류 없이) 잘 컴파일되던 코드가 더 이상 컴파일되지 않게 만드는 변경이에요.
  • binary(바이너리): 두 바이너리 아티팩트를 서로 바꿔 써도 로딩이나 링크 오류가 나지 않으면 바이너리 호환이라고 불러요.
  • behavioral(동작): 같은 프로그램이 변경 적용 전후에 다르게 동작한다면 동작 비호환 변경이라고 해요.

이 정의는 순수 Kotlin에만 해당한다는 점 기억해 두세요. 다른 언어(예를 들어 Java) 관점에서 본 Kotlin 코드의 호환성은 이 문서의 범위 밖이에요.

언어와 표준 라이브러리

시그니처 다형성 호출에서 스프레드 연산자 금지

Issue: KT-35226

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 시그니처 다형성 호출에서 스프레드 연산자(*)를 쓸 수 없어져요.

Deprecation cycle:

  • < 1.5: 문제가 되는 연산자에 대해 호출 지점에서 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-ProhibitSpreadOnSignaturePolymorphicCall을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

해당 클래스에서 보이지 않는 추상 멤버를 가진 비추상 클래스 금지 (internal/패키지-프라이빗)

Issue: KT-27825

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 자기 클래스에서 보이지 않는 추상 멤버(internal/패키지-프라이빗)를 포함한 비추상 클래스를 선언할 수 없게 돼요.

Deprecation cycle:

  • < 1.5: 문제가 되는 클래스에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-ProhibitInvisibleAbstractMethodsInSuperclasses을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

JVM에서 reified가 아닌 타입 파라미터 기반 배열을 reified 타입 인자로 사용하는 것 금지

Issue: KT-31227

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 JVM에서 reified가 아닌 타입 파라미터로 만든 배열을 reified 타입 인자로 넘길 수 없게 돼요.

Deprecation cycle:

  • < 1.5: 문제가 되는 호출에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-ProhibitNonReifiedArraysAsReifiedTypeArguments을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

기본 생성자에 위임하지 않는 enum 클래스의 보조 생성자 금지

Issue: KT-35870

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 기본 생성자에 위임하지 않는 enum 클래스의 보조 생성자를 선언할 수 없게 돼요.

Deprecation cycle:

  • < 1.5: 문제가 되는 생성자에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-RequiredPrimaryConstructorDelegationCallInEnums을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

private inline 함수에서 익명 타입을 노출하는 것 금지

Issue: KT-33917

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 private inline 함수에서 익명 타입을 노출할 수 없게 돼요.

Deprecation cycle:

  • < 1.5: 문제가 되는 생성자에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-ApproximateAnonymousReturnTypesInPrivateInlineFunctions을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

SAM 변환 인자 뒤에 스프레드가 아닌 배열을 전달하는 것 금지

Issue: KT-35224

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 SAM 변환되는 인자 뒤에 스프레드가 아닌 배열을 넘길 수 없게 돼요.

Deprecation cycle:

  • 1.3.70: 문제가 되는 호출에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-ProhibitVarargAsArrayAfterSamArgument을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

밑줄로 이름 지은 catch 블록 파라미터의 특별한 의미 지원

Issue: KT-31567

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 catch 블록에서 예외 파라미터 이름을 생략하는 데 쓰는 밑줄 기호(_)를 참조할 수 없게 돼요.

Deprecation cycle:

  • 1.4.20: 문제가 되는 참조에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-ForbidReferencingToUnderscoreNamedParameterOfCatchBlock을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

SAM 변환 구현 전략을 익명 클래스 기반에서 invokedynamic으로 변경

Issue: KT-44912

Component: Kotlin/JVM

Incompatible change type: behavioral

Short summary: Kotlin 1.5부터 SAM(단일 추상 메서드) 변환 구현 전략이 익명 클래스를 생성하는 방식에서 invokedynamic JVM 명령을 사용하는 방식으로 바뀌어요.

Deprecation cycle:

  • 1.5: SAM 변환 구현 전략을 바꿔요. -Xsam-conversions=class를 쓰면 이전에 사용하던 구현 방식으로 되돌릴 수 있어요.

JVM IR 기반 백엔드의 성능 문제

Issue: KT-48233

Component: Kotlin/JVM

Incompatible change type: behavioral

Short summary: Kotlin 1.5는 Kotlin/JVM 컴파일러에 IR 기반 백엔드를 기본으로 사용해요. 이전 언어 버전에서는 여전히 예전 백엔드가 기본으로 사용돼요.

Kotlin 1.5에서 새 컴파일러를 쓰면 성능 저하가 발생할 수도 있어요. 우리는 그런 경우를 고치는 작업을 하고 있어요.

Deprecation cycle:

  • < 1.5: 기본적으로 예전 JVM 백엔드를 사용해요.
  • = 1.5: 기본적으로 IR 기반 백엔드를 사용해요. Kotlin 1.5에서 예전 백엔드를 써야 한다면, 1.5 이전 동작으로 임시로 되돌리기 위해 프로젝트 설정 파일에 다음 줄을 추가하면 돼요.

Gradle에서:

tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
  kotlinOptions.useOldBackend = true
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) {
  kotlinOptions.useOldBackend = true
}

Maven에서:

<configuration>
    <args>
        <arg>-Xuse-old-backend</arg>
    </args>
</configuration>

이 플래그에 대한 지원은 향후 릴리스 중 하나에서 제거될 예정이에요.

JVM IR 기반 백엔드의 새 필드 정렬

Issue: KT-46378

Component: Kotlin/JVM

Incompatible change type: behavioral

Short summary: Kotlin 1.5부터 IR 기반 백엔드를 사용하면서 JVM 바이트코드 정렬 방식이 달라져요. 즉 생성자에서 선언한 필드를 본문에서 선언한 필드보다 먼저 생성하는데, 예전 백엔드는 그 반대였어요. 이 새 정렬은 필드 순서에 의존하는 직렬화 프레임워크(예: Java 직렬화)를 사용하는 프로그램의 동작을 바꿀 수 있어요.

Deprecation cycle:

  • < 1.5: 기본적으로 예전 JVM 백엔드를 사용해요. 예전 백엔드는 본문에서 선언한 필드를 생성자에서 선언한 필드보다 먼저 배치해요.
  • = 1.5: 기본적으로 새 IR 기반 백엔드를 사용해요. 생성자에서 선언한 필드가 본문에서 선언한 필드보다 먼저 생성돼요. 우회책으로 Kotlin 1.5에서 임시로 예전 백엔드로 전환할 수 있어요. 그러려면 프로젝트 설정 파일에 다음 줄을 추가하면 돼요.

Gradle에서:

tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile> {
  kotlinOptions.useOldBackend = true
}
tasks.withType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile) {
  kotlinOptions.useOldBackend = true
}

Maven에서:

<configuration>
    <args>
        <arg>-Xuse-old-backend</arg>
    </args>
</configuration>

이 플래그에 대한 지원은 향후 릴리스 중 하나에서 제거될 예정이에요.

위임 표현식에 제네릭 호출이 있는 위임 프로퍼티에 nullability 단언 생성

Issue: KT-44304

Component: Kotlin/JVM

Incompatible change type: behavioral

Short summary: Kotlin 1.5부터 컴파일러가 위임 표현식에 제네릭 호출이 있는 위임 프로퍼티에 대해 nullability 단언을 내보내요.

Deprecation cycle:

  • 1.5: 위임 프로퍼티에 대해 nullability 단언을 생성해요(자세한 내용은 이슈 참고). -Xuse-old-backend이나 -language-version 1.4을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

@OnlyInputTypes로 주석이 달린 타입 파라미터가 있는 호출에서 경고를 오류로 변경

Issue: KT-45861

Component: Core language

Incompatible change type: source

Short summary: Kotlin 1.5부터 타입 안전성을 높이기 위해 contains, indexOf, assertEquals처럼 무의미한 인자를 받는 호출을 쓸 수 없게 돼요.

Deprecation cycle:

  • 1.4.0: 문제가 되는 생성자에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-StrictOnlyInputTypesChecks을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

이름 붙은 vararg가 있는 호출에서 올바른 인자 실행 순서 사용

Issue: KT-17691

Component: Kotlin/JVM

Incompatible change type: behavioral

Short summary: Kotlin 1.5부터 이름 붙은 vararg가 있는 호출에서 인자가 실행되는 순서가 바뀌어요.

Deprecation cycle:

  • < 1.5: 문제가 되는 생성자에 대해 경고를 내요.
  • = 1.5: 이 경고를 오류로 올려요. -XXLanguage:-UseCorrectExecutionOrderForVarargArguments을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

연산자 함수 호출에서 파라미터의 기본값 사용

Issue: KT-42064

Component: Kotlin/JVM

Incompatible change type: behavioral

Short summary: Kotlin 1.5부터 연산자 호출에서 파라미터의 기본값을 사용해요.

Deprecation cycle:

  • < 1.5: 예전 동작(자세한 내용은 이슈 참고).
  • = 1.5: 동작이 바뀌었어요. -XXLanguage:-JvmIrEnabledByDefault을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

일반 progression도 비어 있으면 for 루프에서 빈 역방향 progression 생성

Issue: KT-42533

Component: Kotlin/JVM

Incompatible change type: behavioral

Short summary: Kotlin 1.5부터 일반 progression이 비어 있을 때 for 루프에서 역방향 progression도 비어 있는 것으로 생성해요.

Deprecation cycle:

  • < 1.5: 예전 동작(자세한 내용은 이슈 참고).
  • = 1.5: 동작이 바뀌었어요. -XXLanguage:-JvmIrEnabledByDefault을 쓰면 1.5 이전 동작으로 임시로 되돌릴 수 있어요.

Char-to-code와 Char-to-digit 변환 정리

Issue: KT-23451

Component: kotlin-stdlib

Incompatible change type: source

Short summary: Kotlin 1.5부터 Char를 숫자 타입으로 변환하는 함수가 deprecated 처리돼요.

Deprecation cycle:

  • 1.5: Char.toInt()/toShort()/toLong()/toByte()/toDouble()/toFloat()Long.toChar() 같은 역방향 함수를 deprecated 처리하고 대체 방안을 제안해요.

kotlin.text 함수에서 일관되지 않은 대소문자 무시 문자 비교

Issue: KT-45496

Component: kotlin-stdlib

Incompatible change type: behavioral

Short summary: Kotlin 1.5부터 Char.equals가 대소문자 무시 비교에서 먼저 문자의 대문자 변환이 같은지 비교하고, 그다음 그 대문자 변환의 소문자 변환이 같은지(문자 그 자체가 아니라) 비교하는 방식으로 개선돼요.

Deprecation cycle:

  • < 1.5: 예전 동작(자세한 내용은 이슈 참고).
  • 1.5: Char.equals 함수의 동작을 변경해요.

기본 로케일 민감 대소문자 변환 API 제거

Issue: KT-43023

Component: kotlin-stdlib

Incompatible change type: source

Short summary: Kotlin 1.5부터 String.toUpperCase()처럼 기본 로케일을 사용하는 대소문자 변환 함수가 deprecated 처리돼요.

Deprecation cycle:

  • 1.5: 기본 로케일을 사용하는 대소문자 변환 함수를 deprecated 처리하고(자세한 내용은 이슈 참고) 대체 방안을 제안해요.

컬렉션 min/max 함수의 반환 타입을 점진적으로 non-nullable로 변경

Issue: KT-38854

Component: kotlin-stdlib (JVM)

Incompatible change type: source

Short summary: 컬렉션 minmax 함수의 반환 타입이 1.6에서 non-nullable로 바뀔 예정이에요.

Deprecation cycle:

  • 1.4: 동의어로 ...OrNull 함수를 도입하고 영향받는 API를 deprecated 처리해요(자세한 내용은 이슈 참고).
  • 1.5.0: 영향받는 API의 deprecation 수준을 오류로 올려요.
  • =1.6: 영향받는 API를 non-nullable 반환 타입으로 다시 도입해요.

부동소수점 타입을 Short와 Byte로 변환하는 것의 deprecation 수준 상향

Issue: KT-30360

Component: kotlin-stdlib (JVM)

Incompatible change type: source

Short summary: Kotlin 1.4에서 WARNING 수준으로 deprecated 처리한 부동소수점 타입의 ShortByte 변환이 Kotlin 1.5.0부터 오류를 일으켜요.

Deprecation cycle:

  • 1.4: Double.toShort()/toByte()Float.toShort()/toByte()을 deprecated 처리하고 대체 방안을 제안해요.
  • 1.5.0: deprecation 수준을 오류로 올려요.

도구

단일 프로젝트에서 여러 JVM kotlin-test 변형을 섞지 않기

Issue: KT-40225

Component: Gradle

Incompatible change type: behavioral

Short summary: 서로 다른 테스트 프레임워크용 kotlin-test 변형은 서로 배타적이라, 하나가 전이 의존성으로 끌려오면 프로젝트에 여러 변형이 함께 존재할 수 있었어요. 1.5.0부터 Gradle은 서로 다른 테스트 프레임워크용으로 서로 배타적인 kotlin-test 변형을 함께 갖는 것을 허용하지 않아요.

Deprecation cycle:

  • < 1.5: 서로 다른 테스트 프레임워크용으로 서로 배타적인 kotlin-test 변형을 여러 개 가질 수 있어요.
  • = 1.5: 동작이 바뀌었어요. Gradle이 "Cannot select module with conflict on capability..." 같은 예외를 던져요. 해결 방법은 몇 가지가 있어요.

  • 전이 의존성이 가져오는 것과 같은 kotlin-test 변형과 그에 해당하는 테스트 프레임워크를 사용해요.
  • kotlin-test 변형을 전이적으로 가져오지 않는 다른 의존성 변형을 찾아서, 사용하고 싶은 테스트 프레임워크를 사용해요.
  • 사용하고 싶은 테스트 프레임워크와 같은 테스트 프레임워크를 쓰는 다른 kotlin-test 변형을 전이적으로 가져오는 다른 의존성 변형을 찾아요.
  • 전이적으로 가져오는 테스트 프레임워크를 제외해요. 다음 예시는 JUnit 4를 제외하는 경우예요.
configurations { 
    testImplementation.get().exclude("org.jetbrains.kotlin", "kotlin-test-junit")
}

테스트 프레임워크를 제외한 뒤 애플리케이션을 테스트해 봐요. 동작이 멈췄다면 제외 변경을 되돌리고, 라이브러리가 쓰는 것과 같은 테스트 프레임워크를 사용하면서 자신의 테스트 프레임워크를 제외하는 방식으로 바꿔보세요.

더 알아보기 (Learn more)