Kotlin 1.5.x 호환성 가이드
Kotlin 1.5.x 호환성 가이드
Keep the Language Modern과 Comfortable Updates는 Kotlin 언어 설계의 기본 원칙 두 가지예요. 전자는 언어 진화를 막는 구조는 제거해야 한다는 뜻이고, 후자는 그런 제거를 미리 잘 알려서 코드 마이그레이션을 최대한 매끄럽게 해야 한다는 뜻이에요.
대부분의 언어 변경은 이미 업데이트 변경 로그나 컴파일러 경고 같은 다른 채널을 통해 안내됐지만, 이 문서는 그 변경들을 한자리에 모아서 Kotlin 1.4에서 1.5로 마이그레이션할 때 참고할 수 있는 완전한 레퍼런스를 제공해요.
본문
기본 용어
이 문서에서는 몇 가지 종류의 호환성을 다뤄요.
- 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: 컬렉션 min과 max 함수의 반환 타입이 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 처리한 부동소수점 타입의 Short와 Byte 변환이 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")
}
테스트 프레임워크를 제외한 뒤 애플리케이션을 테스트해 봐요. 동작이 멈췄다면 제외 변경을 되돌리고, 라이브러리가 쓰는 것과 같은 테스트 프레임워크를 사용하면서 자신의 테스트 프레임워크를 제외하는 방식으로 바꿔보세요.