-
Notifications
You must be signed in to change notification settings - Fork 0
feature/33-kafka #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature/33-kafka #34
Changes from all commits
640fd30
34297ff
bc61ac3
b0e4cab
b2a419c
3aca7b3
1dcada7
0cd4320
996e4b6
f146848
b2797de
264a048
a89992b
9ab1874
d43a292
999fcad
21c5394
3e7c3e3
ca54f72
1b14e14
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,4 +22,10 @@ interface DeleteUserPort { | |||||||||||||||||||||||||
| * @return 삭제 대상 사용자 목록 | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| fun findWithdrawnUsersOlderThan(days: Long): List<User> | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * 모든 사용자를 삭제합니다. | ||||||||||||||||||||||||||
| * 관리자의 전체 데이터 초기화 시에만 사용됩니다. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| fun deleteAll() | ||||||||||||||||||||||||||
|
Comment on lines
+26
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 대량 삭제는 JPA deleteAll 대신 배치/DDL 기반으로 처리하세요. deleteAll은 엔티티 로딩/개별 삭제로 성능·락 부담이 큽니다. deleteAllInBatch/TRUNCATE(+ FK 안전성 검토)로 구현하고 반환값(삭제 건수)도 노출하는 편이 운영에 유리합니다. -interface DeleteUserPort {
+interface DeleteUserPort {
@@
- fun deleteAll()
+ fun deleteAll(): Long
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,69 @@ | ||||||||||||||||||||||||||||||||||
| package hs.kr.entrydsm.user.infrastructure.kafka.configuration | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| import org.apache.kafka.clients.consumer.ConsumerConfig | ||||||||||||||||||||||||||||||||||
| import org.apache.kafka.common.serialization.StringDeserializer | ||||||||||||||||||||||||||||||||||
| import org.springframework.context.annotation.Bean | ||||||||||||||||||||||||||||||||||
| import org.springframework.context.annotation.Configuration | ||||||||||||||||||||||||||||||||||
| import org.springframework.kafka.annotation.EnableKafka | ||||||||||||||||||||||||||||||||||
| import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory | ||||||||||||||||||||||||||||||||||
| import org.springframework.kafka.core.DefaultKafkaConsumerFactory | ||||||||||||||||||||||||||||||||||
| import org.springframework.kafka.support.serializer.JsonDeserializer | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * Kafka Consumer 설정을 담당하는 Configuration 클래스입니다. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * 원서 생성 이벤트 수신을 위한 Consumer 설정을 구성하며, | ||||||||||||||||||||||||||||||||||
| * Confluent Cloud 연결을 위한 보안 설정을 포함합니다. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * @property kafkaProperty Kafka 연결 정보를 담은 프로퍼티 | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| @EnableKafka | ||||||||||||||||||||||||||||||||||
| @Configuration | ||||||||||||||||||||||||||||||||||
| class KafkaConsumerConfig( | ||||||||||||||||||||||||||||||||||
| private val kafkaProperty: KafkaProperty | ||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * Kafka 리스너 컨테이너 팩토리를 생성합니다. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * 동시성 레벨을 2로 설정하여 병렬 메시지 처리를 지원하며, | ||||||||||||||||||||||||||||||||||
| * 폴링 타임아웃을 500ms로 설정하여 적절한 응답성을 보장합니다. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * @return 설정된 ConcurrentKafkaListenerContainerFactory 인스턴스 | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||||||||
| fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, String> { | ||||||||||||||||||||||||||||||||||
| return ConcurrentKafkaListenerContainerFactory<String, String>().apply { | ||||||||||||||||||||||||||||||||||
| setConcurrency(2) | ||||||||||||||||||||||||||||||||||
| consumerFactory = DefaultKafkaConsumerFactory(consumerFactoryConfig()) | ||||||||||||||||||||||||||||||||||
| containerProperties.pollTimeout = 500 | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+35
to
+40
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JsonDeserializer를 쓰는데 컨테이너 제네릭이 <String, String>인 불일치 — 런타임 캐스팅 이슈 위험 value를 JSON으로 역직렬화한다면 컨테이너/팩토리 제네릭을 <String, Any> 또는 구체 DTO로 맞추세요. 다음처럼 정렬하는 것을 권장합니다: - fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, String> {
- return ConcurrentKafkaListenerContainerFactory<String, String>().apply {
+ fun kafkaListenerContainerFactory(): ConcurrentKafkaListenerContainerFactory<String, Any> {
+ return ConcurrentKafkaListenerContainerFactory<String, Any>().apply {
setConcurrency(2)
- consumerFactory = DefaultKafkaConsumerFactory(consumerFactoryConfig())
+ consumerFactory = DefaultKafkaConsumerFactory<String, Any>(consumerFactoryConfig())
containerProperties.pollTimeout = 500
}
}필요 시 ack 모드도 명시적으로 설정해 주세요(예: RECORD/BATCH). +import org.springframework.kafka.listener.ContainerProperties
...
+ containerProperties.ackMode = ContainerProperties.AckMode.BATCH📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||
| * Kafka Consumer의 기본 설정을 구성합니다. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * Confluent Cloud 연결을 위한 SASL 보안 설정과 역직렬화 설정을 포함하며, | ||||||||||||||||||||||||||||||||||
| * read_committed 격리 레벨로 트랜잭션 안정성을 보장합니다. | ||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||
| * @return Consumer 설정 맵 | ||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||
| private fun consumerFactoryConfig(): Map<String, Any> { | ||||||||||||||||||||||||||||||||||
| return mapOf( | ||||||||||||||||||||||||||||||||||
| ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG to kafkaProperty.serverAddress, | ||||||||||||||||||||||||||||||||||
| ConsumerConfig.ISOLATION_LEVEL_CONFIG to "read_committed", | ||||||||||||||||||||||||||||||||||
| ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG to StringDeserializer::class.java, | ||||||||||||||||||||||||||||||||||
| ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG to "false", | ||||||||||||||||||||||||||||||||||
| ConsumerConfig.AUTO_OFFSET_RESET_CONFIG to "latest", | ||||||||||||||||||||||||||||||||||
| ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG to JsonDeserializer::class.java, | ||||||||||||||||||||||||||||||||||
| ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG to 5000, | ||||||||||||||||||||||||||||||||||
| JsonDeserializer.TRUSTED_PACKAGES to "*", | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+59
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion max.poll.interval.ms=5000(5초)은 과도하게 낮아 잦은 리밸런스 유발 가능 처리 시간이 5초만 넘어도 세션이 끊길 수 있습니다. 기본값(5분, 300000) 수준으로 상향하거나 프로퍼티로 외부화하세요. - ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG to 5000,
+ ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG to 300_000,🤖 Prompt for AI AgentsThere was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. JsonDeserializer TRUSTED_PACKAGES="*"는 보안 취약점 신뢰 패키지를 서비스 도메인으로 한정하세요. 필요 시 여러 패키지를 콤마로 나열. - JsonDeserializer.TRUSTED_PACKAGES to "*",
+ JsonDeserializer.TRUSTED_PACKAGES to "hs.kr.entrydsm.user.**,hs.kr.entrydsm.common.**",여러 이벤트 타입을 수신한다면 type headers를 유지하되, 기본 타입을 강제해야 할 경우: + JsonDeserializer.VALUE_DEFAULT_TYPE to "hs.kr.entrydsm.user.infrastructure.kafka.event.UserEvent",📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| "security.protocol" to "SASL_PLAINTEXT", | ||||||||||||||||||||||||||||||||||
| "sasl.mechanism" to "SCRAM-SHA-512", | ||||||||||||||||||||||||||||||||||
| "sasl.jaas.config" to | ||||||||||||||||||||||||||||||||||
| "org.apache.kafka.common.security.scram.ScramLoginModule required " + | ||||||||||||||||||||||||||||||||||
| "username=\"${kafkaProperty.confluentApiKey}\" " + | ||||||||||||||||||||||||||||||||||
| "password=\"${kafkaProperty.confluentApiSecret}\";" | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,105 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package hs.kr.entrydsm.user.infrastructure.kafka.configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.kafka.clients.producer.ProducerConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.apache.kafka.common.serialization.StringSerializer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.context.annotation.Bean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.context.annotation.Configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.kafka.core.DefaultKafkaProducerFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.kafka.core.KafkaTemplate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.kafka.support.serializer.JsonSerializer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Kafka Producer 설정을 담당하는 Configuration 클래스입니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 사용자 삭제, 전체 테이블 삭제, 사용자 이벤트 발행을 위한 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 각각의 KafkaTemplate과 ProducerFactory를 구성하며, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Confluent Cloud 연결을 위한 보안 설정을 포함합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @property kafkaProperty Kafka 연결 정보를 담은 프로퍼티 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| class KafkaProducerConfig( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private val kafkaProperty: KafkaProperty | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 전체 테이블 삭제 이벤트용 Producer Factory를 생성합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return Unit 타입 메시지용 DefaultKafkaProducerFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun deleteAllTableProducerFactory(): DefaultKafkaProducerFactory<String, Unit> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return DefaultKafkaProducerFactory(producerConfig()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 전체 테이블 삭제 이벤트 발행을 위한 KafkaTemplate을 생성합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return Unit 타입 메시지용 KafkaTemplate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun deleteAllTableKafkaTemplate(): KafkaTemplate<String, Unit> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return KafkaTemplate(deleteAllTableProducerFactory()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 사용자 삭제 이벤트용 Producer Factory를 생성합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return Long 타입 메시지용 DefaultKafkaProducerFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun deleteUserProducerFactory(): DefaultKafkaProducerFactory<String, Long> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return DefaultKafkaProducerFactory(producerConfig()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 사용자 삭제 이벤트 발행을 위한 KafkaTemplate을 생성합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return Long 타입 메시지용 KafkaTemplate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun deleteUserKafkaTemplate(): KafkaTemplate<String, Long> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return KafkaTemplate(deleteUserProducerFactory()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 사용자 이벤트용 Producer Factory를 생성합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return Any 타입 메시지용 DefaultKafkaProducerFactory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun userEventProducerFactory(): DefaultKafkaProducerFactory<String, Any> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return DefaultKafkaProducerFactory(producerConfig()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * 사용자 이벤트 발행을 위한 KafkaTemplate을 생성합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return Any 타입 메시지용 KafkaTemplate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Bean | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fun userEventKafkaTemplate(): KafkaTemplate<String, Any> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return KafkaTemplate(userEventProducerFactory()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Kafka Producer의 기본 설정을 구성합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Confluent Cloud 연결을 위한 SASL 보안 설정과 직렬화 설정을 포함합니다. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * @return Producer 설정 맵 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private fun producerConfig(): Map<String, Any> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return mapOf( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ProducerConfig.BOOTSTRAP_SERVERS_CONFIG to kafkaProperty.serverAddress, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG to StringSerializer::class.java, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG to JsonSerializer::class.java, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "security.protocol" to "SASL_PLAINTEXT", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "sasl.mechanism" to "SCRAM-SHA-512", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "sasl.jaas.config" to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "org.apache.kafka.common.security.scram.ScramLoginModule required " + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "username=\"${kafkaProperty.confluentApiKey}\" " + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "password=\"${kafkaProperty.confluentApiSecret}\";" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+97
to
+102
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chain보안 설정(프로토콜/메커니즘) 환경 불일치 가능 — TLS 기본화 권장 Confluent 환경이면 SASL_SSL/PLAIN 조합을, 자체 SCRAM 환경이어도 TLS 사용을 권장합니다. 현재 설정은 평문 전송입니다. - "security.protocol" to "SASL_PLAINTEXT",
- "sasl.mechanism" to "SCRAM-SHA-512",
- "sasl.jaas.config" to
- "org.apache.kafka.common.security.scram.ScramLoginModule required " +
+ "security.protocol" to "SASL_SSL",
+ "sasl.mechanism" to "PLAIN",
+ "sasl.jaas.config" to
+ "org.apache.kafka.common.security.plain.PlainLoginModule required " +
"username=\"${kafkaProperty.confluentApiKey}\" " +
"password=\"${kafkaProperty.confluentApiSecret}\";"환경에 맞는 truststore 설정도 점검하세요. 🏁 Script executed: #!/bin/bash
# KafkaProducerConfig.kt 파일 상단 컨텍스트 확인
sed -n '1,100p' casper-user/src/main/kotlin/hs/kr/entrydsm/user/infrastructure/kafka/configuration/KafkaProducerConfig.ktLength of output: 2319 KafkaProducerConfig.kt – 보안 설정을 TLS 기반 SASL_SSL/PLAIN으로 변경 - "security.protocol" to "SASL_PLAINTEXT",
- "sasl.mechanism" to "SCRAM-SHA-512",
- "sasl.jaas.config" to
- "org.apache.kafka.common.security.scram.ScramLoginModule required " +
+ "security.protocol" to "SASL_SSL",
+ "sasl.mechanism" to "PLAIN",
+ "sasl.jaas.config" to
+ "org.apache.kafka.common.security.plain.PlainLoginModule required " +
"username=\"${kafkaProperty.confluentApiKey}\" " +
"password=\"${kafkaProperty.confluentApiSecret}\";"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+92
to
+104
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion 프로듀서 내구성/정확성 기본값 강화 필요(acks/idempotence 등) 운영 안전성을 위해 최소한 acks=all, enable.idempotence=true를 권장합니다. 배치/압축도 기본값을 튜닝하세요. return mapOf(
ProducerConfig.BOOTSTRAP_SERVERS_CONFIG to kafkaProperty.serverAddress,
ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG to StringSerializer::class.java,
ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG to JsonSerializer::class.java,
+ ProducerConfig.ACKS_CONFIG to "all",
+ ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG to true,
+ ProducerConfig.RETRIES_CONFIG to Int.MAX_VALUE,
+ ProducerConfig.DELIVERY_TIMEOUT_MS_CONFIG to 120_000,
+ ProducerConfig.LINGER_MS_CONFIG to 10,
+ ProducerConfig.COMPRESSION_TYPE_CONFIG to "zstd",
"security.protocol" to "SASL_PLAINTEXT",
"sasl.mechanism" to "SCRAM-SHA-512",비고: COMPRESSION_TYPE은 인프라 표준에 맞춰 gzip/snappy/zstd 중 선택. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package hs.kr.entrydsm.user.infrastructure.kafka.configuration | ||
|
|
||
| import org.springframework.boot.context.properties.ConfigurationProperties | ||
| import org.springframework.boot.context.properties.ConfigurationPropertiesBinding | ||
|
|
||
| /** | ||
| * Kafka 연결을 위한 설정 프로퍼티 클래스입니다. | ||
| * | ||
| * application.yml의 kafka 섹션에서 설정값을 바인딩하여 | ||
| * Confluent Cloud Kafka 클러스터 연결에 필요한 정보를 관리합니다. | ||
| * | ||
| * @property serverAddress Kafka 브로커 서버 주소 | ||
| * @property confluentApiKey Confluent Cloud 접근을 위한 API 키 | ||
| * @property confluentApiSecret Confluent Cloud 접근을 위한 API 시크릿 | ||
| */ | ||
| @ConfigurationPropertiesBinding | ||
| @ConfigurationProperties("kafka") | ||
| class KafkaProperty( | ||
| val serverAddress: String, | ||
| val confluentApiKey: String, | ||
| val confluentApiSecret: String | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package hs.kr.entrydsm.user.infrastructure.kafka.configuration | ||
|
|
||
| /** | ||
| * Kafka 토픽명을 관리하는 상수 객체입니다. | ||
| * | ||
| * 마이크로서비스 간 이벤트 통신에 사용되는 토픽명들을 중앙에서 관리하여 | ||
| * 토픽명 변경 시 일관성을 보장합니다. | ||
| */ | ||
| object KafkaTopics { | ||
| /** | ||
| * 사용자 삭제 이벤트 토픽 | ||
| * 사용자가 탈퇴했을 때 다른 서비스에 알리기 위해 사용 | ||
| */ | ||
| const val DELETE_USER = "delete-user" | ||
|
|
||
| /** | ||
| * 전체 테이블 삭제 이벤트 토픽 | ||
| * 관리자가 전체 데이터를 초기화할 때 다른 서비스에 알리기 위해 사용 | ||
| */ | ||
| const val DELETE_ALL_TABLE = "delete-all-table" | ||
|
|
||
| /** | ||
| * 원서 생성 이벤트 토픽 | ||
| * 원서 서비스에서 원서가 생성되었을 때 사용자 접수번호 업데이트를 위해 사용 | ||
| */ | ||
| const val CREATE_APPLICATION = "create-application" | ||
|
|
||
| // Choreography 이벤트들 | ||
|
|
||
| /** | ||
| * 사용자 접수번호 업데이트 완료 이벤트 토픽 | ||
| * 사용자 서비스에서 접수번호 업데이트가 성공적으로 완료되었음을 알리는 이벤트 | ||
| */ | ||
| const val USER_RECEIPT_CODE_UPDATE_COMPLETED = "user-receipt-code-update-completed" | ||
|
|
||
| /** | ||
| * 사용자 접수번호 업데이트 실패 이벤트 토픽 | ||
| * 사용자 서비스에서 접수번호 업데이트가 실패했음을 알리는 이벤트 | ||
| * 원서 서비스에서 이 이벤트를 수신하면 보상 트랜잭션을 수행함 | ||
| */ | ||
| const val USER_RECEIPT_CODE_UPDATE_FAILED = "user-receipt-code-update-failed" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
spring-cloud-starter-config → spring-cloud-starter 변경은 오타 가능성이 큽니다
Config 클라이언트를 쓰려면
spring-cloud-starter-config가 맞습니다.spring-cloud-starter는 기능 제공용 스타터가 아니고, 버전 지정도spring-cloud-dependenciesBOM으로 관리하는 편이 안전합니다. 되돌리는 것을 권장합니다.📝 Committable suggestion
🤖 Prompt for AI Agents