ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kotlin] Kotlin의 컴파일 과정 & Kotlin VS JAVA (Annotation Processor)
    Mhwan's Study/JAVA & Kotlin 2021. 8. 9. 02:47

    이 글은 코틀린을 이미 어느정도 아시는 분들을 위한 포스팅입니다. 코틀린을 모르시는 분들에게는 다소 불친절할 수 있으니 양해부탁드립니다.

    다들 이미 아시겠지만 코틀린은 JVM위에서 동작하며, JAVA와 마찬가지로 정적 타입 프로그래밍 언어입니다.

    * 정적 타입(Statically typed) 프로그래밍 언어 : 타입 즉, 자료형을 컴파일 타임에 결정하는 프로그래밍 언어입니다. 
    장점 : 컴파일 타임에 타입에 대한 정보가 결정되어 런타임에 메소드를 더 빨리 호출할 수 있습니다. 안정성또한 올라 갑니다. (C, C++, JAVA, Kotlin 등)
    단점 : 타입을 런타임에 결정하는 프로그래밍 언어로 런타임까지 타입에 대한 결정을 미룰 수 있어 다양한 선택지를 가질 수 있는 이점을 가집니다. (단 안정성이 그만큼 낮아지고, 런타임에 타입에 대한 오류가 발생할 가능성이 커집니다.) (JavaScript, Python 등..)

     

    # Kotlin 프로젝트 빌드 과정

    kotlin도 jvm위에서 동작하기 때문에 java컴파일 과정과 유사합니다. 단, kotlin 파일은 kotlin compiler를 통해 바이트 코드로 변환되는데, 이 코드는 kotlin runtime library에 의존되어 실행합니다.

    kotlin runtime library에는 코틀린 자체 표준 라이브러리, JAVA API를 확장한 내용 등이 있습니다. 이는 Gradle, maven과 같은 빌드 도구가 Application으로 패키징할 때 알아서 kotlin runtime library를 포함시키게 됩니다.

     

    # Kotlin과 JAVA가 함께 있을 때 컴파일 과정

    1. kotlin 컴파일러가 kotlin코드를 컴파일해 바이트 코드 (.class)를 생성함, 이때 Kotlin코드가 참조하는 java코드도 함께 로딩되어 사용됩니다.
    2. java 컴파일러가 java코드를 컴파일해 바이트 코드 (.class)를 생성함. 이때 이미 컴파일된 코틀린의 바이트코드 (.class)의 경로를 class path에 추가해 컴파일합니다.

     

    * 이 때문에 코틀린에서 JAVA의 어노테이션 프로세서로 생성되는 코드를 사용하면 문제가 발생합니다!

    만약 기존의 JAVA에서 Lombok을 사용해 만드는 getter를 코틀린에서 호출한다면 컴파일에서 오류가 발생하게 됩니다. (코틀린 컴파일 시점에 getter가 정의되지 않아 오류 발생!) 위의 컴파일 순서를 보면 Lombok이라는 어노테이션 프로세서로 생성되는 코드는 2단계(이미 코틀린은 컴파일된 단계)에서 2번째에 getter가 생성되게 될 것입니다.

    컴파일 순서가 (코틀린 컴파일) -> (자바 컴파일, 어노테이션 프로세싱)이기 때문에 오류가 발생할 수 밖에 없습니다.

    (참고 : https://d2.naver.com/helloworld/6685007)

     

     

    # 어노테이션 프로세서?

    어노테이션은 자바 소스 코드에 추가되는 메타데이터 형태로, 컴파일러에게 코드 문법에 대한 오류를 체크하도록 정보를 제공하며, 새로운 코드를 생성해주기도 합니다.

    어노테이션 프로세서는 자바 컴파일러의 일종으로 어노테이션에 대한 코드 베이스를 검사, 수정, 생성하는 역할을 담당합니다. 커스텀 어노테이션 프로세서를 만들어 보일러 플레이트 코드를 줄일 수도 있으며, 이미 정말 많은 라이브러리에서 어노테이션 프로세서를 사용합니다.

    - 어노테이션 프로세서 처리 과정

     

    1. 자바 컴파일러가 컴파일 수행 (자바 컴파일러는 어노테이션 프로세서를 미리 알고 있어야함)

    2. 실행되지 않은 어노테이션 프로세서를 수행 (각각의 프로세서는 모두 각자 역할에 맞는 프로세서 구현이 되어 있어야함)

    3. 프로세서 내부에서 어노테이션이 달린 변수, 메소드, 클래스에 대한 처리를 하고, java 코드를 생성함

    4. 컴파일러가 모든 어노테이션이 수행됬는지 보고, 그렇지 않으면 위 작업을 반복

     

    (이 어노테이션 프로세서가 아래 코틀린의 단점과도 연관이 있어 함께 포스팅합니다.)

     

     

    # Kotlin VS JAVA (장점)

    1. 간결한 코드 : java에서 생기는 보일러 플레이트 코드를 상당히 많이 줄여줍니다.

    2. Kotlin-Extensions : 기존에 있는 클래스에 사용자의 필요에 따라 확장함수를 덧붙일 수 있습니다.  (실제 클래스를 확장하는 것이 아니라 .을 기반으로 정적 함수가 컴파일러에 의해 생성됩니다.)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    fun String.hello() : String {
        return "Hello, $this"
    }
    fun main(args: Array<String>) {
        val whom = "cwdoh"
        println(whom.hello())
    }
     
    public final class ExtensionsKt {
       @NotNull
       public static final String hello(@NotNull String $receiver) {
          Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
          return "Hello, " + $receiver;
       }
       public static final void main(@NotNull String[] args) {
          Intrinsics.checkParameterIsNotNull(args, "args");
          String whom = "cwdoh";
          String var2 = hello(whom);
          System.out.println(var2);
       }
    }
    cs

    3. 연산자 오버로딩 : 기본 연산자 (+, -, *, /등)을 클래스에 사용할 수 있게 정의할 수 있습니다.

    4. null 처리 : java에서 자주 발생하는 문제로 항상 해당 객체가 null인지 아닌지 체크하고 실행해야 합니다. (null pointer exception 발생) 코틀린은 기본적으로 ?가 없는 변수 타입에는 null을 할당할 수 없어 개발자의 실수를 예방해줍니다.

    5. 고차함수 : 함수를 파라미터로 담거나, 결과로 반환할 수 있습니다.

    6. Coroutine, flow (코루틴과 flow에 대한 자세한 내용은 추후 포스팅할 계획입니다.)

    (기타 등등 장점은 정말 많습니다. 자세한 내용은 다른 블로그에도 있으니 따로 적지 않습니다.)

    (참고 : https://d2.naver.com/helloworld/7543578)

     

    # Kotlin의 단점

    코틀린으로 어노테이션 프로세싱을 사용할 때 빌드속도가 느립니다.

    코틀린에서는 코틀린만을 위한 어노테이션 프로세서 제공하지 않습니다. 때문에 코틀린에서 어노테이션 프로세싱이 가능하게 하려면 KAPT(Kotlin Annotation Processing Tool)를 사용해야합니다. 이는 코틀린에서 JAVA의 어노테이션 프로세싱을 사용하는 것으로, java stub을 생성해야 하므로 컴파일링 속도가 느려지게 됩니다.

    하지만, 2021년 2월 구글에서 알파버전으로 공개한 KSP가 나와 코틀린 어노테이션 프로세싱에 대한 속도 문제는 개선되었습니다.

    KSP는 코틀린을 위한 어노테이션 프로세싱을 제공하며, 기존의 KAPT와 달리 JVM과 관련이 없어져서 stub생성으로 인해 속도가 느려지는 것을 해소할 수 있게 됩니다. (단, java를 아예 인지하지 못하기 때문에 java 어노테이션 프로세싱과 호환이 지원되지 않습니다.)

    이렇게 KSP는 분명 장점이 있지만 아직 한계점이 분명합니다.

    (참고 : https://soda1127.github.io/introduce-kotlin-symbol-processing/)

     

    댓글

Designed by Mhwan.