Skip to content

다중 라운드 처리

KSP는 _다중 라운드 처리_를 지원합니다. 이는 여러 라운드에 걸쳐 파일을 처리하는 것을 의미합니다. 후속 라운드는 이전 라운드의 결과물을 추가 입력으로 사용합니다.

프로세서 변경 사항

다중 라운드 처리를 사용하려면 SymbolProcessor.process() 함수가 유효하지 않은 심볼에 대해 지연된 심볼(List<KSAnnotated>) 목록을 반환해야 합니다. KSAnnotated.validate()를 사용하여 다음 라운드로 지연될 유효하지 않은 심볼을 필터링할 수 있습니다.

다음 샘플 코드는 유효성 검사(validation check)를 사용하여 유효하지 않은 심볼을 지연시키는 방법을 보여줍니다.

kotlin
override fun process(resolver: Resolver): List<KSAnnotated> {
    val symbols = resolver.getSymbolsWithAnnotation("com.example.annotation.Builder")
    val result = symbols.filter { !it.validate() }
    symbols
        .filter { it is KSClassDeclaration && it.validate() }
        .map { it.accept(BuilderVisitor(), Unit) }
    return result
}

다중 라운드 동작 방식

심볼을 다음 라운드로 지연시키기

프로세서는 특정 심볼의 처리를 다음 라운드로 지연시킬 수 있습니다. 심볼이 지연되면, 프로세서는 다른 프로세서가 추가 정보를 제공하기를 기다립니다. 심볼을 필요한 만큼 여러 라운드에 걸쳐 계속 지연시킬 수 있습니다. 다른 프로세서가 필요한 정보를 제공하면, 프로세서는 지연된 심볼을 처리할 수 있습니다. 프로세서는 필요한 정보가 부족한 유효하지 않은 심볼만 지연시켜야 합니다. 따라서 프로세서는 클래스패스(classpath)의 심볼을 지연시켜서는 안 되며, KSP는 소스 코드가 아닌 지연된 심볼도 걸러냅니다.

예를 들어, 어노테이션이 붙은 클래스를 위한 빌더를 생성하는 프로세서는 생성자의 모든 파라미터 타입이 유효할 것(구체적인 타입으로 확인될 것)을 요구할 수 있습니다. 첫 번째 라운드에서는 파라미터 타입 중 하나가 확인 불가능합니다. 그 후 두 번째 라운드에서는 첫 번째 라운드에서 생성된 파일 덕분에 확인 가능하게 됩니다.

심볼 유효성 검사

심볼을 지연시켜야 할지 결정하는 편리한 방법은 유효성 검사를 통해서입니다. 프로세서는 심볼을 올바르게 처리하는 데 어떤 정보가 필요한지 알아야 합니다. 유효성 검사는 일반적으로 비용이 많이 들 수 있는 해석(resolution)을 필요로 하므로, 필요한 것만 확인하는 것을 권장합니다. 이전 예시를 이어가자면, 빌더 프로세서에 대한 이상적인 유효성 검사는 어노테이션이 붙은 심볼의 생성자에서 확인된 모든 파라미터 타입이 isError == false를 포함하는지 여부만 확인합니다.

KSP는 기본 유효성 검사 유틸리티를 제공합니다. 자세한 내용은 고급 섹션을 참조하세요.

종료 조건

다중 라운드 처리는 전체 라운드 처리에서 새로운 파일이 생성되지 않을 때 종료됩니다. 종료 조건이 충족되었을 때 처리되지 않은 지연된 심볼이 여전히 존재하면, KSP는 처리되지 않은 지연된 심볼을 가진 각 프로세서에 대해 오류 메시지를 기록합니다.

각 라운드에서 접근 가능한 파일

새로 생성된 파일과 기존 파일 모두 Resolver를 통해 접근할 수 있습니다. KSP는 파일 접근을 위한 두 가지 API를 제공합니다: Resolver.getAllFiles()Resolver.getNewFiles(). getAllFiles()는 기존 파일과 새로 생성된 파일 모두의 결합된 목록을 반환하며, getNewFiles()는 새로 생성된 파일만 반환합니다.

getSymbolsAnnotatedWith() 변경 사항

불필요한 심볼 재처리를 방지하기 위해 getSymbolsAnnotatedWith()는 새로 생성된 파일에서 발견된 심볼과 지난 라운드에서 지연된 심볼만을 반환합니다.

프로세서 인스턴스화

프로세서 인스턴스는 한 번만 생성됩니다. 이는 프로세서 객체에 정보를 저장하여 이후 라운드에서 사용할 수 있음을 의미합니다.

라운드 간 일관된 정보

이전 라운드에서 생성된 내용에 따라 해석(resolution) 결과가 잠재적으로 변경될 수 있으므로, 모든 KSP 심볼은 여러 라운드에 걸쳐 재사용될 수 없습니다. 그러나 KSP는 기존 코드 수정을 허용하지 않으므로, 심볼 이름에 대한 문자열 값과 같은 일부 정보는 여전히 재사용 가능해야 합니다. 요약하자면, 프로세서는 이전 라운드의 정보를 저장할 수 있지만, 이 정보가 미래 라운드에서 유효하지 않을 수 있다는 점을 명심해야 합니다.

오류 및 예외 처리

오류(프로세서가 KSPLogger.error()를 호출하여 정의됨) 또는 예외가 발생하면 현재 라운드가 완료된 후 처리가 중단됩니다. 모든 프로세서는 onError() 메서드를 호출하며, finish() 메서드는 호출하지 않습니다.

오류가 발생했더라도 다른 프로세서들은 해당 라운드에서 정상적으로 처리를 계속합니다. 이는 오류 처리가 해당 라운드의 처리가 완료된 후에 발생한다는 것을 의미합니다.

예외 발생 시, KSP는 KSP에서 발생한 예외와 프로세서에서 발생한 예외를 구분하려고 시도합니다. 예외는 즉시 처리 종료를 초래하며 KSPLogger에 오류로 기록됩니다. KSP에서 발생한 예외는 추가 조사를 위해 KSP 개발자에게 보고되어야 합니다. 예외 또는 오류가 발생한 라운드의 끝에서, 모든 프로세서는 자체 오류 처리를 수행하기 위해 onError() 함수를 호출합니다.

KSP는 SymbolProcessor 인터페이스의 일부로 onError()에 대한 기본 no-op 구현을 제공합니다. 이 메서드를 오버라이드하여 자체 오류 처리 로직을 제공할 수 있습니다.

고급

유효성 검사의 기본 동작

KSP가 제공하는 기본 유효성 검사 로직은 검사 대상 심볼의 포함하는 스코프 내에서 직접 접근 가능한 모든 심볼을 검증합니다. 기본 유효성 검사는 포함된 스코프의 참조가 구체적인 타입으로 확인(해결) 가능한지 여부를 확인하지만, 유효성 검사를 수행하기 위해 참조된 타입으로 재귀적으로 깊이 들어가지 않습니다.

사용자 지정 유효성 검사 로직 작성

기본 유효성 검사 동작이 모든 경우에 적합하지 않을 수 있습니다. KSValidateVisitor를 참조하고, 사용자 지정 predicate 람다를 제공하여 자체 유효성 검사 로직을 작성할 수 있습니다. 이 람다는 KSValidateVisitor에 의해 검사해야 할 심볼을 필터링하는 데 사용됩니다.