Spring Batch와 Kotlin 통합 시 발생하는 CGLIB 프록시 생성 오류를 단계별로 분석하고
완벽하게 해결하는 방법을 해결하면서 정리하였습니다.

문제 상황 소개

Spring Batch와 Kafka를 연동하여 배치 처리 시스템을 구현하는 과정에서 CGLIB 프록시 생성 오류가 발생했습니다.

이 오류는 주로 Kotlin에서 클래스가 기본적으로 final로 선언되어 있어 발생합니다.

final 클래스는 CGLIB에 의해 프록시 객체로 변환될 수 없기 때문에 Spring이 프록시를 생성하는 데 문제가 발생합니다.

CGLIB 프록시 생성 오류 정의

에러 메시지 예시

org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.Jennam.api.irb.WebAppLogWriter]: Specified class is final

위와 같은 오류는 Spring Batch의 WebAppLogWriter 구현체를 사용하려 할 때 발생하게 되었습니다.

CGLIB는 동적으로 클래스를 서브클래싱하는 방식으로 프록시 객체를 생성하는데,
Kotlin의 기본 클래스는 final로 되어 있어 이를 확장할 수 없기 때문에 오류가 생성되었습니다.

에러 메시지 분석

Specified class is final 에러 메시지에서 확인할 수 있듯이,
CGLIB가 클래스를 확장하려고 했지만, Kotlin에서는 기본적으로 클래스가 final로 선언됩니다.

이를 해결하려면 해당 클래스를 open 키워드로 선언하여 확장이 가능하게 만들어야 합니다.

해결 방법

open 키워드 사용법

final 클래스를 해결하려면, open 키워드를 사용하여 해당 클래스를 열어야 합니다.

open은 클래스나 메소드가 상속될 수 있도록 허용하는 Kotlin 키워드입니다.

// 수정 전 (final 클래스)
class WebAppLogWriter : AppWriter<MobileLog> {
    override fun write(items: List<MobileLog>) {
        // Write logic
    }
}

// 수정 후 (open 클래스)
open class WebAppLogWriter : AppWriter<MobileLog> {
    override fun write(items: List<MobileLog>) {
        // Write logic
    }
}

WebAppLogWriter 클래스를 open으로 선언하면, Spring에서 이 클래스를 확장하여 프록시 객체를 생성할 수 있게 되었습니다.

kotlin-spring 플러그인 설정

kotlin-spring 플러그인은 Spring 프레임워크와 Kotlin을 통합할 때 필요한 설정입니다.
이를 통해 Spring의 AOP 기능 및 프록시 생성과 관련된 문제를 해결할 수 있습니다.

build.gradle.kts에 다음과 같은 플러그인 설정을 추가합니다.

plugins {
    kotlin("plugin.spring") version "1.8.0"
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-batch")
    implementation("org.springframework.kafka:spring-kafka")
}

이 플러그인을 추가하면 Spring에서 Kotlin 클래스를 프록시로 처리할 수 있도록 지원하게 됩니다.

코드 리팩토링 전략

Kotlin에서 final 클래스를 해결하는 방식은 open 키워드를 사용하는 것입니다.

하지만 이렇게 코드를 변경할 때는 WebAppLogWriter 같은 인터페이스를 구현하는 모든 클래스에서 open 키워드를 사용해야 하며, 이를 통해 Spring AOP와의 통합이 원활하게 이루어집니다.

상세 구현 가이드

WebLogItemWriter 수정

WebAppLogWriter 클래스를 open으로 선언해야 프록시 생성 문제가 해결됩니다.

open class WebLogItemWriter : AppWriter<WebLog> {
    override fun write(items: List<MobileLog>) {
        // 메시지를 Kafka로 전송하는 로직
    }
}

Gradle/Maven 설정 변경

kotlin-spring 플러그인 추가 및 의존성 설정을 통해 Spring과 Kotlin의 통합을 지원합니다.

이 설정을 통해 Spring에서 Kotlin 클래스를 프록시로 사용할 수 있습니다.

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-batch")
    implementation("org.springframework.kafka:spring-kafka")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
}

프록시 생성 확인 방법

프록시가 제대로 생성되었는지 확인하려면 Spring 애플리케이션에서 WebLogItemWriter 클래스가 올바르게 빈으로 등록되었는지 확인합니다.

@PostConstruct와 같은 초기화 메소드를 사용하여 로그를 찍어볼 수 있습니다.

@PostConstruct
fun init() {
    println("WebLogItemWriter initialized")
}

Spring Batch 프록시 메커니즘

Spring Batch에서의 프록시 메커니즘은 주로 AOP(Aspect-Oriented Programming)을 사용하여 트랜잭션 처리, 로그 기록, 성능 측정을 위해 사용됩니다.

CGLIB를 이용한 프록시 객체 생성이 필요하며, Kotlin 클래스는 기본적으로 final이라 이를 해결하려면 open 키워드를 사용해야 합니다.

Kotlin 클래스 특성

Kotlin에서 클래스가 기본적으로 final인 이유는, 불필요한 상속을 방지하고 불변성을 강조하기 위한 언어 설계입니다.

하지만 Spring과 같은 프레임워크에서 프록시를 생성할 때는 open으로 클래스를 열어야 합니다.

성능 및 설계 고려사항

Spring에서 프록시를 생성할 때 성능에 미치는 영향은 최소화하려면, 불필요한 프록시 생성을 피하고, 필요한 경우에만 프록시를 사용해야 합니다.

AOP가 과도하게 적용되면 성능 저하를 유발할 수 있습니다.

유사 문제 예방 방법

  • Kotlin 클래스를 설계할 때, Spring과의 통합을 고려하여 기본적으로 open 키워드를 사용하는 것이 좋습니다.

  • AOP가 필요한 경우, open 클래스와 kotlin-spring 플러그인을 함께 사용하는 것이 유효합니다.

코드 리뷰 시 주의사항

  • Kotlin에서 final 클래스에 대한 이해를 바탕으로 AOP 적용 여부를 검토합니다.

  • CGLIB 프록시 생성 오류가 발생하지 않도록 모든 클래스를 open으로 선언해야 할 필요성을 명확히 합니다.

모범 사례 공유

  • Kotlin과 Spring Batch를 통합할 때는 반드시 open 키워드를 사용하고, Spring 프록시 생성 문제를 사전에 예방하는 전략이 필요합니다.

  • kotlin-spring 플러그인 사용을 권장하여 프록시 생성을 자동으로 처리합니다.

profile
꾸준히, 의미있는 사이드 프로젝트 경험과 문제해결 과정을 기록하기 위한 공간입니다.

0개의 댓글