Kotlin

Kotlin 1.4 에 추가될 사항 그리고 향후 전망

KotlinConf의 기조 연설에서 Andrey는 Kotlin 발전을 위해 현재 집중하고 있는 분야에 대한 전략적인 견해와 내년 중 출시될 Kotlin 1.4에 대한 계획을 밝혔습니다.

아래에서 기조 연설을 보실 수 있습니다.

우리의 비전은 여러분에게 Kotlin이 신뢰할 수 있는 작업 파트너가 되고, 작업 시 기본적으로 선택하는 언어가 되게 하는 것입니다. 이 비전을 달성하기 위하여 저희는 Kotlin이 모든 플랫폼에서 빛을 발하게 만들고자 합니다. 업계에서 인지도가 높은 여러 회사의 사례 연구에 따르면 우리는 이 방향으로 순조롭게 진행하고 있음을 알 수 있습니다.

2020년 봄에 출시될 예정인 Kotlin 1.4는 Kotlin 에코시스템을 한 층 더 발전시키게 될 것입니다.

품질 중심

무엇보다도 Kotlin 1.4에서는 품질과 성능에 중점을 둘 예정입니다. Kotlin은 이미 수많은 아이디어와 접근 방법을 선도하고 있는 최신 언어입니다. 저희는 Kotlin을 항상 최신 상태로 유지하고, 지속적으로 발전시킬 예정입니다. 하지만, 현재 Kotlin은 대규모 기능을 추가하는 것보다는 전반적인 이용 환경을 개선하는 것에 더 중점을 두어야 한다고 생각합니다. 따라서 Kotlin 1.4 에서는 약간의 언어 변경 사항만 추가됩니다. 자세한 내용은 아래에 설명되어 있습니다.

당사는 Kotlin을 지원하는 IDE의 성능을 강화하는 데 주목할 만한 성과를 이미 일부 달성하였습니다. 이전 버전과 비교할 때 코드 완성 속도가 대폭 증가하였습니다.

Gradle 팀과 협력하여 Gradle 스크립트를 더 빠르게 만들었습니다. Kotlin 1.3.10과 비교했을 때 Kotlin 1.3.60에서는 Android Studio에서의 Gradle Import가 2.5배 더 빠르고, 메모리 소비가 75% 더 적습니다.

메모리

게다가 build.gradle.kts를 로드할 때 CPU 사용량이 거의 제로에 가깝습니다! 또한, 코드 캐싱 덕분에 개발 모드에서 Kotlin/Native를 컴파일할 때 최대 2배 더 빠릅니다.

저희는 빌드 속도가 사용자에게 종종 가장 큰 관심 사항이라는 것을 이해하고 있으며, 이를 해결하기 위하여 지속적으로 툴체인을 개선하고 있습니다. 하지만, 점진적인 개선으로는 프로덕션 코드베이스의 자연적인 증가를 따라갈 수 없습니다. 저희는 컴파일을 더 빠르게 만들고 있지만, 사용자는 더 많은 코드를 작성하고, 전반적인 빌드 시간은 충분히 개선되지 않고 있습니다. 컴파일러를 재구현하여 정말로 빠르게 만들 필요성이 명확해졌습니다.

새로운 컴파일러

새로운 컴파일러 구현의 목표는 속도를 대폭 개선하고, Kotlin이 지원하는 모든 플랫폼을 통합하며, 컴파일러 확장에 대한 API를 제공하는 것입니다. 이 목표를 실현하려면 수 년이 소요될 것으로 예상되지만, 저희가 시작한지는 얼마되지 않았습니다. 따라서, 이 새로운 구현의 일부만 1.4에서 제공되고, 향후 매우 점진적으로 이행될 것입니다. 현재, 이미 구현이 적용되고 있습니다. 예를 들어, 타입 추론에 대한 새로운 알고리즘이 새 컴파일러의 일부입니다. 다른 부분에 대한 접근 방법도 동일합니다. 당분간은 두 버전, 즉 이전 버전과 실험적 모드의 신규 버전이 모두 제공됩니다. 신규 버전이 안정화가 되면, 이것이 기본 버전이 될 것입니다.

새로운 프런트엔드를 통한 속도 개선

새로운 컴파일러에서 저희가 기대하는 속도 개선의 대부분은 새로운 프런트엔드 구현을 통해 실현될 예정입니다.

배경에 대해 약간 설명을 드리면, 컴파일은 소스 파일을 받아서 실행 가능한 코드로 단계별로 변환하는 파이프라인이라고 생각할 수 있습니다. 이 파이프라인을 구성하는 첫 번째 주요 단계를 흔히 컴파일러의 프런트엔드라고 부릅니다. 이 단계에서는 코드 구문 분석, 이름 확인, 타입 검사 수행 등 다양한 작업이 진행됩니다. 또한, 이 단계에서 컴파일러는 오류 강조 표시, 정의 탐색, 프로젝트 내 심볼 사용 위치 검색 시 IDE 안에서 작동합니다. 그리고, 현재 kotlinc가 가장 많은 시간을 보내는 단계이기도 합니다. 따라서, 저희는 이 단계를 더 빠르게 만들려고 합니다.

현재의 구현은 완전하지 않고, 1.4에서는 제공되지 않습니다. 하지만, 시간이 걸리는 작업의 대부분은 이미 처리하고 있으므로, 예상되는 속도 개선 효과는 측정할 수 있습니다. 당사의 벤치마크(YouTrack 및 Kotlin 컴파일러 자체를 컴파일하기)에 의하면 새로운 프런트엔드는 기존의 것보다 약 4.5배 더 빠릅니다.

백엔드 통합 및 확장성

프런트엔드가 코드 분석을 완료하면 백엔드가 실행 파일을 생성합니다. 당사의 백엔드에는 Kotlin/JVM, Kotlin/JS, Kotlin/Native의 세 가지가 있습니다. 처음 두 개는 독립적으로 작성되어 많은 코드가 공유되지 않았습니다. 저희가 Kotlin/Native를 시작했을 때는 가상 머신의 바이트코드와 다소 유사한 기능을 제공하는 Kotlin 코드용 내부 표현(IR)을 중심으로 구축된 새로운 인프라에 기반하였습니다. 이제 다른 두 개의 백엔드를 동일한 IR로 마이그레이션하는 중입니다. 결과적으로 당사는 많은 백엔드 로직을 공유하고, 파이프라인을 통합함으로써 단 한 번의 작업을 통하여 대부분의 기능 구현, 최적화 및 버그 수정을 모든 대상에 적용할 수 있게 되었습니다.

저희는 단계적으로 새로운 백엔드로 마이그레이션할 예정입니다. 1.4에서는 기본적으로 활성화될 가능성은 낮지만, 사용자는 명시적인 선택을 통해 사용할 수 있습니다.

공통적인 백엔드 인프라를 통해 다중 플랫폼 컴파일러 확장을 지원할 수 있게 됩니다. 파이프라인에 연결하여 사용자 지정 처리나 변환을 추가하면 모든 대상에 자동으로 적용됩니다. 1.4에서는 이러한 확장에 대한 공용 API가 제공되지 않지만(API는 향후 안정화될 예정입니다), 자체 컴파일러 플러그인을 이미 구축하고 있는 JetPack Compose 등의 파트너와 긴밀히 협력하고 있습니다.

Kotlin 라이브러리 형식 KLib 도입

클라이언트가 의존할 수 있도록 Kotlin에서 다중 플랫폼 라이브러리를 빌드하고 배포하려면 어떤 플랫폼에서든 똑같이 작동하는 배포 형식이 필요합니다. 그래서 Kotlin 다중 플랫폼용 라이브러리 형식인 KLib를 도입하게 되었습니다. KLib 파일에는 시리얼화된 IR이 포함되어 있습니다. 코드는 이를 종속성으로 추가할 수 있으며, 컴파일러 백엔드는 이를 사용해 지정된 플랫폼에 대한 실행 가능한 코드를 생성합니다. 바이트코드와의 유사성은 여기서도 적용되어, KLib를 JVM 바이트코드처럼 분석하고 변환할 수 있습니다. 시리얼화된 IR에 수행된 모든 변환은 KLib가 사용되는 모든 플랫폼에 영향을 미칩니다.

사실, Kotlin/Native는 꽤 오랫동안 KLib 형식을 사용해 Kotlin 네이티브 라이브러리를 배포하고 있었으며, 이제 다른 백엔드 및 다중 플랫폼 라이브러리를 지원하기 위하여 이 형식을 확장하고 있습니다. 이 형식은 1.4에서 실험적인 형태로 적용되지만 향후 버전에서 안정적인 ABI가 제공될 예정입니다.

다중 플랫폼에 관한 기타 소식

Android Studio에서 iOS 코드 실행

현재, iOS 장치 및 시뮬레이터에서 Kotlin 코드를 실행, 테스트, 디버그할 수 있는 Android Studio용 플러그인을 개발하고 있습니다. 이 플러그인은 IntelliJ의 자체적인 코드를 사용하므로 소스는 공개되지 않습니다. Objective-C 또는 Swift에 대한 언어 지원은 제공되지 않고, AppStore 배포와 같은 일부 작업은 Xcode의 실행이 필요할 수도 있습니다. 하지만, 새로운 플러그인을 설치하면 Kotlin 코드를 사용해 수행하는 모든 작업이 Android Studio에서도 지원됩니다. 2020년에 이 플러그인의 테스트 버전이 공개될 예정입니다.

Kotlin/Native 런타임 개선

Linux, Windows, macOS 및 iOS 외에도 Kotlin/Native가 이제 watchOS 및 tvOS에서 작동하여, 사실상 모든 장치에서 Kotlin을 실행할 수 있게 되었습니다. 또한, iOS Kotlin 프로그램이 더 빠르게 실행되도록 Kotlin/Native의 런타임 성능을 개선하고 있습니다.

코어 라이브러리

Kotlin 코어 라이브러리는 모든 플랫폼에서 작동합니다. 여기에는 모든 기본 타입 및 컬렉션을 취급하는 kotlin-stdlibkotlinx.coroutines, kotlinx.serializationkotlinx.io가 포함됩니다. 다중 플랫폼에서는 날짜에 대한 지원이 매우 필요하며, 현재 이에 대한 작업이 진행되고 있습니다. 실험적인 Durations가 stdlib에 이미 추가되었고, DateTime에 대한 지원이 현재 진행 중입니다.

Kotlin 라이브러리에 추가된 또 다른 중요한 기능인 Flow는 Reactive Streams의 코루틴 기반 구현입니다. Flow는 데이터 스트림을 처리하는 데 매우 탁월하며, 이 방면에서 Kotlin의 능력을 활용하고 있습니다. 인체 공학적 장점 외에, Flow는 속도 향상에도 기여합니다. 몇몇 벤치마크에 의하면 Flow는 통용되는 기존의 Reactive Streams 구현보다 약 2배 더 빠릅니다.

라이브러리 작성자를 위한 개선 사항

새로운 라이브러리의 생성은 Kotlin 에코시스템에 매우 중요한 일이므로 라이브러리 작성자들의 이용 환경을 지속적으로 개선하고 있습니다. 새로운 라이브러리 작성 모드를 통해 안정적인 API에 가장 적합한 방식으로 코드를 생성할 수 있습니다. 또한, Dokka 1.0을 출시하여 모든 플랫폼에서 문서 생성을 지원할 예정입니다.

다중 플랫폼 웹

플랫폼 간에 코드를 공유하면 모바일에서뿐만 아니라 웹에서도 많은 도움이 됩니다. 서버 및 모바일 앱과 수많은 코드를 공유할 수 있습니다. 저희는 Kotlin/JS 도구에 더욱더 많은 투자를 하고 있으며, 현재 Kotlin 코드 변경에서 브라우저에서의 결과 확인까지 다양한 개발 주기를 매우 빠르게 진행할 수 있게 되었습니다.

또한, JS 상호 운용성을 개선하여 NPM 종속성을 Kotlin 프로젝트에 연결할 수 있고, .d.ts 타입 정의는 Kotlin 툴체인에서 자동으로 선택됩니다.

그리고, 새로운 IR 기반 백엔드를 통해 바이너리 규모에서 많은 개선 사항이 추가되었습니다. 컴파일된 JS 파일은 현재 크기의 절반까지 작아질 수 있습니다.

새로운 언어 기능

Kotlin 1.4는 몇 가지 새로운 언어 기능을 제공합니다.

Kotlin 클래스의 SAM 변환

커뮤니티로부터 Kotlin 클래스의 SAM 변환 지원을 추가하도록 요청받았습니다(KT-7770). 하나의 추상 메소드만 지닌 인터페이스 또는 클래스가 매개 변수로 기대된 경우 람다를 인수로 전달하면 SAM 변환이 적용됩니다. 그런 다음, 컴파일러가 추상 멤버 함수를 구현하는 클래스의 인스턴스로 람다를 변환합니다.

SAM 변환은 현재 Java 인터페이스 및 추상 클래스에 대해서만 작동합니다. 이렇게 설계한 원래의 목적은 명시적으로 해당 사용 사례로만 함수 형식을 사용하기 위함이었습니다. 하지만, 함수 형식 및 형식 별칭이 모든 사용 사례에 대응하지 않고, 종종 SAM 변환을 수행할 목적으로만 Java에서 인터페이스를 유지해야 했습니다.

Java와 달리 Kotlin은 하나의 추상 메소드만 지닌 인터페이스에 대해 SAM 변환을 허용하지 않습니다. 저희는 인터페이스에 SAM 변환을 적용하려는 의도를 명확하게 해야 한다고 생각합니다. 따라서, SAM 인터페이스를 정의하려면 인터페이스에 fun 키워드를 표기하여, 해당 인터페이스가 함수형 인터페이스로 사용될 수 있음을 강조해야 합니다.

fun interface 대신 람다를 전달하는 것은 새로운 타입 추론 알고리즘에서만 지원된다는 점을 유의해주세요.

명명된 인수 및 포지셔널 인수의 혼합

모든 포지셔널 인수 뒤에 명명된 인수를 배치하는 경우를 제외하고, Kotlin은 명시적인 이름을 지닌 인수(“named”)와 이름이 없는 일반적인 인수(“positional”)를 혼합하는 것을 금지합니다. 하지만, 모든 인수가 정확한 위치에 있는데, 중간에 있는 인수에 이름을 지정하려는 경우 매우 성가신 작업이 됩니다. Kotlin 1.4에서는 이 문제가 해결되어, 다음과 같이 코드를 작성할 수 있습니다.

최적화된 위임된 속성

lazy 속성 및 다른 몇몇 위임된 속성이 컴파일되는 기본적인 방식을 개선할 예정입니다.

일반적으로 위임된 속성은 상응하는 KProperty reflection 객체에 액세스할 수 있습니다. 예를 들어, Delegates.observable을 사용할 때 수정된 속성에 대한 정보를 다음과 같이 표시할 수 있습니다.

이를 가능하게 하기 위하여 Kotlin 컴파일러는 추가적인 구문 멤버 속성을 생성하여, 클래스 안에 사용된 위임된 속성을 나타내는 모든 KProperty 객체를 배열에 보관합니다.

하지만, 일부 위임된 속성은 어떤 방식이로든 KProperty를 사용하지 않습니다. 이 경우 $$delegatedProperties에서 객체를 생성하는 것은 최적이 아닙니다. Kotlin 1.4 릴리스에서는 이 사용 사례가 최적화됩니다. 위임된 속성 연산자가 inline이고, KProperty 매개 변수가 사용되지 않은 경우 상응하는 reflection 객체는 생성되지 않습니다.

가장 두드러진 예는 lazy 속성입니다. lazy 속성에 대한 getValue 구현은 inline이고, KProperty 매개 변수를 사용하지 않습니다.

Kotlin 1.4부터는 lazy 속성을 정의할 때, 상응하는 KProperty 인스턴스가 생성되지 않습니다. 클래스에서 사용하는 유일한 위임된 속성이 lazy 속성(및 이 최적화에 부합하는 다른 속성)일 경우 해당 클래스에 대해 전체 $$delegatedProperties 배열은 생성되지 않습니다.

Trailing Commas

이 사소한 구문 변경은 놀라울 정도의 편리함을 가져왔습니다! 매개 변수 목록의 마지막 매개 변수 뒤에 trailing comma를 추가할 수 있습니다. 그런 다음, 누락된 쉼표를 추가 또는 제거할 필요 없이 라인을 바꾸거나 새로운 매개 변수를 추가할 수 있습니다.

기타 주목할 만한 변경 사항

Kotlin 1.3.40에서 도입된 유용한 typeof 함수가 안정화되어 모든 플랫폼에서 지원됩니다.

when 안에서 breakcontinue를 활성화하는 기능은 1.3.60 릴리스 블로그 게시물에서 이미 소개되었습니다.

감사합니다!

Kotlin EAP 및 실험적 기능을 사용해 보고 피드백을 제공해 주신 모든 분께 진심으로 감사드립니다. 저희는 여러분과 함께 Kotlin 언어를 개발하고 있으며, 여러분의 소중한 의견에 근거하여 많은 디자인 결정을 내리고 있습니다. 커뮤니티와의 피드백 주기를 이렇게 빠르고 효과적으로 유지하는 일은 Kotlin을 최고의 언어로 만드는 데 매우 중요합니다!

Kotlin을 사용해 수많은 멋진 작품을 만들고 계신 모든 커뮤니티 회원께 정말로 깊이 감사드립니다. 앞으로도 계속해서 Kotlin을 발전시켜 나갔으면 좋겠습니다!

그리고, IntelliJ IDEA 및 Android Studio 안의 Kotlin 플러그인은 사용자의 기능 사용에 대하여 익명화된 통계 정보를 수집합니다. 이 통계 정보는 무엇이 작동하거나, 문제를 일으키고 있는지 그리고 어떤 것을 집중적으로 개선해야 하는지 이해하는 데 도움이 되므로, 이 정보의 제공에 동의해 주실 것을 부탁드립니다.

본문은 Svetlana IsakovaWhat to Expect in Kotlin 1.4 and Beyond를 번역한 글입니다.

image description

Discover more