From a800a7d7950d382894469d63ee79f14883a59803 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 11 May 2026 22:13:57 +0900 Subject: [PATCH 01/48] =?UTF-8?q?Docs:=20Spring=EC=97=90=EC=84=9C=20S3?= =?UTF-8?q?=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20build.gradle=EC=97=90=20=EC=9D=98=EC=A1=B4=EC=84=B1?= =?UTF-8?q?=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 8a2b888..e0ebff3 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,9 @@ dependencies { // .env 파일 로딩 implementation 'me.paulschwarz:spring-dotenv:4.0.0' + + // S3 + implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.4.2' } tasks.named('test') { From 7151c139b872be8160d11874b6be88fd225f416b Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Mon, 11 May 2026 22:27:57 +0900 Subject: [PATCH 02/48] =?UTF-8?q?Docs:=20org.springframework...=EC=9D=98?= =?UTF-8?q?=20S3=20=EC=9D=98=EC=A1=B4=EC=84=B1=EC=9D=80=20=EB=A0=88?= =?UTF-8?q?=EA=B1=B0=EC=8B=9C=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=EB=9F=AC?= =?UTF-8?q?=EB=A6=AC=EC=9E=84=EC=9D=84=20=EC=A3=BC=EC=84=9D=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=84=EB=8B=A8=ED=9E=88=20=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index e0ebff3..0557c30 100644 --- a/build.gradle +++ b/build.gradle @@ -55,7 +55,7 @@ dependencies { // .env 파일 로딩 implementation 'me.paulschwarz:spring-dotenv:4.0.0' - // S3 + // S3 (latest dependency) implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.4.2' } From 73946d343a03a22c72ac709f4f4c2d04685cb4fb Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 10:10:23 +0900 Subject: [PATCH 03/48] =?UTF-8?q?Feat:=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=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EB=82=B4=EC=97=90=20User=EB=A5=BC=20Secur?= =?UTF-8?q?ity=20Context=20Holder=EC=97=90=EC=84=9C=20=EC=A3=BC=EC=9E=85,?= =?UTF-8?q?=20wavFile=EC=9D=84=20=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice/web/controller/VoiceController.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java new file mode 100644 index 0000000..0c30412 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java @@ -0,0 +1,38 @@ +package com.capstone.kkumteul.domain.fairytale.voice.web.controller; + +import com.capstone.kkumteul.domain.fairytale.voice.exception.InvalidFileException; +import com.capstone.kkumteul.domain.user.entity.User; +import com.capstone.kkumteul.global.response.SuccessResponse; +import com.capstone.kkumteul.global.security.AuthUser; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@Slf4j +@RestController +// not hard-fix configuration convention +@RequestMapping("/api/users/voice") +@RequiredArgsConstructor +public class VoiceController { + + @PostMapping + public ResponseEntity> sendTtsRequestMessage( + @AuthUser User user, + @RequestPart MultipartFile wavFile + ) { + + // File validation + if(wavFile.isEmpty() || wavFile.getSize() == 0 || wavFile.getName().split("\\.")[1].equals("wav")) + throw new InvalidFileException(); + + + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessResponse.empty()); + } +} From 8980a0ae559c72a4cd8d620529101492c9e059c4 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 10:11:39 +0900 Subject: [PATCH 04/48] =?UTF-8?q?Feat:=20Voice=20=EB=8F=84=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=EC=97=90=EC=84=9C=20=EC=82=AC=EC=9A=A9=ED=95=A0=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=BD=94=EB=93=9C=EB=A5=BC=20=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=ED=95=98=EB=8A=94=20=EC=9D=B4=EB=84=98=20=EA=B0=9D?= =?UTF-8?q?=EC=B2=B4=20=EC=9E=91=EC=84=B1,=20=EC=9E=98=EB=AA=BB=EB=90=9C?= =?UTF-8?q?=20=ED=8C=8C=EC=9D=BC=20=EC=96=91=EC=8B=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=84=98=EC=96=B4=EC=99=94=EC=9D=84=20=EB=95=8C=20=EB=8D=98?= =?UTF-8?q?=EC=A7=88=20=EC=98=88=EC=99=B8=20=EC=BD=94=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice/exception/VoiceErrorCode.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java new file mode 100644 index 0000000..1567acb --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java @@ -0,0 +1,16 @@ +package com.capstone.kkumteul.domain.fairytale.voice.exception; + +import com.capstone.kkumteul.global.response.code.BaseResponseCode; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum VoiceErrorCode implements BaseResponseCode { + + INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "잘못된 파일") + + private final int code; + private final int httpStatus; + private final String message; +} From 4a1cbac94ff161a51b2124a0b74f4f13be801a17 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 10:12:40 +0900 Subject: [PATCH 05/48] =?UTF-8?q?Feat:=20=EC=9D=8C=EC=84=B1=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=EB=A7=81=20=EC=8B=9C,=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EB=B0=94=EB=94=94=EC=97=90=20.wav=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=B4=20=EC=95=84=EB=8B=8C=20=EA=B2=BD=EC=9A=B0,=20?= =?UTF-8?q?=EB=B9=88=20=EA=B2=BD=EC=9A=B0=20=EB=93=B1=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=20=EC=8B=A4=ED=8C=A8=EC=97=90=20=EC=A0=81=EC=9A=A9=ED=95=A0=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EA=B0=9D=EC=B2=B4=20InvalidFileException?= =?UTF-8?q?=EC=9D=84=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/voice/exception/InvalidFileException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/InvalidFileException.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/InvalidFileException.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/InvalidFileException.java new file mode 100644 index 0000000..f149736 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/InvalidFileException.java @@ -0,0 +1,9 @@ +package com.capstone.kkumteul.domain.fairytale.voice.exception; + +import com.capstone.kkumteul.global.exception.BaseException; + +public class InvalidFileException extends BaseException { + public InvalidFileException() { + super(VoiceErrorCode.INVALID_FILE_EXCEPTION); + } +} From bd77924307a37675a88262f72b9ddb92224f4b37 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 10:13:39 +0900 Subject: [PATCH 06/48] =?UTF-8?q?Fix:=20VoiceErrorCode=20=EB=82=B4?= =?UTF-8?q?=EC=97=90=20=EC=9E=98=EB=AA=BB=20=EC=9E=91=EC=84=B1=EB=90=9C=20?= =?UTF-8?q?code=20=ED=83=80=EC=9E=85,=20=EB=88=84=EB=9D=BD=EB=90=9C=20?= =?UTF-8?q?=EC=84=B8=EB=AF=B8=EC=BD=9C=EB=A1=A0=EC=9D=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fairytale/voice/exception/VoiceErrorCode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java index 1567acb..5b5f726 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java @@ -8,9 +8,9 @@ @AllArgsConstructor public enum VoiceErrorCode implements BaseResponseCode { - INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "잘못된 파일") + INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "잘못된 파일"); - private final int code; + private final String code; private final int httpStatus; private final String message; } From e2037144d560dc6683705e43cf9e087a9db33f8b Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 10:55:49 +0900 Subject: [PATCH 07/48] =?UTF-8?q?Feat:=20S3=EC=97=90=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EC=97=85=EB=A1=9C=EB=93=9C=EB=A5=BC=20=EC=A0=84=EB=8B=B4?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A5=BC=20S3U?= =?UTF-8?q?ploader=EB=9D=BC=EB=8A=94=20=EC=9D=B4=EB=A6=84=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/voice/service/S3Uploader.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/S3Uploader.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/S3Uploader.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/S3Uploader.java new file mode 100644 index 0000000..8c840b8 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/S3Uploader.java @@ -0,0 +1,65 @@ +package com.capstone.kkumteul.domain.fairytale.voice.service; + +import com.capstone.kkumteul.domain.user.entity.User; +import io.awspring.cloud.s3.ObjectMetadata; +import io.awspring.cloud.s3.S3Operations; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.util.UUID; + +@Slf4j +@Component +public class S3Uploader { + + private final S3Operations s3Operations; + private final String AWS_S3_BUCKET_NAME; + + // for dependency injection with environment variable + public S3Uploader( + S3Operations s3Operations, + @Value("${AWS_S3_BUCKET_NAME}") + String AWS_S3_BUCKET_NAME + ) { + this.s3Operations = s3Operations; + this.AWS_S3_BUCKET_NAME = AWS_S3_BUCKET_NAME; + } + + // upload S3 bucket + public String upload(MultipartFile wavFile, User user) throws IOException { + return putS3( + convert(wavFile), + createFilename( + wavFile.getOriginalFilename(), + user.getUsername() + ), + wavFile.getContentType() + ); + } + + private InputStream convert(MultipartFile wavFile) throws IOException { + return wavFile.getInputStream(); + } + + // with UUID + private String createFilename(String originalFilename, String username) { + String uuid = UUID.randomUUID().toString(); + String uniqueFilename = uuid + "-" + originalFilename.replaceAll("\\s", "-"); + return username + "/" + uniqueFilename; + } + + private String putS3(InputStream inputStream,String filename, String contentType) throws IOException { + return s3Operations.upload( + AWS_S3_BUCKET_NAME, + filename, + inputStream, + ObjectMetadata.builder() + .contentType(contentType) + .build() + ).getURL().toString(); + } +} From 8dd6458b2b08023a7fe78750c3985aff11fcb9a8 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:00:23 +0900 Subject: [PATCH 08/48] =?UTF-8?q?Feat:=20=ED=8C=8C=EC=9D=BC=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=EC=97=90=20=EC=8B=A4=ED=8C=A8=ED=96=88?= =?UTF-8?q?=EC=9D=84=20=EA=B2=BD=EC=9A=B0=EC=9D=98=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC=20VoiceErrorCode=EC=97=90=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fairytale/voice/exception/VoiceErrorCode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java index 5b5f726..0966c49 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java @@ -8,7 +8,8 @@ @AllArgsConstructor public enum VoiceErrorCode implements BaseResponseCode { - INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "잘못된 파일"); + INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "잘못된 파일"), + FILE_UPLOAD_FAIL("FILE_UPLOAD_FAIL_500", 500, "파일을 변환하고, S3에 업로드하는 것에 실패했습니다."); private final String code; private final int httpStatus; From c79390d15b640014d7caec524e6309cb75624f5c Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:00:44 +0900 Subject: [PATCH 09/48] =?UTF-8?q?Feat:=20=ED=8C=8C=EC=9D=BC=20=EC=97=85?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=EC=97=90=20=EC=8B=A4=ED=8C=A8=ED=96=88?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=20=EB=8D=98=EC=A7=88=20=EC=98=88=EC=99=B8?= =?UTF-8?q?=20=EA=B0=9D=EC=B2=B4=EB=A5=BC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice/exception/FileUploadFailException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/FileUploadFailException.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/FileUploadFailException.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/FileUploadFailException.java new file mode 100644 index 0000000..599588c --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/FileUploadFailException.java @@ -0,0 +1,9 @@ +package com.capstone.kkumteul.domain.fairytale.voice.exception; + +import com.capstone.kkumteul.global.exception.BaseException; + +public class FileUploadFailException extends BaseException { + public FileUploadFailException() { + super(VoiceErrorCode.FILE_UPLOAD_FAIL); + } +} From bac8b382dcb7a56b29f6cd9c57b301d2e986c029 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:20:10 +0900 Subject: [PATCH 10/48] =?UTF-8?q?Feat:=20userId=EC=99=80=20TTS=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=EC=9D=98=20=EC=9D=B4=EB=A6=84=EC=9D=84=20=EB=A7=A4?= =?UTF-8?q?=ED=95=91=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20=EA=B4=80?= =?UTF-8?q?=EA=B3=84=20=ED=85=8C=EC=9D=B4=EB=B8=94=EC=9D=84=201:1=20?= =?UTF-8?q?=EB=A7=A4=ED=95=91=EC=9C=BC=EB=A1=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/voice/entity/VoiceModel.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java new file mode 100644 index 0000000..32c37ea --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java @@ -0,0 +1,28 @@ +package com.capstone.kkumteul.domain.fairytale.voice.entity; + +import com.capstone.kkumteul.domain.user.entity.User; +import com.capstone.kkumteul.global.entity.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class VoiceModel extends BaseEntity { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @JoinColumn(name = "user_id") + private User user; + + // extracted tts model name + @Column(unique = true) + private String modelName; + + @Column(nullable = false, unique = true) + private String originFilename; +} From dcda66f680faf5c9cf79ad76078e453dd081277e Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:20:50 +0900 Subject: [PATCH 11/48] =?UTF-8?q?Feat:=20userId=EC=99=80=20TTS=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EC=9D=B4=EB=A6=84=EC=9D=84=20=EB=A7=A4=ED=95=91?= =?UTF-8?q?=ED=95=9C=20=EC=97=94=ED=84=B0=ED=8B=B0=EB=A5=BC=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=ED=99=94=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20Rep?= =?UTF-8?q?ository=EB=A5=BC=20Spring=20Data=20JPA=20=EB=B0=A9=EC=8B=9D=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EB=A1=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/voice/repository/VoiceModelRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/repository/VoiceModelRepository.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/repository/VoiceModelRepository.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/repository/VoiceModelRepository.java new file mode 100644 index 0000000..2ec3141 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/repository/VoiceModelRepository.java @@ -0,0 +1,7 @@ +package com.capstone.kkumteul.domain.fairytale.voice.repository; + +import com.capstone.kkumteul.domain.fairytale.voice.entity.VoiceModel; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface VoiceModelRepository extends JpaRepository { +} From 0d299f1d222f041497ea7126114aa57b70e23357 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:22:53 +0900 Subject: [PATCH 12/48] =?UTF-8?q?Feat:=20Kafka=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=EC=9D=98=20=EB=B0=94=EB=94=94=20=EB=B6=80=EB=B6=84?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=A0=20MessageInterface=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=EC=B2=B4=EB=A5=BC=20=EB=B6=88=EB=B3=80=20DTO?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=A1=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/voice/web/dto/TtsModelingRequest.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/dto/TtsModelingRequest.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/dto/TtsModelingRequest.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/dto/TtsModelingRequest.java new file mode 100644 index 0000000..4c24c24 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/dto/TtsModelingRequest.java @@ -0,0 +1,12 @@ +package com.capstone.kkumteul.domain.fairytale.voice.web.dto; + +import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TtsModelingRequest implements MessageInterface { + private final Long userId; + private final String uploadedUrl; +} From 0772900e15cb8f498b49961355f3118da9a6a7b8 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:23:18 +0900 Subject: [PATCH 13/48] =?UTF-8?q?Feat:=20VoiceService=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=EC=97=90=20=EC=9D=8C=EC=84=B1=20?= =?UTF-8?q?=EB=85=B9=EC=9D=8C=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fairytale/voice/service/VoiceService.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java new file mode 100644 index 0000000..5a5bd30 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java @@ -0,0 +1,9 @@ +package com.capstone.kkumteul.domain.fairytale.voice.service; + +import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; +import com.capstone.kkumteul.domain.user.entity.User; +import org.springframework.web.multipart.MultipartFile; + +public interface VoiceService { + TtsModelingRequest saveMp3(MultipartFile wavFile, User user); +} From 67eceadc290eaafb0e20a2d84f85ad4ba0b3b2fe Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:24:06 +0900 Subject: [PATCH 14/48] =?UTF-8?q?Feat:=20EventService=20=EB=82=B4=EC=97=90?= =?UTF-8?q?=20S3=EC=97=90=20=EC=97=85=EB=A1=9C=EB=93=9C=EB=90=9C=20?= =?UTF-8?q?=EC=9D=8C=EC=84=B1=20=ED=8C=8C=EC=9D=BC=EA=B3=BC=20userId?= =?UTF-8?q?=EB=A5=BC=20=EB=A9=94=EC=8B=9C=EC=A7=80=EC=97=90=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=ED=95=98=EC=97=AC=20TTS=EB=A5=BC=20=EB=A7=8C=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EB=8B=AC=EB=9D=BC=EB=8A=94=20=ED=86=A0=ED=94=BD?= =?UTF-8?q?=EC=97=90=20=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A5=BC=20=EC=A0=84?= =?UTF-8?q?=EC=86=A1=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/kafka/service/EventService.java | 18 +++++++++++++++++- 1 file changed, 17 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 81938b1..5151179 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.entity.Fairytale; import com.capstone.kkumteul.domain.fairytale.repository.FairytaleRepository; +import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; 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; @@ -28,6 +29,9 @@ public class EventService { @Value("${FAIRYTALE_GENERATION}") private String FAIRYTALE_GENERATION; + @Value(("${TTS_MODELING}")) + private String TTS_MODELING; + @Transactional public Long createFairytaleMessageSend(User user, FairytaleGenerateReq request) { @@ -55,10 +59,22 @@ public Long createFairytaleMessageSend(User user, FairytaleGenerateReq request) kafkaTemplate.send(FAIRYTALE_GENERATION, message) .whenComplete((result, e) -> { if (e != null) { - log.error("fairytale_generate failed", e); + log.error("fairytale_generate failed. userId={}, message={}", e, user.getId(), message); } }); return saved.getId(); } + + public Long sendTtsModelingRequest(TtsModelingRequest message) { + + kafkaTemplate.send(TTS_MODELING, message) + .whenComplete((result, e) -> { + if(e != null) { + log.error("tts_modeling_request failed. userId={}, message={}", e, message.getUserId(), message.getUploadedUrl()); + } + }); + + return message.getUserId(); + } } From 03122b3fb04d93db41cc548619ad2abc587f7bba Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:24:42 +0900 Subject: [PATCH 15/48] =?UTF-8?q?Feat:=20=EC=9D=8C=EC=84=B1=20=EB=85=B9?= =?UTF-8?q?=EC=9D=8C=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20S3=EC=97=90=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=ED=95=98=EA=B3=A0,=20Entity=EB=A5=BC=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=ED=95=9C=20=ED=9B=84=20Kafka=EC=97=90=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=EB=A5=BC=20=EC=A0=84=EC=86=A1?= =?UTF-8?q?=ED=95=98=EB=8A=94=20EventService=EB=A5=BC=20=ED=98=B8=EC=B6=9C?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A1=9C=EC=A7=81=EC=9D=84=20VoiceService?= =?UTF-8?q?=EC=9D=98=20=EA=B5=AC=ED=98=84=EC=B2=B4=20=EB=82=B4=EC=97=90=20?= =?UTF-8?q?=ED=95=98=EB=82=98=EC=9D=98=20=EB=A9=94=EC=86=8C=EB=93=9C?= =?UTF-8?q?=EB=A1=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice/service/VoiceServiceImpl.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java new file mode 100644 index 0000000..98f6f6b --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java @@ -0,0 +1,57 @@ +package com.capstone.kkumteul.domain.fairytale.voice.service; + +import com.capstone.kkumteul.domain.fairytale.voice.entity.VoiceModel; +import com.capstone.kkumteul.domain.fairytale.voice.exception.FileUploadFailException; +import com.capstone.kkumteul.domain.fairytale.voice.repository.VoiceModelRepository; +import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; +import com.capstone.kkumteul.domain.kafka.service.EventService; +import com.capstone.kkumteul.domain.user.entity.User; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +@Slf4j +@Service +@RequiredArgsConstructor +public class VoiceServiceImpl implements VoiceService { + + private final S3Uploader s3Uploader; + private final VoiceModelRepository voiceModelRepository; + private final EventService eventService; + + @Override + @Transactional + public TtsModelingRequest saveMp3(MultipartFile wavFile, User user) { + + String uploadedUrl; + + try { + uploadedUrl = s3Uploader.upload(wavFile, user); + } catch (IOException e) { + log.error("VoiceService error occurred. userId = {}, filename = {}", user.getId(), wavFile.getOriginalFilename()); + throw new FileUploadFailException(); + } + + VoiceModel saved = VoiceModel.builder() + .user(user) + .originFilename(wavFile.getOriginalFilename()) + .build(); + + voiceModelRepository.save(saved); + + TtsModelingRequest result = sendKafkaMessage(user.getId(), uploadedUrl); + + return result; + } + + private TtsModelingRequest sendKafkaMessage(Long userId, String uploadedUrl) { + TtsModelingRequest requestBody = new TtsModelingRequest(userId, uploadedUrl); + eventService.sendTtsModelingRequest(requestBody); + + return requestBody; + } +} From b1e86881d0ed26057727824af88236a08a06134c Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:26:19 +0900 Subject: [PATCH 16/48] =?UTF-8?q?Feat:=20/api/users/voice=EC=97=90=20Post?= =?UTF-8?q?=EB=A1=9C=20=EB=A7=A4=ED=95=91=EB=90=98=EC=96=B4=20wav=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=84=20form-data=20=ED=98=95=EC=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B0=9B=EA=B3=A0,=20=EA=B7=B8=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=84=20Service=20=EB=A9=94=EC=86=8C?= =?UTF-8?q?=EB=93=9C=EC=9D=98=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=EB=8B=AC=ED=95=98=EB=8A=94=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=9E=91=EC=84=B1,=20userId=EB=A5=BC=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=EC=9C=BC=EB=A1=9C=20=EC=A0=84=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/voice/web/controller/VoiceController.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java index 0c30412..d406d38 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java @@ -1,6 +1,8 @@ package com.capstone.kkumteul.domain.fairytale.voice.web.controller; import com.capstone.kkumteul.domain.fairytale.voice.exception.InvalidFileException; +import com.capstone.kkumteul.domain.fairytale.voice.service.VoiceService; +import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.global.response.SuccessResponse; import com.capstone.kkumteul.global.security.AuthUser; @@ -21,6 +23,8 @@ @RequiredArgsConstructor public class VoiceController { + private final VoiceService voiceService; + @PostMapping public ResponseEntity> sendTtsRequestMessage( @AuthUser User user, @@ -31,8 +35,9 @@ public ResponseEntity> sendTtsRequestMessage( if(wavFile.isEmpty() || wavFile.getSize() == 0 || wavFile.getName().split("\\.")[1].equals("wav")) throw new InvalidFileException(); + TtsModelingRequest result = voiceService.saveMp3(wavFile, user); return ResponseEntity.status(HttpStatus.OK) - .body(SuccessResponse.empty()); + .body(SuccessResponse.created(result.getUserId())); } } From 82435941aa8270650c0d6fd40a48da7c94dbe414 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:27:44 +0900 Subject: [PATCH 17/48] =?UTF-8?q?Feat:=20Voice=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4?= =?UTF-8?q?=EC=99=80=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=EC=97=90=20?= =?UTF-8?q?=EC=A0=84=EB=B0=98=EC=A0=81=EC=9C=BC=EB=A1=9C=20saveMp3=20?= =?UTF-8?q?=EB=A9=94=EC=86=8C=EB=93=9C=EC=9D=98=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=83=80=EC=9E=85=EC=9D=B4=EC=97=88=EB=8D=98=20TtsModelingRequ?= =?UTF-8?q?est=EC=9D=B8=20=EA=B2=83=EC=9D=84=20Void=EB=A1=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fairytale/voice/service/VoiceService.java | 2 +- .../domain/fairytale/voice/service/VoiceServiceImpl.java | 7 +++---- .../fairytale/voice/web/controller/VoiceController.java | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java index 5a5bd30..4caa937 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java @@ -5,5 +5,5 @@ import org.springframework.web.multipart.MultipartFile; public interface VoiceService { - TtsModelingRequest saveMp3(MultipartFile wavFile, User user); + Void saveMp3(MultipartFile wavFile, User user); } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java index 98f6f6b..220c646 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java @@ -25,7 +25,7 @@ public class VoiceServiceImpl implements VoiceService { @Override @Transactional - public TtsModelingRequest saveMp3(MultipartFile wavFile, User user) { + public Void saveMp3(MultipartFile wavFile, User user) { String uploadedUrl; @@ -42,10 +42,9 @@ public TtsModelingRequest saveMp3(MultipartFile wavFile, User user) { .build(); voiceModelRepository.save(saved); + sendKafkaMessage(user.getId(), uploadedUrl); - TtsModelingRequest result = sendKafkaMessage(user.getId(), uploadedUrl); - - return result; + return null; } private TtsModelingRequest sendKafkaMessage(Long userId, String uploadedUrl) { diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java index d406d38..bd6cb1c 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java @@ -35,9 +35,9 @@ public ResponseEntity> sendTtsRequestMessage( if(wavFile.isEmpty() || wavFile.getSize() == 0 || wavFile.getName().split("\\.")[1].equals("wav")) throw new InvalidFileException(); - TtsModelingRequest result = voiceService.saveMp3(wavFile, user); + voiceService.saveMp3(wavFile, user); return ResponseEntity.status(HttpStatus.OK) - .body(SuccessResponse.created(result.getUserId())); + .body(SuccessResponse.created(user.getUserId())); } } From aedd52efe4db9549af51dc6c3313bd35f213d270 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:30:27 +0900 Subject: [PATCH 18/48] =?UTF-8?q?Fix:=20VoiceController=EC=97=90=20wav=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=B4=20=EC=95=84=EB=8B=88=EB=9D=BC?= =?UTF-8?q?=EB=A9=B4=20=EB=8D=98=EC=A0=B8=EC=95=BC=20=ED=95=A0=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=EB=A5=BC=20wav=20=ED=8C=8C=EC=9D=BC=EC=9D=BC=20?= =?UTF-8?q?=EB=95=8C=20=EB=8D=98=EC=A7=80=EB=8A=94=20=EC=98=A4=ED=83=88?= =?UTF-8?q?=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fairytale/voice/web/controller/VoiceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java index bd6cb1c..a0f4292 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java @@ -32,7 +32,7 @@ public ResponseEntity> sendTtsRequestMessage( ) { // File validation - if(wavFile.isEmpty() || wavFile.getSize() == 0 || wavFile.getName().split("\\.")[1].equals("wav")) + if(wavFile.isEmpty() || wavFile.getSize() == 0 || !wavFile.getName().split("\\.")[1].equals("wav")) throw new InvalidFileException(); voiceService.saveMp3(wavFile, user); From d6f5075f3c2919a29ae661ab1402595017d1b09d Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:32:50 +0900 Subject: [PATCH 19/48] =?UTF-8?q?Fix:=20VoiceController=EC=97=90=20form-da?= =?UTF-8?q?ta=20=ED=98=95=EC=8B=9D=EC=9D=98=20.wav=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=ED=98=95=EC=8B=9D=20=EA=B2=80=EC=A6=9D=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/voice/web/controller/VoiceController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java index a0f4292..d0a8d8b 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java @@ -32,7 +32,10 @@ public ResponseEntity> sendTtsRequestMessage( ) { // File validation - if(wavFile.isEmpty() || wavFile.getSize() == 0 || !wavFile.getName().split("\\.")[1].equals("wav")) + String originName = wavFile.getOriginalFilename(); + if(wavFile.isEmpty() + || originName.isBlank() + || !originName.toLowerCase().endsWith(".wav")) throw new InvalidFileException(); voiceService.saveMp3(wavFile, user); From 1fa8c745ae01e0e916e731f70de9d85b912ac434 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:33:18 +0900 Subject: [PATCH 20/48] =?UTF-8?q?Fix:=20Slf4j=EC=9D=98=20log.error=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EC=97=90=20=EB=8C=80=ED=95=B4,=20e=EA=B0=80?= =?UTF-8?q?=20=EB=A7=88=EC=A7=80=EB=A7=89=20=EC=9D=B8=EC=9E=90=EA=B0=80=20?= =?UTF-8?q?=EC=95=84=EB=8B=88=EB=9D=BC=20=EC=8A=A4=ED=83=9D=20=ED=8A=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=8A=A4=EA=B0=80=20=EC=B6=9C=EB=A0=A5?= =?UTF-8?q?=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8D=98=20=EB=AC=B8=EC=A0=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=88=98=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 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5151179..d164704 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 @@ -59,7 +59,7 @@ public Long createFairytaleMessageSend(User user, FairytaleGenerateReq request) kafkaTemplate.send(FAIRYTALE_GENERATION, message) .whenComplete((result, e) -> { if (e != null) { - log.error("fairytale_generate failed. userId={}, message={}", e, user.getId(), message); + log.error("fairytale_generate failed. userId={}, message={}", user.getId(), message, e); } }); @@ -71,7 +71,7 @@ public Long sendTtsModelingRequest(TtsModelingRequest message) { kafkaTemplate.send(TTS_MODELING, message) .whenComplete((result, e) -> { if(e != null) { - log.error("tts_modeling_request failed. userId={}, message={}", e, message.getUserId(), message.getUploadedUrl()); + log.error("tts_modeling_request failed. userId={}, message={}", message.getUserId(), message.getUploadedUrl(), e); } }); From 1cf0257c335f886250bd446190c4e0164960fbbd Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:36:41 +0900 Subject: [PATCH 21/48] =?UTF-8?q?Fix:=20=EA=B8=B0=ED=83=80=20=EC=98=A4?= =?UTF-8?q?=ED=83=88=EC=9E=90=EC=99=80=20=EA=B8=B0=EB=8C=80=EB=8C=80?= =?UTF-8?q?=EB=A1=9C=20=EB=8F=99=EC=9E=91=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8D=98=20=EC=BD=94=EB=93=9C=EB=93=A4=EC=9D=84=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 --- .../kkumteul/domain/fairytale/voice/entity/VoiceModel.java | 4 ++-- .../domain/fairytale/voice/service/VoiceService.java | 3 +-- .../domain/fairytale/voice/service/VoiceServiceImpl.java | 2 +- .../fairytale/voice/web/controller/VoiceController.java | 5 ++--- .../capstone/kkumteul/domain/kafka/service/EventService.java | 4 +--- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java index 32c37ea..56120a3 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java @@ -15,12 +15,12 @@ public class VoiceModel extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private User user; // extracted tts model name - @Column(unique = true) + @Column(unique = true, nullable = true) private String modelName; @Column(nullable = false, unique = true) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java index 4caa937..2141061 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java @@ -1,9 +1,8 @@ package com.capstone.kkumteul.domain.fairytale.voice.service; -import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; import com.capstone.kkumteul.domain.user.entity.User; import org.springframework.web.multipart.MultipartFile; public interface VoiceService { - Void saveMp3(MultipartFile wavFile, User user); + Void saveWav(MultipartFile wavFile, User user); } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java index 220c646..c1ba432 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java @@ -25,7 +25,7 @@ public class VoiceServiceImpl implements VoiceService { @Override @Transactional - public Void saveMp3(MultipartFile wavFile, User user) { + public Void saveWav(MultipartFile wavFile, User user) { String uploadedUrl; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java index d0a8d8b..f4c2cdd 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java @@ -2,7 +2,6 @@ import com.capstone.kkumteul.domain.fairytale.voice.exception.InvalidFileException; import com.capstone.kkumteul.domain.fairytale.voice.service.VoiceService; -import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.global.response.SuccessResponse; import com.capstone.kkumteul.global.security.AuthUser; @@ -38,9 +37,9 @@ public ResponseEntity> sendTtsRequestMessage( || !originName.toLowerCase().endsWith(".wav")) throw new InvalidFileException(); - voiceService.saveMp3(wavFile, user); + voiceService.saveWav(wavFile, user); - return ResponseEntity.status(HttpStatus.OK) + return ResponseEntity.status(HttpStatus.CREATED) .body(SuccessResponse.created(user.getUserId())); } } 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 d164704..5320f23 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 @@ -66,7 +66,7 @@ public Long createFairytaleMessageSend(User user, FairytaleGenerateReq request) return saved.getId(); } - public Long sendTtsModelingRequest(TtsModelingRequest message) { + public void sendTtsModelingRequest(TtsModelingRequest message) { kafkaTemplate.send(TTS_MODELING, message) .whenComplete((result, e) -> { @@ -74,7 +74,5 @@ public Long sendTtsModelingRequest(TtsModelingRequest message) { log.error("tts_modeling_request failed. userId={}, message={}", message.getUserId(), message.getUploadedUrl(), e); } }); - - return message.getUserId(); } } From f69d01d0a3909bb23d88f1ad9688ac2291adb7b9 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 11:49:59 +0900 Subject: [PATCH 22/48] =?UTF-8?q?Fix:=20=EC=9E=98=EB=AA=BB=EB=90=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EB=A1=9C=20=EC=9A=94=EC=B2=AD=ED=96=88?= =?UTF-8?q?=EC=9D=84=20=EB=95=8C=EC=9D=98=20message=EB=A5=BC=20=EB=8D=94?= =?UTF-8?q?=20=EC=9E=90=EC=84=B8=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fairytale/voice/exception/VoiceErrorCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java index 0966c49..364f4b8 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java @@ -8,7 +8,7 @@ @AllArgsConstructor public enum VoiceErrorCode implements BaseResponseCode { - INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "잘못된 파일"), + INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "요청값으로 전달한 파일의 양식이 잘못었습니다."), FILE_UPLOAD_FAIL("FILE_UPLOAD_FAIL_500", 500, "파일을 변환하고, S3에 업로드하는 것에 실패했습니다."); private final String code; From cc444f9efe74c2e358ba6a02a77a60ca3d369585 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 13:15:37 +0900 Subject: [PATCH 23/48] =?UTF-8?q?Refactor:=20voice=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=EA=B0=80=20fairytale=20=ED=95=98=EC=9C=84=EC=97=90=20?= =?UTF-8?q?=EC=9E=88=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkumteul/domain/kafka/service/EventService.java | 2 +- .../{fairytale => }/voice/entity/VoiceModel.java | 2 +- .../voice/exception/FileUploadFailException.java | 2 +- .../voice/exception/InvalidFileException.java | 2 +- .../voice/exception/VoiceErrorCode.java | 2 +- .../voice/repository/VoiceModelRepository.java | 4 ++-- .../{fairytale => }/voice/service/S3Uploader.java | 2 +- .../{fairytale => }/voice/service/VoiceService.java | 2 +- .../voice/service/VoiceServiceImpl.java | 10 +++++----- .../voice/web/controller/VoiceController.java | 6 +++--- .../voice/web/dto/TtsModelingRequest.java | 2 +- 11 files changed, 18 insertions(+), 18 deletions(-) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/entity/VoiceModel.java (91%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/exception/FileUploadFailException.java (77%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/exception/InvalidFileException.java (77%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/exception/VoiceErrorCode.java (89%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/repository/VoiceModelRepository.java (51%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/service/S3Uploader.java (97%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/service/VoiceService.java (76%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/service/VoiceServiceImpl.java (80%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/web/controller/VoiceController.java (86%) rename src/main/java/com/capstone/kkumteul/domain/{fairytale => }/voice/web/dto/TtsModelingRequest.java (82%) 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 5320f23..167becd 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,7 +2,7 @@ import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; import com.capstone.kkumteul.domain.fairytale.repository.FairytaleRepository; -import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; +import com.capstone.kkumteul.domain.voice.web.dto.TtsModelingRequest; 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; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java b/src/main/java/com/capstone/kkumteul/domain/voice/entity/VoiceModel.java similarity index 91% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java rename to src/main/java/com/capstone/kkumteul/domain/voice/entity/VoiceModel.java index 56120a3..13270f3 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/entity/VoiceModel.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/entity/VoiceModel.java @@ -1,4 +1,4 @@ -package com.capstone.kkumteul.domain.fairytale.voice.entity; +package com.capstone.kkumteul.domain.voice.entity; import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.global.entity.BaseEntity; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/FileUploadFailException.java b/src/main/java/com/capstone/kkumteul/domain/voice/exception/FileUploadFailException.java similarity index 77% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/FileUploadFailException.java rename to src/main/java/com/capstone/kkumteul/domain/voice/exception/FileUploadFailException.java index 599588c..dcb2599 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/FileUploadFailException.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/exception/FileUploadFailException.java @@ -1,4 +1,4 @@ -package com.capstone.kkumteul.domain.fairytale.voice.exception; +package com.capstone.kkumteul.domain.voice.exception; import com.capstone.kkumteul.global.exception.BaseException; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/InvalidFileException.java b/src/main/java/com/capstone/kkumteul/domain/voice/exception/InvalidFileException.java similarity index 77% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/InvalidFileException.java rename to src/main/java/com/capstone/kkumteul/domain/voice/exception/InvalidFileException.java index f149736..0e80b00 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/InvalidFileException.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/exception/InvalidFileException.java @@ -1,4 +1,4 @@ -package com.capstone.kkumteul.domain.fairytale.voice.exception; +package com.capstone.kkumteul.domain.voice.exception; import com.capstone.kkumteul.global.exception.BaseException; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java similarity index 89% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java rename to src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java index 364f4b8..85e1fb2 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/exception/VoiceErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java @@ -1,4 +1,4 @@ -package com.capstone.kkumteul.domain.fairytale.voice.exception; +package com.capstone.kkumteul.domain.voice.exception; import com.capstone.kkumteul.global.response.code.BaseResponseCode; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/repository/VoiceModelRepository.java b/src/main/java/com/capstone/kkumteul/domain/voice/repository/VoiceModelRepository.java similarity index 51% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/repository/VoiceModelRepository.java rename to src/main/java/com/capstone/kkumteul/domain/voice/repository/VoiceModelRepository.java index 2ec3141..67d95c0 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/repository/VoiceModelRepository.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/repository/VoiceModelRepository.java @@ -1,6 +1,6 @@ -package com.capstone.kkumteul.domain.fairytale.voice.repository; +package com.capstone.kkumteul.domain.voice.repository; -import com.capstone.kkumteul.domain.fairytale.voice.entity.VoiceModel; +import com.capstone.kkumteul.domain.voice.entity.VoiceModel; import org.springframework.data.jpa.repository.JpaRepository; public interface VoiceModelRepository extends JpaRepository { diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/S3Uploader.java b/src/main/java/com/capstone/kkumteul/domain/voice/service/S3Uploader.java similarity index 97% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/S3Uploader.java rename to src/main/java/com/capstone/kkumteul/domain/voice/service/S3Uploader.java index 8c840b8..217dfca 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/S3Uploader.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/service/S3Uploader.java @@ -1,4 +1,4 @@ -package com.capstone.kkumteul.domain.fairytale.voice.service; +package com.capstone.kkumteul.domain.voice.service; import com.capstone.kkumteul.domain.user.entity.User; import io.awspring.cloud.s3.ObjectMetadata; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceService.java similarity index 76% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java rename to src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceService.java index 2141061..ca3faff 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceService.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceService.java @@ -1,4 +1,4 @@ -package com.capstone.kkumteul.domain.fairytale.voice.service; +package com.capstone.kkumteul.domain.voice.service; import com.capstone.kkumteul.domain.user.entity.User; import org.springframework.web.multipart.MultipartFile; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java similarity index 80% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java rename to src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java index c1ba432..0f5722d 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/service/VoiceServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java @@ -1,9 +1,9 @@ -package com.capstone.kkumteul.domain.fairytale.voice.service; +package com.capstone.kkumteul.domain.voice.service; -import com.capstone.kkumteul.domain.fairytale.voice.entity.VoiceModel; -import com.capstone.kkumteul.domain.fairytale.voice.exception.FileUploadFailException; -import com.capstone.kkumteul.domain.fairytale.voice.repository.VoiceModelRepository; -import com.capstone.kkumteul.domain.fairytale.voice.web.dto.TtsModelingRequest; +import com.capstone.kkumteul.domain.voice.entity.VoiceModel; +import com.capstone.kkumteul.domain.voice.exception.FileUploadFailException; +import com.capstone.kkumteul.domain.voice.repository.VoiceModelRepository; +import com.capstone.kkumteul.domain.voice.web.dto.TtsModelingRequest; import com.capstone.kkumteul.domain.kafka.service.EventService; import com.capstone.kkumteul.domain.user.entity.User; import lombok.RequiredArgsConstructor; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java similarity index 86% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java rename to src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java index f4c2cdd..0faecc9 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java @@ -1,7 +1,7 @@ -package com.capstone.kkumteul.domain.fairytale.voice.web.controller; +package com.capstone.kkumteul.domain.voice.web.controller; -import com.capstone.kkumteul.domain.fairytale.voice.exception.InvalidFileException; -import com.capstone.kkumteul.domain.fairytale.voice.service.VoiceService; +import com.capstone.kkumteul.domain.voice.exception.InvalidFileException; +import com.capstone.kkumteul.domain.voice.service.VoiceService; import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.global.response.SuccessResponse; import com.capstone.kkumteul.global.security.AuthUser; diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/dto/TtsModelingRequest.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsModelingRequest.java similarity index 82% rename from src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/dto/TtsModelingRequest.java rename to src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsModelingRequest.java index 4c24c24..7a8eff1 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/voice/web/dto/TtsModelingRequest.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsModelingRequest.java @@ -1,4 +1,4 @@ -package com.capstone.kkumteul.domain.fairytale.voice.web.dto; +package com.capstone.kkumteul.domain.voice.web.dto; import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; import lombok.AllArgsConstructor; From 4b7ffff997ae75ab7aff7ad3bb750fc1c0db94e7 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 13:30:18 +0900 Subject: [PATCH 24/48] =?UTF-8?q?Feat:=20=EB=AC=B8=EC=9E=A5=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=EB=A1=9C=20TTS=20=ED=8C=8C=EC=9D=BC=EA=B3=BC=20User?= =?UTF-8?q?=EB=A5=BC=20=EB=A7=A4=ED=95=91=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EB=8A=94=20=EC=A4=91=EA=B0=84=20=ED=85=8C=EC=9D=B4=EB=B8=94=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/voice/entity/TtsHistory.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java b/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java new file mode 100644 index 0000000..be80377 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java @@ -0,0 +1,27 @@ +package com.capstone.kkumteul.domain.voice.entity; + +import com.capstone.kkumteul.domain.fairytale.entity.Paragraph; +import com.capstone.kkumteul.domain.user.entity.User; +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class TtsHistory { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToOne(fetch = FetchType.LAZY) + private User user; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "paragraph_id") + private Paragraph paragraph; + + private String ttsUrl; +} From 7eaac1584f7e85f2acdc2239c43b8b53b7b3a030 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 13:33:51 +0900 Subject: [PATCH 25/48] =?UTF-8?q?Feat:=20TtsHistory=EC=9D=98=20Spring=20Da?= =?UTF-8?q?ta=20JPA=20interface=20declaration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/voice/repository/TtsHistoryRepository.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java b/src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java new file mode 100644 index 0000000..99a4ed7 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java @@ -0,0 +1,7 @@ +package com.capstone.kkumteul.domain.voice.repository; + +import com.capstone.kkumteul.domain.voice.entity.TtsHistory; +import org.springframework.data.repository.CrudRepository; + +public interface TtsHistoryRepository extends CrudRepository { +} From c875c4a74ef57579c52281ec3bafe75ea79ddc2f Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:42:01 +0900 Subject: [PATCH 26/48] =?UTF-8?q?Feat:=20202=20ACCEPTED=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkumteul/global/response/code/SuccessResponseCode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/global/response/code/SuccessResponseCode.java b/src/main/java/com/capstone/kkumteul/global/response/code/SuccessResponseCode.java index a6348cd..bfcf470 100644 --- a/src/main/java/com/capstone/kkumteul/global/response/code/SuccessResponseCode.java +++ b/src/main/java/com/capstone/kkumteul/global/response/code/SuccessResponseCode.java @@ -11,7 +11,8 @@ public enum SuccessResponseCode implements BaseResponseCode { SUCCESS_OK("SUCCESS_200", OK, "호출에 성공했습니다."), - SUCCESS_CREATED("SUCCESS_201", CREATED, "호출에 성공했습니다."); + SUCCESS_CREATED("SUCCESS_201", CREATED, "호출에 성공했습니다."), + SUCCESS_ACCEPTED("SUCCESS_202", 202, "요청 진행이 받아들여졌습니다."); private final String code; private final int httpStatus; From 1c295ac1d4ea7c310037525370acdec449794d29 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:42:19 +0900 Subject: [PATCH 27/48] =?UTF-8?q?Feat:=20202=20ACCEPTED=EB=A5=BC=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=9C=20=EC=A0=95=EC=A0=81=20=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/kkumteul/global/response/SuccessResponse.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/capstone/kkumteul/global/response/SuccessResponse.java b/src/main/java/com/capstone/kkumteul/global/response/SuccessResponse.java index 9eefe54..964a6c2 100644 --- a/src/main/java/com/capstone/kkumteul/global/response/SuccessResponse.java +++ b/src/main/java/com/capstone/kkumteul/global/response/SuccessResponse.java @@ -40,6 +40,10 @@ public static SuccessResponse empty() { return new SuccessResponse<>(null, SuccessResponseCode.SUCCESS_OK); } + public static SuccessResponse accepted() { + return new SuccessResponse<>(null, SuccessResponseCode.SUCCESS_ACCEPTED); + } + public static SuccessResponse of(T data, BaseResponseCode baseResponseCode) { return new SuccessResponse<>(data, baseResponseCode); } From fc299baaedc25b55c90656e82bca0093035507b7 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:44:26 +0900 Subject: [PATCH 28/48] =?UTF-8?q?Refactor:=20Kafka=20Message=EC=9D=98=20Bo?= =?UTF-8?q?dy=20interface=EB=A5=BC=20=EB=8D=94=20=ED=8F=AD=20=EB=84=93?= =?UTF-8?q?=EA=B2=8C=20=EC=82=AC=EC=9A=A9=ED=95=98=EA=B8=B0=20=EC=9C=84?= =?UTF-8?q?=ED=95=B4=20MessageInterface=EC=97=90=20=EC=9E=88=EB=8D=98=20ge?= =?UTF-8?q?tUserId=20=EB=A9=94=EC=86=8C=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/kkumteul/domain/kafka/dto/MessageInterface.java | 2 -- 1 file changed, 2 deletions(-) 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 index fd733ce..6415d5f 100644 --- a/src/main/java/com/capstone/kkumteul/domain/kafka/dto/MessageInterface.java +++ b/src/main/java/com/capstone/kkumteul/domain/kafka/dto/MessageInterface.java @@ -1,6 +1,4 @@ package com.capstone.kkumteul.domain.kafka.dto; public interface MessageInterface { - - Long getUserId(); } From 25ffdc70dc64dd4106645da8d1ad4bb6654aedcb Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:45:20 +0900 Subject: [PATCH 29/48] =?UTF-8?q?Feat:=20TTS=EB=A5=BC=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=9C=20=EC=9D=8C=EC=84=B1=20=EB=85=B9=EC=9D=8C=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=9D=B4=20=EC=97=86=EC=9D=84=20=EB=95=8C=20=EB=8D=98?= =?UTF-8?q?=EC=A7=88=20=EC=98=88=EC=99=B8=EC=97=90=20=EB=8C=80=ED=95=9C=20?= =?UTF-8?q?=EC=BD=94=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 --- .../capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java index 85e1fb2..5bc0071 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceErrorCode.java @@ -9,6 +9,7 @@ public enum VoiceErrorCode implements BaseResponseCode { INVALID_FILE_EXCEPTION("INVALID_FILE_400", 400, "요청값으로 전달한 파일의 양식이 잘못었습니다."), + FILE_NOT_CREATED("FILE_NOT_FOUND_404", 404, "TTS 음성 파일을 찾을 수 없습니다."), FILE_UPLOAD_FAIL("FILE_UPLOAD_FAIL_500", 500, "파일을 변환하고, S3에 업로드하는 것에 실패했습니다."); private final String code; From 630fc78610376d837447fa2ddc622a4b1f00cd4b Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:46:10 +0900 Subject: [PATCH 30/48] =?UTF-8?q?Feat:=20Paragraph=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=97=90=20=EC=8B=A4=ED=8C=A8=ED=96=88=EC=9D=84=20=EB=95=8C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=A0=20=EC=97=90=EB=9F=AC=20=EC=BD=94?= =?UTF-8?q?=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/fairytale/exception/FairytaleErrorCode.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 4c3abb1..c27b127 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 @@ -10,7 +10,8 @@ @AllArgsConstructor public enum FairytaleErrorCode implements BaseResponseCode { - FAIRYTALE_NOT_FOUND("FAIRYTALE_404_1", NOT_FOUND, "동화를 찾을 수 없습니다."); + FAIRYTALE_NOT_FOUND("FAIRYTALE_404_1", NOT_FOUND, "동화를 찾을 수 없습니다."), + PARAGRAPH_NOT_FOUND("FAIRYTALE_404_2", NOT_FOUND, "요청한 ID에 해당하는 Paragraph를 찾을 수 없습니다."); private final String code; private final int httpStatus; From 5ad2f092ff63bef37e7a3bdd2c6077871cdaf75c Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:46:58 +0900 Subject: [PATCH 31/48] =?UTF-8?q?Feat:=20Paragraph=EB=A5=BC=20=EC=B0=BE?= =?UTF-8?q?=EC=A7=80=20=EB=AA=BB=ED=96=88=EC=9D=84=20=EB=95=8C=20=EB=8D=98?= =?UTF-8?q?=EC=A7=88=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EA=B0=9D=EC=B2=B4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/exception/ParagraphNotFoundException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/exception/ParagraphNotFoundException.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/exception/ParagraphNotFoundException.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/exception/ParagraphNotFoundException.java new file mode 100644 index 0000000..8b457a1 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/exception/ParagraphNotFoundException.java @@ -0,0 +1,9 @@ +package com.capstone.kkumteul.domain.fairytale.exception; + +import com.capstone.kkumteul.global.exception.BaseException; + +public class ParagraphNotFoundException extends BaseException { + public ParagraphNotFoundException() { + super(FairytaleErrorCode.PARAGRAPH_NOT_FOUND); + } +} From fd9fd4a98559e20de19cec9f61b7ef9d0290689c Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:48:49 +0900 Subject: [PATCH 32/48] =?UTF-8?q?Feat:=20TTS=20=EC=9D=8C=EC=84=B1=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EC=B0=BE=EC=A7=80=20=EB=AA=BB?= =?UTF-8?q?=ED=96=88=EC=9D=84=20=EB=95=8C=20=EB=8D=98=EC=A7=88=20=EC=BB=A4?= =?UTF-8?q?=EC=8A=A4=ED=85=80=20=EC=98=88=EC=99=B8=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice/exception/VoiceFileNotFoundException.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceFileNotFoundException.java diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceFileNotFoundException.java b/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceFileNotFoundException.java new file mode 100644 index 0000000..bc1419c --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/voice/exception/VoiceFileNotFoundException.java @@ -0,0 +1,9 @@ +package com.capstone.kkumteul.domain.voice.exception; + +import com.capstone.kkumteul.global.exception.BaseException; + +public class VoiceFileNotFoundException extends BaseException { + public VoiceFileNotFoundException() { + super(VoiceErrorCode.FILE_NOT_CREATED); + } +} From 3f8f42ea3f0d8a9ff06d97fee35e4de880656976 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:49:41 +0900 Subject: [PATCH 33/48] =?UTF-8?q?Feat:=20TTS=20=EC=9A=94=EC=B2=AD,=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=97=90=20=EB=8C=80=ED=95=9C=20DTO=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/voice/web/dto/TtsFileRequest.java | 13 +++++++++++++ .../domain/voice/web/dto/TtsFileResponse.java | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java create mode 100644 src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileResponse.java diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java new file mode 100644 index 0000000..48c14b1 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java @@ -0,0 +1,13 @@ +package com.capstone.kkumteul.domain.voice.web.dto; + +import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TtsFileRequest implements MessageInterface { + + private Long userId; + private Long paragraphId; +} diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileResponse.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileResponse.java new file mode 100644 index 0000000..4c5460e --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileResponse.java @@ -0,0 +1,13 @@ +package com.capstone.kkumteul.domain.voice.web.dto; + +import com.capstone.kkumteul.domain.kafka.dto.MessageInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class TtsFileResponse implements MessageInterface { + + private final Long paragraphId; + private final String ttsUrl; +} From e4c65ce2668ac080d6bba7cf70595bf7c2a58c3e Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:50:29 +0900 Subject: [PATCH 34/48] =?UTF-8?q?Feat:=20TTS=20=EC=9D=8C=EC=84=B1=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=B4=20=EC=A1=B4=EC=9E=AC=ED=95=98?= =?UTF-8?q?=EB=8A=94=EC=A7=80=20=ED=99=95=EC=9D=B8,=20=EC=97=86=EB=8B=A4?= =?UTF-8?q?=EB=A9=B4=20=EC=83=9D=EC=84=B1=EC=9D=84=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=B9=84=EC=A6=88=EB=8B=88=EC=8A=A4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=A9=94=EC=86=8C=EB=93=9C=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/kkumteul/domain/voice/service/VoiceService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceService.java b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceService.java index ca3faff..e126ec9 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceService.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceService.java @@ -1,8 +1,12 @@ package com.capstone.kkumteul.domain.voice.service; import com.capstone.kkumteul.domain.user.entity.User; +import com.capstone.kkumteul.domain.voice.web.dto.TtsFileResponse; import org.springframework.web.multipart.MultipartFile; public interface VoiceService { Void saveWav(MultipartFile wavFile, User user); + Boolean hasTtsHistory(Long userId, Long paragraphId); + TtsFileResponse getTtsFile(Long userId, Long paragraphId); + Void createTtsFile(Long userId, Long paragraphId); } From 315fcf64dbc5d7f7b4ceed14c19eaac331b7a822 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:51:10 +0900 Subject: [PATCH 35/48] =?UTF-8?q?Feat:=20paragraphId=EC=99=80=20userId?= =?UTF-8?q?=EB=A5=BC=20=EA=B8=B0=EB=B0=98=EC=9C=BC=EB=A1=9C=20TTS=20?= =?UTF-8?q?=EC=9D=8C=EC=84=B1=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=ED=96=88=EB=8A=94=EC=A7=80=20=EC=9D=B4=EB=A0=A5?= =?UTF-8?q?=EC=9D=84=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=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 --- .../voice/repository/TtsHistoryRepository.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java b/src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java index 99a4ed7..5153671 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/repository/TtsHistoryRepository.java @@ -1,7 +1,23 @@ package com.capstone.kkumteul.domain.voice.repository; import com.capstone.kkumteul.domain.voice.entity.TtsHistory; +import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.CrudRepository; +import org.springframework.data.repository.query.Param; + +import java.util.Optional; public interface TtsHistoryRepository extends CrudRepository { + + @Query(""" +select th +from TtsHistory th join fetch + th.paragraph p +where p.id = :paragraphId + and th.user.id = :userId +""" + ) + Optional findByParagraphIdAndUserId( + @Param("paragraphId") Long paragraphId, + @Param("userId") Long userId); } From 8ac4359cb91d1b196a1b1397956842f92fd04d96 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:51:58 +0900 Subject: [PATCH 36/48] =?UTF-8?q?Feat:=20PagraphId=EA=B0=80=20=EC=9C=A0?= =?UTF-8?q?=ED=9A=A8=ED=95=9C=20=EA=B0=92=EC=9C=BC=EB=A1=9C=20=EB=93=A4?= =?UTF-8?q?=EC=96=B4=EC=99=94=EB=8A=94=EC=A7=80=20=EA=B2=80=EC=A6=9D?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=B1=85=EC=9E=84=EC=9D=84=20fairytale=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EC=97=90=20=EC=9C=84=EC=9E=84?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fairytale/validator/ParagraphValidator.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java new file mode 100644 index 0000000..9c444b6 --- /dev/null +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java @@ -0,0 +1,16 @@ +package com.capstone.kkumteul.domain.fairytale.validator; + +import com.capstone.kkumteul.domain.fairytale.repository.ParagraphRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ParagraphValidator { + + private final ParagraphRepository paragraphRepository; + + public boolean paragraphIdIsValid(Long paragraphId) { + return paragraphRepository.existsById(paragraphId); + } +} From ec99362710b8228cfab7ebc2342f42f6d1be99ba Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:52:42 +0900 Subject: [PATCH 37/48] =?UTF-8?q?Feat:=20TTS=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=9A=94=EC=B2=AD=20Kafka=20Message?= =?UTF-8?q?=EB=A5=BC=20=EC=A0=84=EC=86=A1=ED=95=98=EB=8A=94=20=EB=A9=94?= =?UTF-8?q?=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 | 14 ++++++++++++++ 1 file changed, 14 insertions(+) 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 167becd..a3b0ee2 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,8 @@ import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; import com.capstone.kkumteul.domain.fairytale.repository.FairytaleRepository; +import com.capstone.kkumteul.domain.voice.web.dto.TtsFileRequest; +import com.capstone.kkumteul.domain.voice.web.dto.TtsFileResponse; import com.capstone.kkumteul.domain.voice.web.dto.TtsModelingRequest; import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleGenerateReq; import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; @@ -15,6 +17,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.concurrent.ExecutionException; + /* 동화 생성 이벤트 전파 */ @Service @@ -75,4 +79,14 @@ public void sendTtsModelingRequest(TtsModelingRequest message) { } }); } + + public void sendTtsFileRequest(TtsFileRequest message) { + + kafkaTemplate.send(TTS_MODELING, message) + .whenComplete((result, e) -> { + if( e != null) { + log.error("tts_file_request occurred error. userId={}, paragraphId={}", message.getUserId(), message.getParagraphId(), e); + } + }); + } } From 4f395a0da1d128ef68ade2de28c2e2fc7fb17338 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:53:23 +0900 Subject: [PATCH 38/48] =?UTF-8?q?Feat:=20TTS=20=EC=9D=8C=EC=84=B1=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=B4=20=EC=83=9D=EC=84=B1=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EB=8A=94=EC=A7=80=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=A9=94=EC=86=8C=EB=93=9C,=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=9D=84=20=EC=9A=94=EC=B2=AD=ED=95=98=EB=8A=94=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 --- .../voice/service/VoiceServiceImpl.java | 71 +++++++++++++++++-- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java index 0f5722d..86c2d0a 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java @@ -1,11 +1,18 @@ package com.capstone.kkumteul.domain.voice.service; +import com.capstone.kkumteul.domain.fairytale.exception.ParagraphNotFoundException; +import com.capstone.kkumteul.domain.fairytale.validator.ParagraphValidator; +import com.capstone.kkumteul.domain.kafka.service.EventService; +import com.capstone.kkumteul.domain.user.entity.User; +import com.capstone.kkumteul.domain.voice.entity.TtsHistory; import com.capstone.kkumteul.domain.voice.entity.VoiceModel; import com.capstone.kkumteul.domain.voice.exception.FileUploadFailException; +import com.capstone.kkumteul.domain.voice.exception.VoiceFileNotFoundException; +import com.capstone.kkumteul.domain.voice.repository.TtsHistoryRepository; import com.capstone.kkumteul.domain.voice.repository.VoiceModelRepository; +import com.capstone.kkumteul.domain.voice.web.dto.TtsFileRequest; +import com.capstone.kkumteul.domain.voice.web.dto.TtsFileResponse; import com.capstone.kkumteul.domain.voice.web.dto.TtsModelingRequest; -import com.capstone.kkumteul.domain.kafka.service.EventService; -import com.capstone.kkumteul.domain.user.entity.User; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -13,6 +20,7 @@ import org.springframework.web.multipart.MultipartFile; import java.io.IOException; +import java.util.Optional; @Slf4j @Service @@ -21,7 +29,9 @@ public class VoiceServiceImpl implements VoiceService { private final S3Uploader s3Uploader; private final VoiceModelRepository voiceModelRepository; + private final TtsHistoryRepository ttsRepository; private final EventService eventService; + private final ParagraphValidator paragraphValidator; @Override @Transactional @@ -42,15 +52,64 @@ public Void saveWav(MultipartFile wavFile, User user) { .build(); voiceModelRepository.save(saved); - sendKafkaMessage(user.getId(), uploadedUrl); + sendTtsModelingRequest(user.getId(), uploadedUrl); + + return null; + } + + @Override + public Boolean hasTtsHistory(Long userId, Long paragraphId) { + return ttsRepository.findByParagraphIdAndUserId(paragraphId, userId).isPresent(); + } + + @Override + public Void createTtsFile(Long userId, Long paragraphId) { + + if(!paragraphValidator.paragraphIdIsValid(paragraphId)) { + throw new ParagraphNotFoundException(); + } + sendTtsFileRequest(userId, paragraphId); return null; } - private TtsModelingRequest sendKafkaMessage(Long userId, String uploadedUrl) { + @Override + public TtsFileResponse getTtsFile(Long userId, Long paragraphId) { + + // validation paragraph id by using fairytale package's validator + if (!paragraphValidator.paragraphIdIsValid(paragraphId)) + throw new ParagraphNotFoundException(); + + // User가 해당하는 paragraphId에 대한 tts 음성 파일을 만든 이력이 있는지 체크 + Optional found = ttsRepository.findByParagraphIdAndUserId(paragraphId, userId); + boolean isFound = found.isPresent(); + + TtsFileResponse response; + + if (!isFound) { + // 만든 적 없다면 해당 요청에 들어오면 안됨. 예외처리 + log.error("Voice file not created. userId={}, paragraphId={}", userId, paragraphId); + throw new VoiceFileNotFoundException(); + } else { + // 만든 적 있다면 DB에서 그 음성 파일을 조회 + TtsHistory ttsHistory = found.get(); + response = new TtsFileResponse( + ttsHistory.getParagraph().getId(), + ttsHistory.getTtsUrl() + ); + } + + return response; + } + + private void sendTtsFileRequest(Long userId, Long paragraphId) { + TtsFileRequest request = new TtsFileRequest(userId, paragraphId); + + eventService.sendTtsFileRequest(request); + } + + private void sendTtsModelingRequest(Long userId, String uploadedUrl) { TtsModelingRequest requestBody = new TtsModelingRequest(userId, uploadedUrl); eventService.sendTtsModelingRequest(requestBody); - - return requestBody; } } From dd958d69c3b85f7d453594b1b2050edbd88a7a40 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 14:53:58 +0900 Subject: [PATCH 39/48] =?UTF-8?q?Feat:=20TTS=20=EC=9D=8C=EC=84=B1=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=9D=B4=20=EC=83=9D=EC=84=B1=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EB=8A=94=EC=A7=80=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=98=EB=8A=94=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EB=A9=94=EC=86=8C=EB=93=9C,=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9D=84=20=EC=9A=94=EC=B2=AD=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20=EB=A9=94?= =?UTF-8?q?=EC=86=8C=EB=93=9C=20=EC=A7=84=EC=9E=85=EC=A0=90=EC=9D=84=20?= =?UTF-8?q?=EA=B0=81=EA=B0=81=20=EC=84=A0=EC=96=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../voice/web/controller/VoiceController.java | 40 +++++++++++++++++-- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java index 0faecc9..5b552b3 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java @@ -3,16 +3,14 @@ import com.capstone.kkumteul.domain.voice.exception.InvalidFileException; import com.capstone.kkumteul.domain.voice.service.VoiceService; import com.capstone.kkumteul.domain.user.entity.User; +import com.capstone.kkumteul.domain.voice.web.dto.TtsFileResponse; import com.capstone.kkumteul.global.response.SuccessResponse; import com.capstone.kkumteul.global.security.AuthUser; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestPart; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @Slf4j @@ -42,4 +40,38 @@ public ResponseEntity> sendTtsRequestMessage( return ResponseEntity.status(HttpStatus.CREATED) .body(SuccessResponse.created(user.getUserId())); } + + @GetMapping("/{paragraphId}") + public ResponseEntity> getTtsFile( + @AuthUser User user, + @PathVariable Long paragraphId + ) { + TtsFileResponse response = voiceService.getTtsFile(user.getId(), paragraphId); + + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessResponse.ok(response)); + } + + @PostMapping("/{paragraphId}") + public ResponseEntity> postTtsFile( + @AuthUser User user, + @PathVariable Long paragraphId + ) { + voiceService.createTtsFile(user.getId(), paragraphId); + + return ResponseEntity.status(HttpStatus.ACCEPTED) + .body(SuccessResponse.accepted()); + } + + @GetMapping("/{paragraphId}/check") + public ResponseEntity> hasTtsHistory( + @AuthUser User user, + @PathVariable Long paragraphId + ) { + Boolean response = voiceService.hasTtsHistory(user.getId(), paragraphId); + + return ResponseEntity.status(HttpStatus.OK) + .body(SuccessResponse.ok(response)); + } + } From ca19ed6aa6bed927d5ba30a81f7355661c5389c7 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 15:17:36 +0900 Subject: [PATCH 40/48] Docs: import optimize --- .../capstone/kkumteul/domain/kafka/service/EventService.java | 3 --- .../kkumteul/domain/voice/web/controller/VoiceController.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) 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 a3b0ee2..d429097 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 @@ -3,7 +3,6 @@ import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; import com.capstone.kkumteul.domain.fairytale.repository.FairytaleRepository; import com.capstone.kkumteul.domain.voice.web.dto.TtsFileRequest; -import com.capstone.kkumteul.domain.voice.web.dto.TtsFileResponse; import com.capstone.kkumteul.domain.voice.web.dto.TtsModelingRequest; import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleGenerateReq; import com.capstone.kkumteul.domain.kafka.dto.FairytaleGenerateMessage; @@ -17,8 +16,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.concurrent.ExecutionException; - /* 동화 생성 이벤트 전파 */ @Service diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java index 5b552b3..9512841 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java @@ -70,7 +70,7 @@ public ResponseEntity> hasTtsHistory( ) { Boolean response = voiceService.hasTtsHistory(user.getId(), paragraphId); - return ResponseEntity.status(HttpStatus.OK) + return ResponseEntity.status(response ? HttpStatus.OK : HttpStatus.NO_CONTENT) .body(SuccessResponse.ok(response)); } From 0ab92f7e31a4a7e8b92c8cc28c7c036c93ae93a2 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Tue, 12 May 2026 15:18:29 +0900 Subject: [PATCH 41/48] Docs: optimize all import --- .../fairytale/web/controller/FairytaleController.java | 3 +-- .../kkumteul/domain/game/service/GameServiceImpl.java | 7 +------ .../kkumteul/domain/kafka/service/EventService.java | 4 ++-- .../capstone/kkumteul/domain/voice/entity/TtsHistory.java | 1 - .../domain/voice/web/controller/VoiceController.java | 2 +- .../kkumteul/global/config/KafkaProducerConfig.java | 1 - 6 files changed, 5 insertions(+), 13 deletions(-) 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 0373c1b..e8ca9cb 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 @@ -2,6 +2,7 @@ import com.capstone.kkumteul.domain.fairytale.entity.Island; import com.capstone.kkumteul.domain.fairytale.service.FairytaleService; +import com.capstone.kkumteul.domain.fairytale.service.sse.SseService; 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; @@ -9,8 +10,6 @@ import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.global.response.SuccessResponse; import com.capstone.kkumteul.global.security.AuthUser; - -import com.capstone.kkumteul.domain.fairytale.service.sse.SseService; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; 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 b6adf4d..1a43811 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 @@ -2,13 +2,8 @@ import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; import com.capstone.kkumteul.domain.fairytale.entity.Paragraph; -import com.capstone.kkumteul.domain.fairytale.repository.ParagraphRepository; -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.fairytale.repository.ParagraphRepository; import com.capstone.kkumteul.domain.game.entity.*; import com.capstone.kkumteul.domain.game.exception.*; import com.capstone.kkumteul.domain.game.repository.EdgeChoiceRepository; 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 d429097..812f1d0 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,12 +2,12 @@ import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; import com.capstone.kkumteul.domain.fairytale.repository.FairytaleRepository; -import com.capstone.kkumteul.domain.voice.web.dto.TtsFileRequest; -import com.capstone.kkumteul.domain.voice.web.dto.TtsModelingRequest; 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 com.capstone.kkumteul.domain.user.entity.User; +import com.capstone.kkumteul.domain.voice.web.dto.TtsFileRequest; +import com.capstone.kkumteul.domain.voice.web.dto.TtsModelingRequest; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java b/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java index be80377..7dd282d 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java @@ -2,7 +2,6 @@ import com.capstone.kkumteul.domain.fairytale.entity.Paragraph; import com.capstone.kkumteul.domain.user.entity.User; -import com.fasterxml.jackson.annotation.JsonBackReference; import jakarta.persistence.*; import lombok.*; diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java index 9512841..9d0cb63 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java @@ -1,8 +1,8 @@ package com.capstone.kkumteul.domain.voice.web.controller; +import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.domain.voice.exception.InvalidFileException; import com.capstone.kkumteul.domain.voice.service.VoiceService; -import com.capstone.kkumteul.domain.user.entity.User; import com.capstone.kkumteul.domain.voice.web.dto.TtsFileResponse; import com.capstone.kkumteul.global.response.SuccessResponse; import com.capstone.kkumteul.global.security.AuthUser; 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 3b7e6a5..f04bf37 100644 --- a/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java +++ b/src/main/java/com/capstone/kkumteul/global/config/KafkaProducerConfig.java @@ -6,7 +6,6 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.kafka.core.DefaultKafkaProducerFactory; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.kafka.core.ProducerFactory; From 6fdf2ff61afba7746cb4d70d3432d503acbc29fb Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Wed, 13 May 2026 21:11:03 +0900 Subject: [PATCH 42/48] =?UTF-8?q?Fix:=20S3=EC=97=90=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4=EB=A6=84?= =?UTF-8?q?=EC=9D=84=20=EB=94=94=EB=A0=89=ED=86=A0=EB=A6=AC=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/kkumteul/domain/voice/service/S3Uploader.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/service/S3Uploader.java b/src/main/java/com/capstone/kkumteul/domain/voice/service/S3Uploader.java index 217dfca..4547d7a 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/service/S3Uploader.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/service/S3Uploader.java @@ -35,7 +35,7 @@ public String upload(MultipartFile wavFile, User user) throws IOException { convert(wavFile), createFilename( wavFile.getOriginalFilename(), - user.getUsername() + user.getId() ), wavFile.getContentType() ); @@ -46,10 +46,10 @@ private InputStream convert(MultipartFile wavFile) throws IOException { } // with UUID - private String createFilename(String originalFilename, String username) { + private String createFilename(String originalFilename, Long userId) { String uuid = UUID.randomUUID().toString(); String uniqueFilename = uuid + "-" + originalFilename.replaceAll("\\s", "-"); - return username + "/" + uniqueFilename; + return "tts/" + userId.toString() + "/train/" + uniqueFilename; } private String putS3(InputStream inputStream,String filename, String contentType) throws IOException { From a5fffba0537bbcb417e8503393d6e861367490a1 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Thu, 14 May 2026 00:04:36 +0900 Subject: [PATCH 43/48] =?UTF-8?q?Fix:=20=EB=AC=B8=EB=8B=A8=20=EB=8B=A8?= =?UTF-8?q?=EC=9C=84=20TTS=20=EC=83=9D=EC=84=B1=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=A0=84=EC=B2=B4=20=EB=8F=99=ED=99=94=EC=97=90=20=EB=8C=80?= =?UTF-8?q?=ED=95=9C=20=EC=83=9D=EC=84=B1=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java index 48c14b1..feb07cc 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/dto/TtsFileRequest.java @@ -9,5 +9,5 @@ public class TtsFileRequest implements MessageInterface { private Long userId; - private Long paragraphId; + private Long fairytaleId; } From 33520c4a37a212c99839abd441f9afb4be0ed2c0 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Thu, 14 May 2026 13:08:40 +0900 Subject: [PATCH 44/48] =?UTF-8?q?Fix:=20=EC=BD=94=EB=93=9C=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EA=B8=B0=EB=B0=98=20=EB=9F=B0=ED=83=80=EC=9E=84=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VoiceController: getOriginalFilename() null 반환 시 NPE 방어 처리 - TtsHistory: user 연관관계 @OneToOne → @ManyToOne 변경으로 유저당 복수 TTS 이력 허용 - ParagraphRepository: @Query 제거 후 existsById, existsByFairytaleId 파생 쿼리로 분리 - ParagraphValidator: paragraphIdIsValid 복원, fairytaleIdIsValid 신규 추가 - VoiceServiceImpl: createTtsFile에서 fairytaleIdIsValid 호출로 검증 대상 수정 Co-Authored-By: Claude Sonnet 4.6 --- .../fairytale/repository/ParagraphRepository.java | 4 ++++ .../fairytale/validator/ParagraphValidator.java | 4 ++++ .../kkumteul/domain/kafka/service/EventService.java | 2 +- .../kkumteul/domain/voice/entity/TtsHistory.java | 5 +++-- .../kkumteul/domain/voice/entity/VoiceModel.java | 2 +- .../domain/voice/service/VoiceServiceImpl.java | 8 ++++---- .../domain/voice/web/controller/VoiceController.java | 12 ++++++++---- 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/ParagraphRepository.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/ParagraphRepository.java index aeb4f04..83f0167 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/ParagraphRepository.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/ParagraphRepository.java @@ -13,4 +13,8 @@ public interface ParagraphRepository extends JpaRepository { /** 특정 페이지의 문장들 조회 — 단어장 추출 시 페이지 단위 본문 로드 */ List findByFairytaleIdAndPage(Long fairytaleId, int page); + + boolean existsById(Long paragraphId); + + boolean existsByFairytaleId(Long fairytaleId); } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java index 9c444b6..66792f3 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/validator/ParagraphValidator.java @@ -13,4 +13,8 @@ public class ParagraphValidator { public boolean paragraphIdIsValid(Long paragraphId) { return paragraphRepository.existsById(paragraphId); } + + public boolean fairytaleIdIsValid(Long fairytaleId) { + return paragraphRepository.existsByFairytaleId(fairytaleId); + } } 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 812f1d0..236841f 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 @@ -82,7 +82,7 @@ public void sendTtsFileRequest(TtsFileRequest message) { kafkaTemplate.send(TTS_MODELING, message) .whenComplete((result, e) -> { if( e != null) { - log.error("tts_file_request occurred error. userId={}, paragraphId={}", message.getUserId(), message.getParagraphId(), e); + log.error("tts_file_request occurred error. userId={}, paragraphId={}", message.getUserId(), message.getFairytaleId(), e); } }); } diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java b/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java index 7dd282d..d0c1b3e 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/entity/TtsHistory.java @@ -15,10 +15,11 @@ public class TtsHistory { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @OneToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id") private User user; - @OneToOne(fetch = FetchType.LAZY) + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "paragraph_id") private Paragraph paragraph; diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/entity/VoiceModel.java b/src/main/java/com/capstone/kkumteul/domain/voice/entity/VoiceModel.java index 13270f3..7ca2201 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/entity/VoiceModel.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/entity/VoiceModel.java @@ -24,5 +24,5 @@ public class VoiceModel extends BaseEntity { private String modelName; @Column(nullable = false, unique = true) - private String originFilename; + private String wavFileUrl; } diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java index 86c2d0a..05428b5 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/service/VoiceServiceImpl.java @@ -48,7 +48,7 @@ public Void saveWav(MultipartFile wavFile, User user) { VoiceModel saved = VoiceModel.builder() .user(user) - .originFilename(wavFile.getOriginalFilename()) + .wavFileUrl(uploadedUrl) .build(); voiceModelRepository.save(saved); @@ -63,13 +63,13 @@ public Boolean hasTtsHistory(Long userId, Long paragraphId) { } @Override - public Void createTtsFile(Long userId, Long paragraphId) { + public Void createTtsFile(Long userId, Long fairytaleId) { - if(!paragraphValidator.paragraphIdIsValid(paragraphId)) { + if(!paragraphValidator.fairytaleIdIsValid(fairytaleId)) { throw new ParagraphNotFoundException(); } - sendTtsFileRequest(userId, paragraphId); + sendTtsFileRequest(userId, fairytaleId); return null; } diff --git a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java index 9d0cb63..ccabd15 100644 --- a/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java +++ b/src/main/java/com/capstone/kkumteul/domain/voice/web/controller/VoiceController.java @@ -30,6 +30,10 @@ public ResponseEntity> sendTtsRequestMessage( // File validation String originName = wavFile.getOriginalFilename(); + if(originName == null) { + throw new InvalidFileException(); + } + if(wavFile.isEmpty() || originName.isBlank() || !originName.toLowerCase().endsWith(".wav")) @@ -38,7 +42,7 @@ public ResponseEntity> sendTtsRequestMessage( voiceService.saveWav(wavFile, user); return ResponseEntity.status(HttpStatus.CREATED) - .body(SuccessResponse.created(user.getUserId())); + .body(SuccessResponse.created(user.getId())); } @GetMapping("/{paragraphId}") @@ -52,12 +56,12 @@ public ResponseEntity> getTtsFile( .body(SuccessResponse.ok(response)); } - @PostMapping("/{paragraphId}") + @PostMapping("/{fairytaleId}") public ResponseEntity> postTtsFile( @AuthUser User user, - @PathVariable Long paragraphId + @PathVariable Long fairytaleId ) { - voiceService.createTtsFile(user.getId(), paragraphId); + voiceService.createTtsFile(user.getId(), fairytaleId); return ResponseEntity.status(HttpStatus.ACCEPTED) .body(SuccessResponse.accepted()); From 636395f8deba5587734db54475d23b592c458926 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Thu, 14 May 2026 13:11:29 +0900 Subject: [PATCH 45/48] =?UTF-8?q?Fix:=20Controller=EC=9D=98=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B2=BD=EB=A1=9C=EC=9D=B8=20"/api"=EB=A5=BC=20Req?= =?UTF-8?q?uestMapping=EC=97=90=20=ED=8F=AC=ED=95=A8=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kkumteul/domain/auth/web/controller/AuthController.java | 2 +- .../domain/fairytale/web/controller/FairytaleController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/auth/web/controller/AuthController.java b/src/main/java/com/capstone/kkumteul/domain/auth/web/controller/AuthController.java index de7ff76..2f9be52 100644 --- a/src/main/java/com/capstone/kkumteul/domain/auth/web/controller/AuthController.java +++ b/src/main/java/com/capstone/kkumteul/domain/auth/web/controller/AuthController.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController; @RestController -@RequestMapping("/auth") +@RequestMapping("/api/auth") @RequiredArgsConstructor public class AuthController { 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 e8ca9cb..87195a3 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 @@ -23,7 +23,7 @@ @RestController @RequiredArgsConstructor -@RequestMapping("/fairytales") +@RequestMapping("/api/fairytales") public class FairytaleController { private final FairytaleService fairytaleService; From d759da90aff06d15279b9ea17860ca39a78c79a6 Mon Sep 17 00:00:00 2001 From: zzuhannn Date: Thu, 14 May 2026 20:46:24 +0900 Subject: [PATCH 46/48] =?UTF-8?q?hotfix:=20CORS=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/capstone/kkumteul/global/config/SecurityConfig.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java b/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java index f2c60e3..60036e9 100644 --- a/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java +++ b/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java @@ -7,6 +7,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -60,6 +61,9 @@ public AuthenticationManager authenticationManager(AuthenticationConfiguration a @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http + // CORS 활성화 (WebConfig.addCorsMappings 사용) + .cors(Customizer.withDefaults()) + // JWT 기반 API 서버에서는 stateless를 지향하기 때문에, 세션 비활성화 .csrf(AbstractHttpConfigurer::disable) From ae725b95155bd64072e96a9bbe48f61051f2624b Mon Sep 17 00:00:00 2001 From: zzuhannn Date: Thu, 14 May 2026 20:48:55 +0900 Subject: [PATCH 47/48] =?UTF-8?q?hotfix:SecurityConfig=EC=97=90=20CORS=20?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94=20=EB=B0=8F=20OPTIONS=20=ED=97=88?= =?UTF-8?q?=EC=9A=A9=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/capstone/kkumteul/global/config/SecurityConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java b/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java index 60036e9..c4e3bfc 100644 --- a/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java +++ b/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java @@ -6,6 +6,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; @@ -69,6 +70,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // API path 마다 역할 기반 인가 정책 설정 .authorizeHttpRequests(auth -> auth + .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers("/auth/**").permitAll() .anyRequest().authenticated() // 필터링되지 않은 모든 URL에 대해서 인증 강제 ) From 0272a5e49ab4c46a26a50ae82989019019c93793 Mon Sep 17 00:00:00 2001 From: Joonseok-Lee Date: Fri, 15 May 2026 00:06:57 +0900 Subject: [PATCH 48/48] =?UTF-8?q?Fix:=20SecurityConfig=20=EB=82=B4?= =?UTF-8?q?=EC=97=90=20requestMatchers=EA=B0=80=20/api=EB=A1=9C=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=ED=95=98=EC=A7=80=20=EC=95=8A=EC=95=84=20?= =?UTF-8?q?=EB=AA=A8=EB=93=A0=20=EC=9A=94=EC=B2=AD=EC=9D=B4=20Unauthorized?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=EA=B0=80=20=ED=84=B0=EC=A7=80=EB=8D=98=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/capstone/kkumteul/global/config/SecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java b/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java index c4e3bfc..115f4f4 100644 --- a/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java +++ b/src/main/java/com/capstone/kkumteul/global/config/SecurityConfig.java @@ -71,7 +71,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // API path 마다 역할 기반 인가 정책 설정 .authorizeHttpRequests(auth -> auth .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() - .requestMatchers("/auth/**").permitAll() + .requestMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() // 필터링되지 않은 모든 URL에 대해서 인증 강제 )