KSP가 필요한 이유
컴파일러 플러그인은 코드 작성 방식을 크게 향상시킬 수 있는 강력한 메타프로그래밍 도구입니다. 컴파일러 플러그인은 컴파일러를 라이브러리처럼 직접 호출하여 입력 프로그램을 분석하고 편집합니다. 이 플러그인들은 또한 다양한 용도로 결과물을 생성할 수 있습니다. 예를 들어, 상용구 코드를 생성할 수 있으며, Parcelable
과 같이 특별히 표시된 프로그램 요소에 대한 완전한 구현체를 생성할 수도 있습니다. 플러그인에는 다양한 다른 용도가 있으며, 언어에서 직접 제공되지 않는 기능을 구현하고 미세 조정하는 데 사용될 수도 있습니다.
컴파일러 플러그인은 강력하지만, 이러한 강력함에는 대가가 따릅니다. 가장 간단한 플러그인을 작성하더라도 어느 정도의 컴파일러 배경 지식과 특정 컴파일러의 구현 세부 사항에 대한 일정 수준의 익숙함이 필요합니다. 또 다른 실제적인 문제는 플러그인이 특정 컴파일러 버전에 밀접하게 연결되어 있는 경우가 많다는 것입니다. 이는 새로운 버전의 컴파일러를 지원할 때마다 플러그인을 업데이트해야 할 수도 있음을 의미합니다.
KSP는 경량 컴파일러 플러그인 생성을 더 쉽게 만듭니다.
KSP는 컴파일러 변경 사항을 숨기도록 설계되어, 이를 사용하는 프로세서의 유지보수 노력을 최소화합니다. KSP는 JVM에 종속되지 않도록 설계되어, 향후 다른 플랫폼에 더 쉽게 적용될 수 있습니다. KSP는 또한 빌드 시간을 최소화하도록 설계되었습니다. Glide와 같은 일부 프로세서의 경우, KSP는 kapt와 비교했을 때 전체 컴파일 시간을 최대 25% 단축합니다.
KSP 자체는 컴파일러 플러그인으로 구현됩니다. Google의 Maven 저장소에는 프로젝트를 직접 빌드할 필요 없이 다운로드하여 사용할 수 있는 사전 빌드된 패키지가 있습니다.
kotlinc 컴파일러 플러그인과의 비교
kotlinc
컴파일러 플러그인은 컴파일러의 거의 모든 것에 접근할 수 있으므로 최대의 강력함과 유연성을 가집니다. 반면에 이러한 플러그인은 컴파일러의 모든 것에 잠재적으로 의존할 수 있기 때문에 컴파일러 변경에 민감하며 자주 유지보수해야 합니다. 또한 이 플러그인들은 kotlinc
구현에 대한 깊은 이해가 필요하므로 학습 곡선이 가파를 수 있습니다.
KSP는 잘 정의된 API를 통해 대부분의 컴파일러 변경 사항을 숨기는 것을 목표로 하지만, 컴파일러나 Kotlin 언어의 주요 변경 사항은 여전히 API 사용자에게 노출되어야 할 수 있습니다.
KSP는 강력함을 단순함과 맞바꾼 API를 제공하여 일반적인 사용 사례를 충족시키려고 노력합니다. 그 기능은 일반 kotlinc
플러그인의 엄격한 하위 집합입니다. 예를 들어, kotlinc
는 표현식과 문을 검사하고 코드를 수정할 수도 있지만, KSP는 그럴 수 없습니다.
kotlinc
플러그인을 작성하는 것이 재미있을 수 있지만, 많은 시간이 걸릴 수도 있습니다. 만약 kotlinc
의 구현을 학습할 여유가 없고 소스 코드를 수정하거나 표현식을 읽을 필요가 없다면, KSP가 좋은 선택일 수 있습니다.
리플렉션과의 비교
KSP의 API는 kotlin.reflect
와 비슷해 보입니다. 둘의 주요 차이점은 KSP의 타입 참조가 명시적으로 해석(resolve)되어야 한다는 것입니다. 이것이 인터페이스가 공유되지 않는 이유 중 하나입니다.
kapt와의 비교
kapt는 많은 Java 어노테이션 프로세서가 Kotlin 프로그램에서 즉시 작동하도록 만드는 뛰어난 솔루션입니다. KSP가 kapt에 비해 가지는 주요 장점은 향상된 빌드 성능, JVM에 종속되지 않는다는 점, 더 관용적인 Kotlin API, 그리고 Kotlin 전용 심볼을 이해하는 능력입니다.
Java 어노테이션 프로세서를 수정 없이 실행하기 위해, kapt는 Kotlin 코드를 Java 어노테이션 프로세서가 필요로 하는 정보를 유지하는 Java 스텁으로 컴파일합니다. 이 스텁을 생성하기 위해 kapt는 Kotlin 프로그램의 모든 심볼을 해석(resolve)해야 합니다. 스텁 생성은 전체 kotlinc
분석 시간의 약 1/3과 kotlinc
코드 생성 시간과 비슷한 수준의 비용이 듭니다. 많은 어노테이션 프로세서의 경우, 이는 프로세서 자체에서 소요되는 시간보다 훨씬 깁니다. 예를 들어, Glide는 미리 정의된 어노테이션이 있는 매우 제한된 수의 클래스만 확인하며, 코드 생성은 상당히 빠릅니다. 거의 모든 빌드 오버헤드는 스텁 생성 단계에 있습니다. KSP로 전환하면 컴파일러에서 소요되는 시간을 즉시 25% 줄일 수 있습니다.
성능 평가를 위해, Tachiyomi 프로젝트를 위한 코드를 생성하도록 KSP로 Glide의 간소화된 버전을 구현했습니다. 테스트 장치에서 프로젝트의 전체 Kotlin 컴파일 시간은 21.55초였지만, kapt가 코드를 생성하는 데 8.67초가 걸렸고, KSP 구현이 코드를 생성하는 데 1.15초가 걸렸습니다.
kapt와 달리, KSP의 프로세서는 입력 프로그램을 Java의 관점에서 보지 않습니다. API는 Kotlin에 더 자연스러우며, 특히 최상위 함수와 같은 Kotlin 특정 기능에 더욱 그렇습니다. KSP는 kapt처럼 javac
에 위임하지 않기 때문에, JVM 특정 동작을 가정하지 않으며 잠재적으로 다른 플랫폼과 함께 사용될 수 있습니다.
제한 사항
KSP는 대부분의 일반적인 사용 사례에 대한 간단한 솔루션이 되려고 노력하지만, 다른 플러그인 솔루션에 비해 몇 가지 절충을 했습니다. 다음은 KSP의 목표가 아닙니다.
- 소스 코드의 표현식 수준 정보 검사.
- 소스 코드 수정.
- Java Annotation Processing API와의 100% 호환성.