Android KSP(Kotlin Symbol Processing) 오류 처리



개인 광고 영역

앞 두 글에서 KSP 사용법까지 알아보았다. 이 글에서는 KSP의 로그 출력을 통한 디버그와 오류 처리에 대해 살펴본다.

Kotlin Symbol Processing API - 공식 문서

KSP를 통해 작성한 오류는 단순 워닝을 통해 로그를 대체할 수 있고, 오류 발생으로 빌드에 실패하도록 할 수도 있다.


이 글에서 알아볼 내용

  • 로그 출력하는 방법을 알아본다
  • ksp 에서 오류 처리 방법을 살펴본다


로그 출력하는 방법

로그 출력 방법은 KSPLogger를 활용한다.

KSP Logger에는 5개의 fun 정의를 가지고 있다.

fun logging(message: String, symbol: KSNode? = null)
fun info(message: String, symbol: KSNode? = null)
fun warn(message: String, symbol: KSNode? = null)
fun error(message: String, symbol: KSNode? = null)

fun exception(e: Throwable)

필자는 info, warn, error를 주로 사용했는데 이를 통해 로그를 출력할 수 있다. 만약 진행을 멈추거나 오류 위치를 알려주고 싶다면 뒤에 있는 KSNode를 넘겨주어야 한다.

KSNode의 정보를 가지고, 진행 중 오류가 발생한 class 정보를 제공할 수 있다.

[ksp] /Users/.../app/src/main/java/tech/thdev/app/ui/BActivity.kt:6:
   No activity class!!! BActivity Remove @GenerateAnnotation


KSP의 디버깅

KSP에서의 디버깅은 logger를 활용하는 게 가장 좋은데, 이때 KSPLogger의 info, warn, error를 통해 확인할 수 있다.

아쉽지만 디버깅하는 방법은 아직 잘 모르겠는데, 혹시 아시는 분은 댓글로 부탁드립니다.

필자는 KSPLogger를 넘겨줘 logger 출력을 통해 디버깅하는 방법으로 적용하고 있다.


이전 코드의 문제점을 찾아보자.

이전 글에서 작성한 startActivity 처리하는 코드의 문제점이 있는데, Activity가 아니더라도 자동으로 extension을 만들어준다는 점이다.

상속을 확인하지 않고, @GenerateAnnotation이 붙어있을 경우 무조건 extension을 생성한다는 점이다.

이렇게 생성된 코드는 당장은 문제가 없지만 runtime에 exception 발생이 난다.

KSP를 통해 사전에 activity 인지 체크하고, 안정적인 코드 작성을 돕는 게 중요한데, KSPLogger의 error를 통해 이를 만들 수 있다.


오류를 내보자.

그러면 오류를 발생하도록 코드 변경이 필요하다. 어느 시점이 좋을까?

  • @GenerateAnnotation을 서칭하는 위치가 좋을까?
  • 파일로 쓰기 직전이 좋을까?

당연히 첫 번째 방법인 @GenerateAnnotation 찾는 위치에서 이를 확인하는 게 좋은데, 방법은 간단하다.

  • 나의 상속 클래스 정보를 분석하고, 이를 filter 한다.

먼저 다음의 코드에 @GenerateAnnotation을 붙였다고 가정하자.

@GenerateAnnotation
class BActivity

어노테이션이 붙어있기 때문에 문제없이 showBActivity() extension이 만들어진다.

public fun Context.showBActivity(): Unit {
      Intent(this, BActivity::class.java).run {
          startActivity(this)
      }
}

이 함수가 호출되는 runtime 시점에 오류가 발생한다.

이미 ksClassDeclaration 클래스 정보를 통해 상속 class 정보를 가져올 수 있는데, superType을 통해 이를 확인할 수 있다.

superType은 N 개일 수 있으니 다음과 같이 첫 번째 superType 정보를 불러오고, filter 한다.

단순하게 코드로 작성한 부분이라 N 개만큼 다음의 코드가 동작한다. 이 코드는 단순히 AppCompatActivity로 시작하는 이름을 서칭하고, NotNull인 경우를 필터링하는 코드이다.

.filter { ksClassDeclaration ->
    ksClassDeclaration.superTypes
        .map {
            it.resolve().declaration
        }
        .firstOrNull { superType -> superType.simpleName.asString() == "AppCompatActivity" } != null
}

이 코드는 @GenerateAnnotation를 적용한 상속 클래스가 AppCompatActivity 인지를 체크하게 된다.


Activity 상속하지 않은 클래스라면 오류를 표시해 주자.

Activity 상속하지 않았다면 오류 발생을 시키는 게 좋은데, 1차적으로 로그만 출력해 보자.

다음과 같이 logger.error에 오류 메시지를 추가한다. 동적으로 클래스 이름도 출력해 주면 좋으니 아래와 같이 ksClassDeclaration.simpleName을 출력해 본다.

.filter { ksClassDeclaration ->
    val findClass = ksClassDeclaration.superTypes
        .map {
            it.resolve().declaration
        }
        .firstOrNull { superType -> superType.simpleName.asString() == "AppCompatActivity" }

    if (findClass == null) {
        logger.error("No activity class!!! ${ksClassDeclaration.simpleName.asString()} Remove @GenerateAnnotation")
    }
    true
}

이 코드의 결과는 다음과 같다.

e: [ksp] No activity class!!! BActivity Remove @GenerateAnnotation

이러한 방법으로는 warn과 동일한 수준의 메시지 출력을 하는데, Android studio의 build 환경에 그냥 지나가는 오류로 노출되게 된다.

image_04

하지만 왼쪽 빌드에서도 확인이 가능한데 코드상으론 오류지만 실제론 오류로 잡히지 않는다.

그래서 다음과 같이 logger의 마지막에 class 정보를 함께 넘겨주어야 한다.

logger.error("No activity class!!! ${ksClassDeclaration.simpleName.asString()} Remove @GenerateAnnotation", ksClassDeclaration)

이 경우 오류는 진행이 멈추며, 라인도 알려준다.

[ksp] /Users/taehwankwon/Dropbox/StudioProjects/Android-BlogExample/app/src/main/java/tech/thdev/app/ui/BActivity.kt:6:
   No activity class!!! BActivity Remove @GenerateAnnotation

image_05


마무리

KSP를 작성하는 개발자는 로그 확인을 통해 코드 확인이 가능할 수 있지만, 이를 모르고 사용하는 개발자는 모르고 지나칠 수 있다.

단순히 디버깅 노출을 필요로 하는 경우라면 logger의 warn, info 등을 통해 로그 출력이 가능하지만, 위치도 알려야 한다면 꼭 class 정보를 함께 넘겨야 한다.

이 글에서는 KSP logger 확인 방법을 알아보았다.



About Taehwan

My name is Taehwan Kwon. I have developed Android for 6 years and blog has been active for eight years.

Comments