From 493bb6ab5ba3f29099e67fd6231707e2ce9a8c56 Mon Sep 17 00:00:00 2001 From: zzuhannn Date: Sun, 17 May 2026 14:33:20 +0900 Subject: [PATCH 1/3] =?UTF-8?q?refactor:=20=EB=8B=A8=EC=96=B4=EC=9E=A5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EC=86=8C=EC=9C=A0=EA=B6=8C=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/vocab/exception/VocabErrorCode.java | 1 - .../vocab/exception/VocabForbiddenException.java | 9 --------- .../domain/vocab/service/VocabServiceImpl.java | 14 +++++--------- src/main/resources/application-dev.properties | 6 ------ 4 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabForbiddenException.java delete mode 100644 src/main/resources/application-dev.properties diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java b/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java index f518fbe..cebda4a 100644 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java +++ b/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabErrorCode.java @@ -11,7 +11,6 @@ public enum VocabErrorCode implements BaseResponseCode { PARAGRAPH_NOT_FOUND_FOR_VOCAB("VOCAB_404_1", NOT_FOUND, "해당 페이지의 본문을 찾을 수 없습니다."), - VOCAB_FORBIDDEN("VOCAB_403_1", FORBIDDEN, "본인 동화의 단어장만 조회할 수 있습니다."), VOCAB_EXTRACT_FAILED("VOCAB_500_1", INTERNAL_SERVER_ERROR, "단어장 추출에 실패했습니다."); private final String code; diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabForbiddenException.java b/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabForbiddenException.java deleted file mode 100644 index 679cd6c..0000000 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/exception/VocabForbiddenException.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.capstone.kkumteul.domain.vocab.exception; - -import com.capstone.kkumteul.global.exception.BaseException; - -public class VocabForbiddenException extends BaseException { - public VocabForbiddenException() { - super(VocabErrorCode.VOCAB_FORBIDDEN); - } -} diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java index 04f5ba9..1a48517 100644 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java @@ -6,7 +6,6 @@ import com.capstone.kkumteul.domain.fairytale.service.FairytaleCheckService; import com.capstone.kkumteul.domain.kafka.dto.VocabExtractedMessage; import com.capstone.kkumteul.domain.vocab.entity.WordEntry; -import com.capstone.kkumteul.domain.vocab.exception.VocabForbiddenException; import com.capstone.kkumteul.domain.vocab.repository.WordEntryRepository; import com.capstone.kkumteul.domain.vocab.service.dto.VocabExtractionResult; import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; @@ -19,7 +18,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; -import java.util.Objects; import java.util.Optional; @Slf4j @@ -134,16 +132,14 @@ public VocabExtractionResult processExtractedWord(VocabExtractedMessage message) } /** - * 본인 동화 누적 단어장 조회. - * 동화 소유권 검증 후 페이지 순서로 반환. + * 동화 누적 단어장 조회. + * 동화 존재 여부만 확인 후 페이지 순서로 반환. + * 동화 상세 조회와 동일한 정책 — 로그인한 사용자 모두 접근 가능. */ @Override public List getVocab(Long userId, Long fairytaleId) { - Fairytale fairytale = fairytaleRepository.findById(fairytaleId) - .orElseThrow(FairytaleNotFoundException::new); - Objects.requireNonNull(fairytale.getUser(), "Fairytale.user는 null이 될 수 없음"); - if (!fairytale.getUser().getId().equals(userId)) { - throw new VocabForbiddenException(); + if (!fairytaleRepository.existsById(fairytaleId)) { + throw new FairytaleNotFoundException(); } return WordEntryRes.listOf(wordEntryRepository.findByFairytaleIdOrderByPageNoAsc(fairytaleId)); } diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties deleted file mode 100644 index b034870..0000000 --- a/src/main/resources/application-dev.properties +++ /dev/null @@ -1,6 +0,0 @@ -# dev profile — Kafka 미가용 환경에서 vocab만 시연. -# 활성화는 -Dspring.profiles.active=dev 또는 SPRING_PROFILES_ACTIVE=dev 환경변수로. -# spring.profiles.active=dev 는 부트스트랩 패러독스 방지 위해 여기에 작성하지 않는다. - -fastapi.base-url=http://localhost:8000 -logging.level.com.capstone.kkumteul=DEBUG From 25e5d9e5d4bbb657b8a762b1b0f79dad4f49e723 Mon Sep 17 00:00:00 2001 From: zzuhannn Date: Sun, 17 May 2026 14:41:28 +0900 Subject: [PATCH 2/3] =?UTF-8?q?refactor:=20=EB=8B=A8=EC=96=B4=EC=9E=A5=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EB=A5=BC=20=EB=8F=99=ED=99=94=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20API=EC=97=90=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/FairytaleServiceImpl.java | 8 ++++- .../fairytale/web/dto/FairytaleDetailRes.java | 9 +++-- .../domain/vocab/service/VocabService.java | 11 +----- .../vocab/service/VocabServiceImpl.java | 13 ------- .../vocab/web/controller/VocabController.java | 35 ------------------- 5 files changed, 14 insertions(+), 62 deletions(-) delete mode 100644 src/main/java/com/capstone/kkumteul/domain/vocab/web/controller/VocabController.java diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java index 942cc4f..27c91f0 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java @@ -8,6 +8,8 @@ import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleDetailRes; import com.capstone.kkumteul.domain.fairytale.web.dto.FairytaleListRes; import com.capstone.kkumteul.domain.fairytale.web.dto.ParagraphRes; +import com.capstone.kkumteul.domain.vocab.repository.WordEntryRepository; +import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -23,6 +25,7 @@ public class FairytaleServiceImpl implements FairytaleService { private final FairytaleRepository fairytaleRepository; private final ParagraphRepository paragraphRepository; + private final WordEntryRepository wordEntryRepository; @Override public Page getMyFairytales(Long userId, Island island, Pageable pageable) { @@ -46,6 +49,9 @@ public FairytaleDetailRes getFairytaleDetail(Long fairytaleId) { .map(ParagraphRes::from) .toList(); - return FairytaleDetailRes.of(fairytale, paragraphs); + List vocab = WordEntryRes.listOf( + wordEntryRepository.findByFairytaleIdOrderByPageNoAsc(fairytaleId)); + + return FairytaleDetailRes.of(fairytale, paragraphs, vocab); } } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java index bce7cae..4021602 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/web/dto/FairytaleDetailRes.java @@ -1,6 +1,7 @@ package com.capstone.kkumteul.domain.fairytale.web.dto; import com.capstone.kkumteul.domain.fairytale.entity.Fairytale; +import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import java.util.List; @@ -11,9 +12,10 @@ public record FairytaleDetailRes( String morality, String charSpecies, String background, - List paragraphs + List paragraphs, + List vocab ) { - public static FairytaleDetailRes of(Fairytale fairytale, List paragraphs) { + public static FairytaleDetailRes of(Fairytale fairytale, List paragraphs, List vocab) { return new FairytaleDetailRes( fairytale.getId(), fairytale.getTitle(), @@ -21,7 +23,8 @@ public static FairytaleDetailRes of(Fairytale fairytale, List para fairytale.getMorality().name(), fairytale.getCharSpecies().name(), fairytale.getBackground().name(), - paragraphs + paragraphs, + vocab ); } } diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java index d01de3a..c88e0a7 100644 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java +++ b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabService.java @@ -2,12 +2,11 @@ import com.capstone.kkumteul.domain.kafka.dto.VocabExtractedMessage; import com.capstone.kkumteul.domain.vocab.service.dto.VocabExtractionResult; -import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import java.util.List; /** - * 단어장 추출/조회 서비스. + * 단어장 추출 서비스. 조회는 동화 상세(FairytaleService)에 포함됨. */ public interface VocabService { @@ -26,12 +25,4 @@ public interface VocabService { * word가 null/blank이면 NO_DIFFICULT_WORD 처리. 모든 종착 분기에서 markVocabDone 호출 (SSE guarantee). */ VocabExtractionResult processExtractedWord(VocabExtractedMessage message); - - /** - * 본인 동화의 누적 단어장 조회. 페이지 번호 오름차순. - * - * @param userId 요청자 (소유권 검증용) - * @param fairytaleId 동화 ID - */ - List getVocab(Long userId, Long fairytaleId); } diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java index 1a48517..65764f3 100644 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/vocab/service/VocabServiceImpl.java @@ -8,7 +8,6 @@ import com.capstone.kkumteul.domain.vocab.entity.WordEntry; import com.capstone.kkumteul.domain.vocab.repository.WordEntryRepository; import com.capstone.kkumteul.domain.vocab.service.dto.VocabExtractionResult; -import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; import com.capstone.kkumteul.global.client.VocabExtractClient; import com.capstone.kkumteul.global.client.dto.VocabExtractResponse; import lombok.RequiredArgsConstructor; @@ -131,16 +130,4 @@ public VocabExtractionResult processExtractedWord(VocabExtractedMessage message) } } - /** - * 동화 누적 단어장 조회. - * 동화 존재 여부만 확인 후 페이지 순서로 반환. - * 동화 상세 조회와 동일한 정책 — 로그인한 사용자 모두 접근 가능. - */ - @Override - public List getVocab(Long userId, Long fairytaleId) { - if (!fairytaleRepository.existsById(fairytaleId)) { - throw new FairytaleNotFoundException(); - } - return WordEntryRes.listOf(wordEntryRepository.findByFairytaleIdOrderByPageNoAsc(fairytaleId)); - } } diff --git a/src/main/java/com/capstone/kkumteul/domain/vocab/web/controller/VocabController.java b/src/main/java/com/capstone/kkumteul/domain/vocab/web/controller/VocabController.java deleted file mode 100644 index c007ed4..0000000 --- a/src/main/java/com/capstone/kkumteul/domain/vocab/web/controller/VocabController.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.capstone.kkumteul.domain.vocab.web.controller; - -import com.capstone.kkumteul.domain.user.entity.User; -import com.capstone.kkumteul.domain.vocab.service.VocabService; -import com.capstone.kkumteul.domain.vocab.web.dto.WordEntryRes; -import com.capstone.kkumteul.global.response.SuccessResponse; -import com.capstone.kkumteul.global.security.AuthUser; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/fairytales") -public class VocabController { - - private final VocabService vocabService; - - /** 본인 동화의 누적 단어장 조회 (페이지 순). */ - @GetMapping("/{fairytaleId}/vocab") - public ResponseEntity>> getVocab( - @AuthUser User user, - @PathVariable Long fairytaleId - ) { - List entries = vocabService.getVocab(user.getId(), fairytaleId); - return ResponseEntity.status(HttpStatus.OK) - .body(SuccessResponse.ok(entries)); - } -} From ad45f87ea6f2226eabc2ed75ee44bbcccbf4000c Mon Sep 17 00:00:00 2001 From: zzuhannn Date: Sun, 17 May 2026 15:02:36 +0900 Subject: [PATCH 3/3] =?UTF-8?q?refactor:=20shared=20=EB=8F=99=ED=99=94=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20API=20=EC=84=AC=20=EB=B6=84=EB=A5=98=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fairytale/repository/FairytaleRepository.java | 6 +++--- .../kkumteul/domain/fairytale/service/FairytaleService.java | 2 +- .../domain/fairytale/service/FairytaleServiceImpl.java | 4 ++-- .../fairytale/web/controller/FairytaleController.java | 3 +-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java index 11b3b8a..eae3a05 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/repository/FairytaleRepository.java @@ -19,9 +19,9 @@ public interface FairytaleRepository extends JpaRepository { countQuery = "SELECT COUNT(f) FROM Fairytale f WHERE f.user.id = :userId AND f.background IN :backgrounds") Page findByUserIdAndBackgroundIn(@Param("userId") Long userId, @Param("backgrounds") List backgrounds, Pageable pageable); - @Query(value = "SELECT f FROM Fairytale f JOIN FETCH f.user WHERE f.user.id <> :userId AND f.background IN :backgrounds", - countQuery = "SELECT COUNT(f) FROM Fairytale f WHERE f.user.id <> :userId AND f.background IN :backgrounds") - Page findByUserIdNotAndBackgroundIn(@Param("userId") Long userId, @Param("backgrounds") List backgrounds, Pageable pageable); + @Query(value = "SELECT f FROM Fairytale f JOIN FETCH f.user WHERE f.user.id <> :userId ORDER BY f.createdAt DESC", + countQuery = "SELECT COUNT(f) FROM Fairytale f WHERE f.user.id <> :userId") + Page findSharedFairytales(@Param("userId") Long userId, Pageable pageable); @Query("SELECT f FROM Fairytale f JOIN FETCH f.user WHERE f.id = :id") Optional findByIdWithUser(@Param("id") Long id); diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java index a9099e7..b237d70 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleService.java @@ -10,7 +10,7 @@ public interface FairytaleService { Page getMyFairytales(Long userId, Island island, Pageable pageable); - Page getSharedFairytales(Long userId, Island island, Pageable pageable); + Page getSharedFairytales(Long userId, Pageable pageable); FairytaleDetailRes getFairytaleDetail(Long fairytaleId); } diff --git a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java index 27c91f0..1024dcb 100644 --- a/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java +++ b/src/main/java/com/capstone/kkumteul/domain/fairytale/service/FairytaleServiceImpl.java @@ -34,8 +34,8 @@ public Page getMyFairytales(Long userId, Island island, Pageab } @Override - public Page getSharedFairytales(Long userId, Island island, Pageable pageable) { - return fairytaleRepository.findByUserIdNotAndBackgroundIn(userId, island.getBackgrounds(), pageable) + public Page getSharedFairytales(Long userId, Pageable pageable) { + return fairytaleRepository.findSharedFairytales(userId, pageable) .map(FairytaleListRes::from); } 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 87195a3..9805c52 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 @@ -55,10 +55,9 @@ public ResponseEntity>> getMyFairytales( @GetMapping("/shared") public ResponseEntity>> getSharedFairytales( @AuthUser User user, - @RequestParam Island island, @PageableDefault(size = 6) Pageable pageable ) { - Page res = fairytaleService.getSharedFairytales(user.getId(), island, pageable); + Page res = fairytaleService.getSharedFairytales(user.getId(), pageable); return ResponseEntity.status(HttpStatus.OK).body(SuccessResponse.ok(res)); }