Skip to content

Kotlin 심볼 프로세싱 API

Kotlin 심볼 프로세싱(KSP)은 경량 컴파일러 플러그인을 개발하는 데 사용할 수 있는 API입니다. KSP는 Kotlin의 강력한 기능을 활용하면서도 학습 곡선을 최소화하는 단순화된 컴파일러 플러그인 API를 제공합니다. kapt와 비교했을 때, KSP를 사용하는 어노테이션 프로세서는 최대 두 배 더 빠르게 실행될 수 있습니다.

개요

KSP API는 Kotlin 프로그램을 관용적으로 처리합니다. KSP는 확장 함수, 선언 위치 분산(declaration-site variance) 및 지역 함수(local functions)와 같은 Kotlin 고유 기능을 이해합니다. 또한 타입을 명시적으로 모델링하고 동등성 및 할당 호환성과 같은 기본적인 타입 검사를 제공합니다.

이 API는 Kotlin 문법에 따라 Kotlin 프로그램 구조를 심볼 수준에서 모델링합니다. KSP 기반 플러그인이 소스 프로그램을 처리할 때, 클래스, 클래스 멤버, 함수 및 연관된 매개변수와 같은 구성 요소는 프로세서에서 접근할 수 있지만, if 블록 및 for 루프와 같은 요소는 그렇지 않습니다.

개념적으로 KSP는 Kotlin 리플렉션의 KType와 유사합니다. 이 API를 사용하면 프로세서가 클래스 선언에서 특정 타입 인수를 가진 해당 타입으로 이동하거나 그 반대로 이동할 수 있습니다. 또한 타입 인수를 대체하고, 분산(variance)을 지정하고, 스타 프로젝션을 적용하고, 타입의 널러블성(nullability)을 표시할 수 있습니다.

KSP를 Kotlin 프로그램의 전처리기 프레임워크로 생각할 수도 있습니다. KSP 기반 플러그인을 심볼 프로세서 또는 간단히 _프로세서_라고 간주함으로써, 컴파일의 데이터 흐름은 다음 단계로 설명할 수 있습니다.

  1. 프로세서는 소스 프로그램과 리소스를 읽고 분석합니다.
  2. 프로세서는 코드 또는 다른 형태의 출력을 생성합니다.
  3. Kotlin 컴파일러는 소스 프로그램과 생성된 코드를 함께 컴파일합니다.

완전한 컴파일러 플러그인과 달리 프로세서는 코드를 수정할 수 없습니다. 언어 의미론을 변경하는 컴파일러 플러그인은 때때로 매우 혼란스러울 수 있습니다. KSP는 소스 프로그램을 읽기 전용으로 처리하여 이를 방지합니다.

이 비디오에서 KSP에 대한 개요를 확인할 수도 있습니다:

KSP가 소스 파일을 보는 방식

대부분의 프로세서는 입력 소스 코드의 다양한 프로그램 구조를 탐색합니다. API 사용법을 자세히 알아보기 전에, KSP의 관점에서 파일이 어떻게 보이는지 살펴보겠습니다:

text
KSFile
  packageName: KSName
  fileName: String
  annotations: List<KSAnnotation>  (File annotations)
  declarations: List<KSDeclaration>
    KSClassDeclaration // class, interface, object
      simpleName: KSName
      qualifiedName: KSName
      containingFile: String
      typeParameters: KSTypeParameter
      parentDeclaration: KSDeclaration
      classKind: ClassKind
      primaryConstructor: KSFunctionDeclaration
      superTypes: List<KSTypeReference>
      // contains inner classes, member functions, properties, etc.
      declarations: List<KSDeclaration>
    KSFunctionDeclaration // top level function
      simpleName: KSName
      qualifiedName: KSName
      containingFile: String
      typeParameters: KSTypeParameter
      parentDeclaration: KSDeclaration
      functionKind: FunctionKind
      extensionReceiver: KSTypeReference?
      returnType: KSTypeReference
      parameters: List<KSValueParameter>
      // contains local classes, local functions, local variables, etc.
      declarations: List<KSDeclaration>
    KSPropertyDeclaration // global variable
      simpleName: KSName
      qualifiedName: KSName
      containingFile: String
      typeParameters: KSTypeParameter
      parentDeclaration: KSDeclaration
      extensionReceiver: KSTypeReference?
      type: KSTypeReference
      getter: KSPropertyGetter
        returnType: KSTypeReference
      setter: KSPropertySetter
        parameter: KSValueParameter

이 보기에는 파일에 선언된 일반적인 요소(클래스, 함수, 프로퍼티 등)가 나열됩니다.

SymbolProcessorProvider: 진입점

KSP는 SymbolProcessor를 인스턴스화하기 위해 SymbolProcessorProvider 인터페이스의 구현체를 요구합니다:

kotlin
interface SymbolProcessorProvider {
    fun create(environment: SymbolProcessorEnvironment): SymbolProcessor
}

SymbolProcessor는 다음과 같이 정의됩니다:

kotlin
interface SymbolProcessor {
    fun process(resolver: Resolver): List<KSAnnotated> // Let's focus on this
    fun finish() {}
    fun onError() {}
}

ResolverSymbolProcessor에게 심볼과 같은 컴파일러 세부 정보에 대한 접근 권한을 제공합니다. 모든 최상위 함수(top-level functions)와 최상위 클래스의 비지역 함수(non-local functions)를 찾는 프로세서는 다음과 같이 보일 수 있습니다:

kotlin
class HelloFunctionFinderProcessor : SymbolProcessor() {
    // ...
    val functions = mutableListOf<KSClassDeclaration>()
    val visitor = FindFunctionsVisitor()

    override fun process(resolver: Resolver) {
        resolver.getAllFiles().forEach { it.accept(visitor, Unit) }
    }

    inner class FindFunctionsVisitor : KSVisitorVoid() {
        override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
            classDeclaration.getDeclaredFunctions().forEach { it.accept(this, Unit) }
        }

        override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
            functions.add(function)
        }

        override fun visitFile(file: KSFile, data: Unit) {
            file.declarations.forEach { it.accept(this, Unit) }
        }
    }
    // ...
    
    class Provider : SymbolProcessorProvider {
        override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = TODO()
    }
}

자료

지원 라이브러리

다음 표는 Android에서 인기 있는 라이브러리 목록과 KSP에 대한 다양한 지원 단계를 포함합니다:

라이브러리상태
Room공식적으로 지원됨
Moshi공식적으로 지원됨
RxHttp공식적으로 지원됨
Kotshi공식적으로 지원됨
Lyricist공식적으로 지원됨
Lich SavedState공식적으로 지원됨
gRPC Dekorator공식적으로 지원됨
EasyAdapter공식적으로 지원됨
Koin Annotations공식적으로 지원됨
Glide공식적으로 지원됨
Micronaut공식적으로 지원됨
Epoxy공식적으로 지원됨
Paris공식적으로 지원됨
Auto Dagger공식적으로 지원됨
SealedX공식적으로 지원됨
Ktorfit공식적으로 지원됨
Mockative공식적으로 지원됨
DeeplinkDispatchairbnb/DeepLinkDispatch#323를 통해 지원됨
Dagger알파
Motif알파
Hilt진행 중
Auto Factory아직 지원되지 않음