에러의 발생
장장 3일째 이 에러를 해결하지 못하고 있다. 해당 에러는 kafka 를 연결하기 위한 설정파일은 작성한 뒤, MemberApplication의 기능 추가를 위한 테스트를 진행하면서 식별했다. 그리고 원인은 kafka 설정 파일이 문제라는 것까지 원인을 파악했다.
분석
테스트를 실행하니, initializationError 가 발생했다. 에러의 내용은 다음과 같았다.
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
...
ApplicationContext 로드에 실패했고, 계속해서 에러를 확인했다.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'kafkaProducerImpl' defined in file [/Users/sy/git_projects/shoes-ordering-system/build/classes/java/main/com/shoes/ordering/system/common/kafka/producer/service/KafkaProducerImpl.class]: Unsatisfied dependency expressed through constructor parameter 0;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'kafkaTemplate' defined in class path resource [com/shoes/ordering/system/common/kafka/producer/KafkaProducerConfig.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.kafka.core.KafkaTemplate]: Factory method 'kafkaTemplate' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'producerFactory' defined in class path resource [com/shoes/ordering/system/common/kafka/producer/KafkaProducerConfig.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.kafka.core.ProducerFactory]: Factory method 'producerFactory' threw exception;
nested exception is org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'producerConfig' defined in class path resource [com/shoes/ordering/system/common/kafka/producer/KafkaProducerConfig.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [java.util.Map]: Factory method 'producerConfig' threw exception;
nested exception is java.lang.NullPointerException:
Cannot invoke "java.lang.Integer.intValue()"
because the return value of "com.shoes.ordering.system.common.kafka.config.KafkaProducerConfigData.getBatchSize()" is null
에러가 길어 줄 바꿈으로 에러를 나눠보았다. 요약을 하자면 'kafkaProducerImpl' 에서 Bean에 종속성 문제가 발생함을 의미한다.
계속해서 체이닝을 통해 근본원인을 따라가보면, KafkaProducerConfig 클래스에 정의된 producerFactory Bean의 예외로 인해 kafkaTemplate Bean 생성에 실패했다. producerFactory Bean 인스턴스화는 producerConfig Bean 생성에 실패하기 때문에 예외를 던진것이다. 동일한 KafkaProducerConfig 클래스에 정의된 producerConfig Bean은 producerConfig 메서드에 NullPointerException이 발생했기 때문에 예외를 발생시켰다. 그리고 이 NullPointerException은 KafkaProducerConfigData 클래스에서 getBatchSize()의 반환 값에 대해 intValue() 메서드를 호출할 때 발생했다.
먼저 KafkaProducerConfig 클래스 코드를 보면 다음과 같다.
@Configuration
public class KafkaProducerConfig<K extends Serializable, V extends SpecificRecordBase> {
private final KafkaConfigData kafkaConfigData;
private final KafkaProducerConfigData kafkaProducerConfigData;
public KafkaProducerConfig(KafkaConfigData kafkaConfigData,
KafkaProducerConfigData kafkaProducerConfigData) {
this.kafkaConfigData = kafkaConfigData;
this.kafkaProducerConfigData = kafkaProducerConfigData;
}
@Bean
public Map<String, Object> producerConfig() {
Map<String, Object> props = new HashMap<>();
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaConfigData.getBootstrapServers());
props.put(kafkaConfigData.getSchemaRegistryUrlKey(), kafkaConfigData.getSchemaRegistryUrl());
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, kafkaProducerConfigData.getKeySerializerClass());
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, kafkaProducerConfigData.getValueSerializerClass());
props.put(ProducerConfig.BATCH_SIZE_CONFIG, kafkaProducerConfigData.getBatchSize() *
kafkaProducerConfigData.getBatchSizeBoostFactor());
props.put(ProducerConfig.LINGER_MS_CONFIG, kafkaProducerConfigData.getLingerMs());
props.put(ProducerConfig.COMPRESSION_TYPE_CONFIG, kafkaProducerConfigData.getCompressionType());
props.put(ProducerConfig.ACKS_CONFIG, kafkaProducerConfigData.getAcks());
props.put(ProducerConfig.REQUEST_TIMEOUT_MS_CONFIG, kafkaProducerConfigData.getRequestTimeoutMs());
props.put(ProducerConfig.RETRIES_CONFIG, kafkaProducerConfigData.getRetryCount());
return props;
}
@Bean
public ProducerFactory<K, V> producerFactory() {
return new DefaultKafkaProducerFactory<>(producerConfig());
}
// 에러가 발생한 부분
@Bean
public KafkaTemplate<K, V> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}
에러가 발생한 부분은 kafkaTemplate() 를 통해 KafkaTemplate 을 생성하는데 producerFactory() 를 호출하며, producerFactory메서드는 producerConfig()를 호출한다.그리고 여기서 문제가 발생했다.
그리고 바로 kafkaProducerConfigData(KafkaProducerConfigData 클래스) 를 확인해보기 전, 예상해볼 수 있는 해당 에러의 원인 후보로 클래스가 컴포넌트 스캔에 포함되지 않는다.(어노테이션이 문제) 를 예상했다.
@Data
@Configuration
@ConfigurationProperties(prefix = "kafka-producer-config")
public class KafkaProducerConfigData {
private String keySerializerClass;
private String valueSerializerClass;
private String compressionType;
private String acks;
private Integer batchSize;
private Integer batchSizeBoostFactor;
private Integer lingerMs;
private Integer requestTimeoutMs;
private Integer retryCount;
}
하지만 어노테이션은 잘 되어 있었다. @Configuration 을 통해 나는 Bean 객체로 등록했다. 그리고 @ConfigurationProperties 의 prefix 를 선언하고, application.yml 을 통해 값을 지정했다. 하지만 에러가 발생했다. 그렇다면 내가 생각해볼 수 있는 에러의 원인으로는 1. application.yml에 작성된 값이 잘못되었다. 2. gradle.build에 종속성이 빠졌다. 로 추려볼 수 있었다.
application.yml 에는 문제가 없었다. 오타가 있을까 확인도 해봤지만, 오타는 없었다. 다음 gradle.build 파일을 확인했다.
여기까지 와선 이제 정말 혼돈의 카오스 그 자체였다. 그럼 생각해볼 수 있는 것은.. 왜 null 을 뱉어내는가?(왜 application.yml 를 읽어오지 못하는가?) 이다. 이유가 뭘까.. 무엇이 문제인 것일까... 왜 null을 뱉어내는 것일까...? 해당 문제로 3일동안 나아가질 못하고있다... 흑흑 ㅠㅠ
'회고 > TIL' 카테고리의 다른 글
작성하자.. 문서.. 기록하자.. 문서.. (0) | 2023.06.21 |
---|---|
Member 도메인 완료, 느낀점 (0) | 2023.06.20 |
오늘도 여전한 에러 해결을 위한 노력과 무시하고 개발하는 나, 이벤트기반 마이크로서비스 구축 (0) | 2023.06.15 |
kafka 에러... 하루종일.... (0) | 2023.06.14 |
코틀린 공부, 이벤트기반 마이크로서비스 구축, gradle-avro-pluginPublic 문제 해결사용하기 (0) | 2023.06.12 |