From 34d4c4298ede35e5dbe8687323077711921b69c7 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 4 May 2026 12:47:00 +0900 Subject: [PATCH 01/13] =?UTF-8?q?Docs:=20Spring=20Kafka=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle b/build.gradle index d2b05bd..af3f677 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,8 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.kafka:spring-kafka' + testImplementation 'org.springframework.kafka:spring-kafka-test' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' From 773b1b4c0252a6aabe1f48ab778e4f2da66c547e Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 4 May 2026 14:37:11 +0900 Subject: [PATCH 02/13] =?UTF-8?q?Feat:=20=EB=8F=99=ED=99=94=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=9A=94=EC=B2=AD=EC=9D=B4=20=EB=93=A4=EC=96=B4?= =?UTF-8?q?=EC=98=AC=20=EB=95=8C,=20=ED=95=B4=EB=8B=B9=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EC=9D=84=20DTO=EB=A1=9C=20=EA=B4=80=EB=A6=AC=ED=95=A0?= =?UTF-8?q?=20=EC=88=98=20=EC=9E=88=EA=B2=8C=EB=81=94=20=EA=B0=9D=EC=B2=B4?= =?UTF-8?q?=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/dto/FairytaleGenerateReq.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java new file mode 100644 index 0000000..bdfaf66 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java @@ -0,0 +1,23 @@ +package com.capstone.kkumteul.domain.fairytale.web.dto; + +import com.capstone.kkumteul.domain.fairytale.entity.Background; +import com.capstone.kkumteul.domain.fairytale.entity.CharSpecies; +import com.capstone.kkumteul.domain.fairytale.entity.Morality; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class FairytaleGenerateReq { + + @NotNull(message = "배경은 비어있을 수 없습니다.") + private final Background background; + + @NotNull(message = "등장인물 종류는 비어있을 수 없습니다.") + private final CharSpecies charSpecie; + + @NotNull(message = "교훈은 비어있을 수 없습니다.") + private final Morality morality; + +} From fc45113a6468a4be9ae4375f43303c02e922ccba Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 4 May 2026 14:49:38 +0900 Subject: [PATCH 03/13] =?UTF-8?q?Feat:=20Kafka=20Producer=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95,=20key=20-=20String,=20value=20-=20Json=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=8C=8C=EC=8B=B1=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=ED=95=A8.=20=EB=98=90=ED=95=9C,=20KafkaTemplate=EB=A1=9C=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EB=B6=80=EB=B6=84=EC=9D=84=20Mes?= =?UTF-8?q?sageInterface=EB=A1=9C=20=EB=9F=B0=ED=83=80=EC=9E=84=EC=97=90?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=EC=9D=84=20=EC=B6=94=EB=A1=A0=ED=95=98?= =?UTF-8?q?=EA=B2=8C=20=ED=95=98=EC=97=AC=20=EB=8F=99=EC=A0=81=20=EB=8B=A4?= =?UTF-8?q?=ED=98=95=EC=84=B1=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/config/KafkaProducerConfig.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java diff --git a/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java b/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java new file mode 100644 index 0000000..f368cad --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java @@ -0,0 +1,47 @@ +package com.capstone.kkumteul.global.config; + +import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; +import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.common.serialization.StringSerializer; +import org.springframework.beans.factory.annotation.Value; +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.core.ProducerFactory; +import org.springframework.kafka.support.serializer.JsonSerializer; + +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class KafkaProducerConfig { + + private final String KAFKA_URL; + + public KafkaProducerConfig( + @Value("${KAFKA_URL}") String KAFKA_URL + ) { this.KAFKA_URL = KAFKA_URL; } + + @Bean + + public ProducerFactory producerFactory() { + + Map config = new HashMap<>(); + + // key - String Serialization + // value - Json Serialization + config.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KAFKA_URL); + config.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); + config.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class); + config.put(JsonSerializer.ADD_TYPE_INFO_HEADERS, false); + + return new DefaultKafkaProducerFactory<>(config); + } + + @Bean + public KafkaTemplate kafkaTemplate() { + return new KafkaTemplate<>(producerFactory()); + } +} From 0c56107031d40ee9b7d107cf13b1aac2368208a6 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 4 May 2026 14:50:51 +0900 Subject: [PATCH 04/13] =?UTF-8?q?Feat:=20KafkaTemplate=EC=9D=98=20?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20?= =?UTF-8?q?=EB=8F=99=EC=A0=81=20=EB=8B=A4=ED=98=95=EC=84=B1=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A4=EA=B3=84=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20MessageInterface=20=EC=9D=B8=ED=84=B0=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EA=B0=9D=EC=B2=B4=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkumteul/domain/kafka/dto/MessageInterface.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/kafka/dto/MessageInterface.java diff --git a/src/main/java/com/capstone/kkumteul/domain/kafka/dto/MessageInterface.java b/src/main/java/com/capstone/kkumteul/domain/kafka/dto/MessageInterface.java new file mode 100644 index 0000000..fd733ce --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/kafka/dto/MessageInterface.java @@ -0,0 +1,6 @@ +package com.capstone.kkumteul.domain.kafka.dto; + +public interface MessageInterface { + + Long getUserId(); +} From 838f4ba5a6322312130939b736ba89fbe9cb53c9 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 4 May 2026 14:52:27 +0900 Subject: [PATCH 05/13] =?UTF-8?q?Feat:=20=EB=8F=99=ED=99=94=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=9A=94=EC=B2=AD=EC=9D=84=20=EB=A1=9C=EC=BB=AC=20?= =?UTF-8?q?FastAPI=EB=A1=9C=20=EC=A0=84=ED=8C=8C=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=ED=95=84=EC=9A=94=ED=95=9C=20=EA=B0=92?= =?UTF-8?q?=EC=9D=84=20=EB=A9=94=EC=84=B8=EC=A7=80=EB=A1=9C=20=EC=A0=84?= =?UTF-8?q?=ED=8C=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=EA=B0=9D=EC=B2=B4=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kafka/dto/FairytaleGenerateMessage.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/kafka/dto/FairytaleGenerateMessage.java diff --git a/src/main/java/com/capstone/kkumteul/domain/kafka/dto/FairytaleGenerateMessage.java b/src/main/java/com/capstone/kkumteul/domain/kafka/dto/FairytaleGenerateMessage.java new file mode 100644 index 0000000..f0488af --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/kafka/dto/FairytaleGenerateMessage.java @@ -0,0 +1,20 @@ +package com.capstone.kkumteul.domain.kafka.dto; + +import com.capstone.kkumteul.domain.fairytale.entity.Background; +import com.capstone.kkumteul.domain.fairytale.entity.CharSpecies; +import com.capstone.kkumteul.domain.fairytale.entity.Morality; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class FairytaleGenerateMessage implements MessageInterface { + + private final Long userId; + + private final Background background; + private final CharSpecies charSpecies; + private final Morality morality; +} From 1fcde037857995ed945ac5992e6bb0f16adba001 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 4 May 2026 14:54:22 +0900 Subject: [PATCH 06/13] =?UTF-8?q?Feat:=20Kafka=EC=97=90=20=EB=A9=94?= =?UTF-8?q?=EC=84=B8=EC=A7=80=EB=A5=BC=20=EB=93=B1=EB=A1=9D=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=ED=95=98=EB=8A=94=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EA=B0=9D=EC=B2=B4=EC=99=80=20=EB=8F=99=ED=99=94=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=EB=A5=BC=20?= =?UTF-8?q?=EC=A0=84=ED=8C=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/kafka/service/EventService.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java diff --git a/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java new file mode 100644 index 0000000..58e55c6 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java @@ -0,0 +1,33 @@ +package com.capstone.kkumteul.domain.kafka.service; + +import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleGenerateReq; +import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.stereotype.Service; + +/* 동화 생성 이벤트 전파 */ + +@Service +@Slf4j(topic = "event") +@RequiredArgsConstructor +public class EventService { + + private final KafkaTemplate kafkaTemplate; + + public void commentMessageCreate(Long userId, FairytaleGenerateReq request) { + + FairytaleGenerateMessage message = FairytaleGenerateMessage.builder() + .userId(userId) + .background(request.getBackground()) + .charSpecies(request.getCharSpecie()) + .morality(request.getMorality()) + .build(); + + log.info("fairytale_generate userId={}, message={}", userId, message); + + kafkaTemplate.send("fairytale_generate", message); + + } +} From df6059cb4b56a73f5ed380f840270f078fa1ca25 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Wed, 6 May 2026 19:41:05 +0900 Subject: [PATCH 07/13] =?UTF-8?q?Docs:=20Kafka=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=B4=20.env=EC=97=90=20Kafka?= =?UTF-8?q?=20=EC=8B=A4=EC=A0=9C=20=ED=86=B5=EC=8B=A0=20=EC=84=9C=EB=B2=84?= =?UTF-8?q?=20=EC=A3=BC=EC=86=8C=20=EA=B8=B0=EB=A1=9D=20=EB=B0=8F=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20.env=EB=A5=BC=20=EC=A7=81=EC=A0=91=20?= =?UTF-8?q?=EC=9D=BD=EA=B2=8C=ED=95=98=EC=97=AC=20Kafka=20=ED=86=B5?= =?UTF-8?q?=EC=8B=A0=20=EC=A3=BC=EC=86=8C=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 ++ .../kkumteul/kafka/KafkaProducerTest.java | 51 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java diff --git a/build.gradle b/build.gradle index af3f677..292218c 100644 --- a/build.gradle +++ b/build.gradle @@ -42,6 +42,9 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3' + + // for Kafka test + testImplementation 'me.paulschwarz:spring-dotenv:4.0.0' } tasks.named('test') { diff --git a/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java b/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java new file mode 100644 index 0000000..deef991 --- /dev/null +++ b/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java @@ -0,0 +1,51 @@ +package com.capstone.kkumteul.kafka; + +import com.capstone.kkumteul.domain.fairytale.entity.Background; +import com.capstone.kkumteul.domain.fairytale.entity.CharSpecies; +import com.capstone.kkumteul.domain.fairytale.entity.Morality; +import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; +import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; +import com.capstone.kkumteul.global.config.KafkaProducerConfig; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.kafka.support.SendResult; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest( + classes = KafkaProducerConfig.class, + properties = "KAFKA_URL=52.78.205.133:9092" +) +class KafkaProducerTest { + + @Autowired + private KafkaTemplate kafkaTemplate; + + @Test + void messagePublicationTest() throws Exception { + + FairytaleGenerateMessage message = FairytaleGenerateMessage.builder() + .userId(1L) + .background(Background.FOREST_NATURE) + .charSpecies(CharSpecies.ANIMAL) + .morality(Morality.KINDNESS_REWARDED) + .build(); + + CompletableFuture> future = + kafkaTemplate.send("fairytale_generate", message); + + SendResult result = future.get(10, TimeUnit.SECONDS); + + assertThat(result.getRecordMetadata().topic()).isEqualTo("fairytale_generate"); + assertThat(result.getRecordMetadata().offset()).isGreaterThanOrEqualTo(0); + + System.out.println("토픽: " + result.getRecordMetadata().topic()); + System.out.println("파티션: " + result.getRecordMetadata().partition()); + System.out.println("오프셋: " + result.getRecordMetadata().offset()); + } +} From d228a432925b6efecb20948bc403f5fcf20d900a Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Wed, 6 May 2026 19:41:23 +0900 Subject: [PATCH 08/13] =?UTF-8?q?Test:=20Kafka=20=ED=86=B5=EC=8B=A0=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4,=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/capstone/kkumteul/kafka/KafkaProducerTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java b/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java index deef991..1611b9e 100644 --- a/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java +++ b/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java @@ -18,8 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; @SpringBootTest( - classes = KafkaProducerConfig.class, - properties = "KAFKA_URL=52.78.205.133:9092" + classes = KafkaProducerConfig.class ) class KafkaProducerTest { From 383bcc3c687a0882055b0762e45092b4d74dc97c Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Wed, 6 May 2026 19:43:03 +0900 Subject: [PATCH 09/13] =?UTF-8?q?Fix:=20kakfa=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=A0=9C=EB=84=A4=EB=A6=AD=EC=9D=84=20=EC=84=A0?= =?UTF-8?q?=EC=96=B8=ED=95=9C=20Bean=20=ED=83=80=EC=9E=85=EA=B3=BC=20?= =?UTF-8?q?=EC=9D=BC=EC=B9=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/kkumteul/domain/kafka/service/EventService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java index 58e55c6..3891864 100644 --- a/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java +++ b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java @@ -2,6 +2,7 @@ import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleGenerateReq; import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; +import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.kafka.core.KafkaTemplate; @@ -14,7 +15,7 @@ @RequiredArgsConstructor public class EventService { - private final KafkaTemplate kafkaTemplate; + private final KafkaTemplate kafkaTemplate; public void commentMessageCreate(Long userId, FairytaleGenerateReq request) { From 2a477d3503c2800d31c124b75e26929efed3d46f Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Thu, 7 May 2026 18:50:40 +0900 Subject: [PATCH 10/13] =?UTF-8?q?Docs:=20Kafka=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=B4=20.env=20=EB=82=B4?= =?UTF-8?q?=EC=9D=98=20Kafka=20=EA=B2=BD=EB=A1=9C=EB=A5=BC=20=EC=A7=81?= =?UTF-8?q?=EC=A0=91=20=EC=A3=BC=EC=9E=85=EB=B0=9B=EA=B2=8C=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=EB=A5=BC=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 292218c..053c33c 100644 --- a/build.gradle +++ b/build.gradle @@ -43,8 +43,8 @@ dependencies { runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3' - // for Kafka test - testImplementation 'me.paulschwarz:spring-dotenv:4.0.0' + // .env 파일 로딩 + implementation 'me.paulschwarz:spring-dotenv:4.0.0' } tasks.named('test') { From 5002fd1408506c98b88a4bcd77544f86a4ecbe70 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Thu, 7 May 2026 18:51:15 +0900 Subject: [PATCH 11/13] =?UTF-8?q?Fix:=20DI=EB=B0=9B=EB=8D=98=20kafkaTempla?= =?UTF-8?q?te=EC=9D=98=20=EB=A7=A4=EA=B0=9C=EB=B3=80=EC=88=98=EB=A5=BC=20F?= =?UTF-8?q?airytaleGenerateReq=EC=97=90=EC=84=9C=20MessageInterface?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/kkumteul/domain/kafka/service/EventService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java index 3891864..cc8c3b4 100644 --- a/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java +++ b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java @@ -17,7 +17,7 @@ public class EventService { private final KafkaTemplate kafkaTemplate; - public void commentMessageCreate(Long userId, FairytaleGenerateReq request) { + public void createFairytaleMessageSend(Long userId, FairytaleGenerateReq request) { FairytaleGenerateMessage message = FairytaleGenerateMessage.builder() .userId(userId) From 8bab1403db09f2a132634f2cba6fa78482085aff Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Thu, 7 May 2026 18:51:38 +0900 Subject: [PATCH 12/13] =?UTF-8?q?Feat:=20=EB=8F=99=ED=99=94=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=9A=94=EC=B2=AD=EC=9D=B4=20=EB=93=A4=EC=96=B4?= =?UTF-8?q?=EC=98=AC=20=EC=8B=9C,=20Kafka=EC=97=90=20=EB=8F=99=ED=99=94=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EB=A9=94=EC=84=B8=EC=A7=80=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=ED=95=98=EB=8A=94=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=A9=94=EC=86=8C=EB=93=9C=EB=A5=BC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/controller/FairytaleController.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java index 8780372..acc463a 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/controller/FairytaleController.java @@ -3,10 +3,13 @@ import com.capstone.kkumteul.domain.fairytale.entity.Island; import com.capstone.kkumteul.domain.fairytale.service.FairytaleService; import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleDetailRes; +import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleGenerateReq; import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleListRes; +import com.capstone.kkumteul.domain.kafka.service.EventService; import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.global.response.SuccessResponse; import com.capstone.kkumteul.global.security.AuthUser; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -21,6 +24,19 @@ public class FairytaleController { private final FairytaleService fairytaleService; + private final EventService eventService; + + @PostMapping + public ResponseEntity> createFairytale( + @AuthUser User user, + @Valid @RequestBody FairytaleGenerateReq request + ) { + eventService.createFairytaleMessageSend(user.getId(), request); + + return ResponseEntity + .status(HttpStatus.CREATED) + .body(SuccessResponse.empty()); + } @GetMapping("/my") public ResponseEntity>> getMyFairytales( From 603562eb89b9f6563506a3c2014d22490c3f7bbf Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Thu, 7 May 2026 19:00:53 +0900 Subject: [PATCH 13/13] =?UTF-8?q?Docs:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20import=EB=AC=B8=20=EC=B5=9C?= =?UTF-8?q?=EC=A0=81=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/exception/FairytaleErrorCode.java | 2 +- .../fairytale/web/dto/FairytaleGenerateReq.java | 2 +- .../kkumteul/domain/game/entity/NodeCategory.java | 3 +-- .../kkumteul/domain/game/service/GameServiceImpl.java | 6 +----- .../kkumteul/domain/kafka/service/EventService.java | 11 ++++++++++- .../kkumteul/global/config/KafkaProducerConfig.java | 1 - .../global/exception/GlobalExceptionHandler.java | 2 +- .../capstone/kkumteul/kafka/KafkaProducerTest.java | 8 +++++--- 8 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/exception/FairytaleErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/exception/FairytaleErrorCode.java index ad82647..4c3abb1 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/exception/FairytaleErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/exception/FairytaleErrorCode.java @@ -4,7 +4,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; -import static com.capstone.kkumteul.global.constant.StaticValue.*; +import static com.capstone.kkumteul.global.constant.StaticValue.NOT_FOUND; @Getter @AllArgsConstructor diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java index bdfaf66..386732a 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleGenerateReq.java @@ -15,7 +15,7 @@ public class FairytaleGenerateReq { private final Background background; @NotNull(message = "등장인물 종류는 비어있을 수 없습니다.") - private final CharSpecies charSpecie; + private final CharSpecies charSpecies; @NotNull(message = "교훈은 비어있을 수 없습니다.") private final Morality morality; diff --git a/src/main/java/com/capstone/kkumteul/domain/game/entity/NodeCategory.java b/src/main/java/com/capstone/kkumteul/domain/game/entity/NodeCategory.java index cd29c58..3cf97a6 100644 --- a/src/main/java/com/capstone/kkumteul/domain/game/entity/NodeCategory.java +++ b/src/main/java/com/capstone/kkumteul/domain/game/entity/NodeCategory.java @@ -1,10 +1,9 @@ package com.capstone.kkumteul.domain.game.entity; +import com.capstone.kkumteul.domain.game.exception.InvalidCategoryException; import lombok.AllArgsConstructor; import lombok.Getter; -import com.capstone.kkumteul.domain.game.exception.InvalidCategoryException; - import java.util.Arrays; @Getter diff --git a/src/main/java/com/capstone/kkumteul/domain/game/service/GameServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/game/service/GameServiceImpl.java index 68fe4c7..6ea1cb9 100644 --- a/src/main/java/com/capstone/kkumteul/domain/game/service/GameServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/game/service/GameServiceImpl.java @@ -1,12 +1,8 @@ package com.capstone.kkumteul.domain.game.service; import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; -import com.capstone.kkumteul.domain.game.entity.GraphEdge; -import com.capstone.kkumteul.domain.game.entity.GraphNode; -import com.capstone.kkumteul.domain.game.entity.NodeCategory; -import com.capstone.kkumteul.domain.game.entity.EdgeChoice; -import com.capstone.kkumteul.domain.game.entity.GameResult; import com.capstone.kkumteul.domain.fairytale.exception.FairytaleNotFoundException; +import com.capstone.kkumteul.domain.game.entity.*; import com.capstone.kkumteul.domain.game.exception.*; import com.capstone.kkumteul.domain.game.repository.EdgeChoiceRepository; import com.capstone.kkumteul.domain.game.repository.GameResultRepository; diff --git a/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java index cc8c3b4..18d4aeb 100644 --- a/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java +++ b/src/main/java/com/capstone/kkumteul/domain/kafka/service/EventService.java @@ -5,6 +5,7 @@ import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.stereotype.Service; @@ -17,6 +18,9 @@ public class EventService { private final KafkaTemplate kafkaTemplate; + @Value("${FAIRYTALE_GENERATION}") + private String FAIRYTALE_GENERATION; + public void createFairytaleMessageSend(Long userId, FairytaleGenerateReq request) { FairytaleGenerateMessage message = FairytaleGenerateMessage.builder() @@ -28,7 +32,12 @@ public void createFairytaleMessageSend(Long userId, FairytaleGenerateReq request log.info("fairytale_generate userId={}, message={}", userId, message); - kafkaTemplate.send("fairytale_generate", message); + kafkaTemplate.send(FAIRYTALE_GENERATION, message) + .whenComplete((result, e) -> { + if (e != null) { + log.error("fairytale_generate failed", e); + } + }); } } diff --git a/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java b/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java index f368cad..f04bf37 100644 --- a/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java +++ b/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java @@ -1,6 +1,5 @@ package com.capstone.kkumteul.global.config; -import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; import org.apache.kafka.clients.producer.ProducerConfig; import org.apache.kafka.common.serialization.StringSerializer; diff --git a/src/main/java/com/capstone/kkumteul/global/exception/GlobalExceptionHandler.java b/src/main/java/com/capstone/kkumteul/global/exception/GlobalExceptionHandler.java index d35e58e..477184d 100644 --- a/src/main/java/com/capstone/kkumteul/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/capstone/kkumteul/global/exception/GlobalExceptionHandler.java @@ -8,10 +8,10 @@ import org.springframework.validation.BindException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; -import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.resource.NoResourceFoundException; diff --git a/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java b/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java index 1611b9e..9578cc9 100644 --- a/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java +++ b/src/test/java/com/capstone/kkumteul/kafka/KafkaProducerTest.java @@ -6,6 +6,7 @@ import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; import com.capstone.kkumteul.global.config.KafkaProducerConfig; +import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; @@ -17,6 +18,7 @@ import static org.assertj.core.api.Assertions.assertThat; +@Slf4j @SpringBootTest( classes = KafkaProducerConfig.class ) @@ -43,8 +45,8 @@ void messagePublicationTest() throws Exception { assertThat(result.getRecordMetadata().topic()).isEqualTo("fairytale_generate"); assertThat(result.getRecordMetadata().offset()).isGreaterThanOrEqualTo(0); - System.out.println("토픽: " + result.getRecordMetadata().topic()); - System.out.println("파티션: " + result.getRecordMetadata().partition()); - System.out.println("오프셋: " + result.getRecordMetadata().offset()); + log.info("토픽: {}", result.getRecordMetadata().topic()); + log.info("파티션: {}", result.getRecordMetadata().partition()); + log.info("오프셋: {}", result.getRecordMetadata().offset()); } }