본문 바로가기
  • 개발하는 곰돌이
Programming Language/Kotlin & Java

[Kotlin] Java와 Kotlin, 그리고 Lombok

by 개발하는 곰돌이 2023. 4. 21.

목차

    Java에서의 Lombok

    자바에서는 기본적으로 클래스의 Getter, Setter, 생성자를 비롯한 기본적인 메소드나 Builder 패턴 등의 디자인 패턴을 개발자가 직접 작성하거나 IDE의 도움을 받아 작성해야 한다. 그런데 클래스에 새로운 필드를 추가하거나 기존 필드를 삭제하면 기존의 코드에도 영향을 주게 되어 굉장히 번거롭다.

     

    이러한 번거로움을 Lombok이 등장하면서 덜어주게 되었다. 추가하고자 하는 기능에 대한 어노테이션만 추가해 놓으면 코드를 작성할 땐 클래스의 필드에 변경이 일어나더라도 전혀 신경 쓰지 않고 사용할 수 있게 된 것이다. 이러한 편의성으로 인해 자바를 이용한 개발에서 Lombok은 뗄 수 없는 관계라고 볼 수도 있을 것이다.

    Kotlin과 Lombok

    코틀린은 클래스의 기본 생성자, 명명된 인자(Named Arguments), Data Class 등의 문법적 특징으로 인해 Lombok이 크게 필요한 것은 아니라고 볼 수 있다. 기본 생성자에 정의한 필드는 val이면 Getter가, var이면 Getter와 Setter가 포함된 프로퍼티가 되며, 명명된 인자를 통해 빌더 패턴의 효과를 누릴 수 있고, Data Class는 equals(), hashCode(), toString() 등의 기본적인 메소드가 모두 포함되어 있기 때문이다.

     

    그렇다고 코틀린에서 아예 Lombok을 사용할 수 없는 것은 아니다. Lombok의 어노테이션이 이것들만 있는 것이 아니고 Logger 객체 생성과 같은 역할을 해주는 어노테이션들이 편리하긴 하므로 Kotlin을 위한 Lombok이 개발 중에 있다. 다만, 아직 실험적인 단계이기 때문에 사용할 수는 있으나 자바의 Lombok에 비하면 아래와 같은 제한적인 기능만 제공한다.

    • @Getter, @Setter
    • @Builder
    • @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
    • @Data
    • @With
    • @Value

     

    코틀린에서 Lombok을 사용하기 위해선 Gradle 또는 Maven에 아래 코드를 추가해서 사용할 수 있다고 한다.

     

    Gradle(Kotlin)

    plugins {
        kotlin("plugin.lombok") version "{Kotlin버전}"
        id("io.freefair.lombok") version "5.3.0"
    }

     

    Gradle(Groovy)

    plugins {
        id 'org.jetbrains.kotlin.plugin.lombok' version '{Kotlin버전}'
        id 'io.freefair.lombok' version '5.3.0'
    }

     

    Maven

    <plugin>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-maven-plugin</artifactId>
        <version>{Kotlin버전}</version>
        <configuration>
            <compilerPlugins>
                <plugin>lombok</plugin>
            </compilerPlugins>
        </configuration>
        <dependencies>
            <dependency>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-lombok</artifactId>
                <version>{Kotlin버전}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.20</version>
                <scope>provided</scope>
            </dependency>
        </dependencies>
    </plugin>

    Java와 Kotlin을 함께 사용할 때 Lombok의 문제

    그런데 자바와 코틀린을 함께 사용할 때는 코틀린 쪽에서 자바의 Lombok을 인식하지 못한다. 이는 JVM의 빌드 과정과 Lombok의 동작 원리와 관련되어 있다.

     

    Lombok은 자바 코드가 자바 바이트 코드로 컴파일될 때 Lombok 어노테이션에 해당하는 기능의 자바 바이트 코드가 자동으로 생성되는 방식으로 동작한다. 이로 인해 자바 코드가 컴파일된 .class 파일을 디컴파일 해보면 Lombok 어노테이션은 모두 사라져 있고 각 어노테이션에 해당하는 코드가 작성되어 있는 것을 볼 수 있다.

    @Data 어노테이션을 사용한 DTO를 디컴파일해보면 해당하는 코드가 작성되어 있다.

    그런데 JVM은 코틀린 코드를 먼저 자바 바이트 코드로 컴파일하고, 그 이후에 자바 코드를 자바 바이트 코드로 컴파일한다. 즉, 자바와 코틀린이 함께 사용된 프로젝트를 빌드할 땐 아래와 같은 순서로 컴파일이 진행된다.

    1. .kt 파일을 .class 파일로 컴파일
    2. .java 파일을 .class 파일로 컴파일
      • Lombok 어노테이션을 프로세싱하여 관련 코드 생성
    3. 이후 런타임 과정 진행

    이러한 컴파일 순서로 인해 코틀린 코드가 자바 바이트 코드로 컴파일될 때는 자바 코드에서 각 Lombok 어노테이션에 해당하는 메소드나 기능이 존재하지 않는다. 이로 인해 컴파일 과정에서 코틀린 코드가 존재하지도 않는 메소드를 사용하려는 것으로 인식하기 때문에 컴파일 에러가 발생하게 된다.

    그럼 어떻게 해야 하지?

    근본적으로 자바와 코틀린을 함께 사용하면서 자바에서 Lombok을 사용하는 것은 불가능하다. 따라서 이 문제는 크게 두 가지 방법으로 해결 아닌 해결을 할 수 있다.

    Lombok 제거

    인텔리제이에 기본적으로 내장되어 있는 Lombok 플러그인에는 Delombok이라는 기능이 있다. Lombok 어노테이션을 사용 중인 클래스에서 RefactorDelombok에 들어가면 현재 클래스에 적용된 Lombok 어노테이션을 자바 코드로 즉시 변환할 수 있다.

    인텔리제이의 Delombok
    Delombok을 사용하여 @Data 어노테이션을 자바 코드로 변환할 수 있다.

    하지만 이 방법을 사용하면 Lombok 어노테이션이 제거되기 때문에 필드에 변경이 있을 때 개발자가 직접 코드를 변경해야 하고, 코드가 길어진다는 단점이 있다.

    Lombok이 적용된 Java 클래스를 Kotlin 클래스로 변환

    아니면 아예 Lombok이 적용된 자바 클래스를 코틀린 클래스로 변환하여 사용하는 방법도 있다. 이 경우에는 간결한 코드를 사용하면서 동시에 자바와 코틀린의 호환성을 유지할 수 있다는 장점이 있다. 위의 LoginDTO의 경우라면 아래와 같이 코틀린 클래스로 작성하는 것이다.

    data class LoginDTO(
        @Schema(example = "아이디")
        val userId: String,
        @Schema(example = "비밀번호")
        val userPw: String
    )

    이 방법도 단점이 없는 것은 아니다. @Getter, @Setter, @Data 등과 같은 어노테이션은 코틀린의 문법적 특징으로 손쉽게 작성이 가능하지만, @ToString, @EqualsAndHashCode만 필요한 경우에는 IDE를 통해 수동으로 작성해 줘야 한다. 또한 코틀린에서는 명명된 인자를 사용하여 빌더 패턴의 효과를 누릴 수 있지만, 자바에서 코틀린 클래스에 접근할 때는 명명된 인자를 사용할 수 없기 때문에 직접 빌더 패턴을 구현해줘야 한다는 문제도 있다.

    컴파일 순서 변경

    정말로 코틀린 코드에서 자바 코드에 작성된 Lombok을 사용하려면 자바 코드가 먼저 컴파일되도록 컴파일 순서를 엄격하게 정의해야 한다. 하지만 이 방법을 사용하면 코틀린 코드에서 자바 코드의 Lombok을 사용할 수는 있지만, 반대로 자바 코드에서 모든 코틀린 코드를 사용할 수 없다는 매우 큰 단점이 존재한다.

    결론

    결국 이런 문제를 피하려면 프로젝트에서 사용하는 언어를 자바 또는 코틀린 중 하나로 통일시키고, 자바 프로젝트를 코틀린으로 마이그레이션 할 때는 통째로 마이그레이션 하는 것이 가장 최선이 아닐까 생각한다. 현재의 개발자들이 원만히 합의를 봐서 두 언어를 혼용하여 개발했다고 하더라도, 후임 개발자가 이어받았을 때 문제가 발생할 수 있기 때문이다.

     

    코틀린이 자바와 100% 호환된다고는 하지만 자바를 기반으로 만들어진 다양한 기술들에 대해선 아직 완벽히 호환되지 않는 것 같다. 이 부분은 시간이 지나면서 해결되기를 바란다.

    참조 링크

     

    Kotlin doesn't see Java Lombok accessors?

    Using Kotlin 1.0.0 release (compiling in IntelliJ 15). println(myPojoInstance.foo) When it tries to compile code (in IntelliJ or Gradle) that references Lombok based POJOs it gives the error "Can...

    stackoverflow.com

     

    Lombok compiler plugin

    The Lombok compiler plugin is Experimental. It may be dropped or changed at any time. Use it only for evaluation purposes. We would appreciate your feedback on it in YouTrack.

    kotlinlang.org

     

    Kotlin 도입 과정에서 만난 문제와 해결 방법

    "Google I/O 2017 참관기 - Kotlin"에서 소개한 것처럼 Kotlin은 JVM은 물론 Android, JavaScript, 네이티브 영역 등 다양한 플랫폼에서 사용할 수 있는 정...

    d2.naver.com/helloworld

    댓글