From 5910157318fa37bbc127b1959530c32d97ff5946 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 14:13:13 +0900 Subject: [PATCH 01/26] =?UTF-8?q?[feat]=20=EC=A0=80=EC=9E=A5=ED=95=9C=20?= =?UTF-8?q?=EC=B1=85=20=EB=98=90=EB=8A=94=20=EC=B0=B8=EC=97=AC=20=EC=A4=91?= =?UTF-8?q?=20=EB=AA=A8=EC=9E=84=EC=9D=98=20=EC=B1=85=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20api=20=ED=8B=80=20=EC=99=84=EC=84=B1=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/BookQueryController.java | 20 ++++++++++--- .../response/BookSelectableListResponse.java | 17 +++++++++++ .../port/in/BookSelectableListUseCase.java | 8 +++++ .../port/in/dto/BookSelectableType.java | 27 +++++++++++++++++ .../service/BookSelectableListService.java | 29 +++++++++++++++++++ 5 files changed, 97 insertions(+), 4 deletions(-) create mode 100644 src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java create mode 100644 src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java create mode 100644 src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableType.java create mode 100644 src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index 47c529960..463252764 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -4,13 +4,12 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Pattern; -import konkuk.thip.book.adapter.in.web.response.BookDetailSearchResponse; -import konkuk.thip.book.adapter.in.web.response.BookMostSearchResponse; -import konkuk.thip.book.adapter.in.web.response.BookRecruitingRoomsResponse; -import konkuk.thip.book.adapter.in.web.response.BookSearchListResponse; +import konkuk.thip.book.application.port.in.dto.BookSelectableType; +import konkuk.thip.book.adapter.in.web.response.*; import konkuk.thip.book.application.port.in.BookMostSearchUseCase; import konkuk.thip.book.application.port.in.BookRecruitingRoomsUseCase; import konkuk.thip.book.application.port.in.BookSearchUseCase; +import konkuk.thip.book.application.port.in.BookSelectableListUseCase; import konkuk.thip.common.dto.BaseResponse; import konkuk.thip.common.security.annotation.UserId; import konkuk.thip.common.swagger.annotation.ExceptionDescription; @@ -32,6 +31,7 @@ public class BookQueryController { private final BookSearchUseCase bookSearchUseCase; private final BookMostSearchUseCase bookMostSearchUseCase; private final BookRecruitingRoomsUseCase bookRecruitingRoomsUseCase; + private final BookSelectableListUseCase bookSelectableListUseCase; @Operation( summary = "책 검색결과 조회", @@ -87,4 +87,16 @@ public BaseResponse showRecruitingRoomsWithBook( return BaseResponse.ok(bookRecruitingRoomsUseCase.getRecruitingRoomsWithBook(isbn, cursor)); } + @Operation( + summary = "저장한 책 또는 참여 중 모임의 책 조회", + description = "저장한 책 또는 참여 중인 모임의 책을 조회합니다. (방 생성시 책 선택 화면 페이지)" + ) + @GetMapping("/books/selectable-list") + public BaseResponse showSelectableBookList( + @Parameter(description = "저장한 책 또는 참여 중인 모임의 책을 구분하는 필드 (SAVED : 저장한 책 / JOINING : 모임 방의 책)", example = "SAVED") @RequestParam final String type, + @Parameter(hidden = true) @UserId final Long userId + ) { + return BaseResponse.ok(bookSelectableListUseCase.getSelectableBookList(BookSelectableType.from(type), userId)); + } + } diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java new file mode 100644 index 000000000..42fa9c9e1 --- /dev/null +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java @@ -0,0 +1,17 @@ +package konkuk.thip.book.adapter.in.web.response; + +import java.util.List; + +public record BookSelectableListResponse( + List bookList +) { + public record BookDto( + Long bookId, + String bookTitle, + String authorName, + String publisher, + String bookImageUrl, + String isbn + ) { + } +} diff --git a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java new file mode 100644 index 000000000..6ee48c0fa --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java @@ -0,0 +1,8 @@ +package konkuk.thip.book.application.port.in; + +import konkuk.thip.book.application.port.in.dto.BookSelectableType; +import konkuk.thip.book.adapter.in.web.response.BookSelectableListResponse; + +public interface BookSelectableListUseCase { + BookSelectableListResponse getSelectableBookList(BookSelectableType bookSelectableType, Long userId); +} diff --git a/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableType.java b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableType.java new file mode 100644 index 000000000..290e88072 --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableType.java @@ -0,0 +1,27 @@ +package konkuk.thip.book.application.port.in.dto; + +import konkuk.thip.common.exception.InvalidStateException; +import konkuk.thip.common.exception.code.ErrorCode; +import lombok.Getter; + +@Getter +public enum BookSelectableType { + SAVED("SAVED"), // 저장된 책 + JOINING("JOINING") // 참여 중인 모임 방의 책 + ; + + private final String type; + + BookSelectableType(String type) { + this.type = type; + } + + public static BookSelectableType from(String type) { + for (BookSelectableType bookSelectableType : BookSelectableType.values()) { + if (bookSelectableType.type.equals(type)) { + return bookSelectableType; + } + } + throw new InvalidStateException(ErrorCode.API_INVALID_PARAM, new IllegalArgumentException("타입은 SAVED 또는 JOINING이어야 합니다. 현재 타입: " + type)); + } +} diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java new file mode 100644 index 000000000..ac6814fcd --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -0,0 +1,29 @@ +package konkuk.thip.book.application.service; + +import konkuk.thip.book.application.port.in.dto.BookSelectableType; +import konkuk.thip.book.adapter.in.web.response.BookSelectableListResponse; +import konkuk.thip.book.application.port.in.BookSelectableListUseCase; +import konkuk.thip.book.domain.Book; + +import java.util.List; + +import static konkuk.thip.book.application.port.in.dto.BookSelectableType.*; + +public class BookSelectableListService implements BookSelectableListUseCase { + @Override + public BookSelectableListResponse getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { + List bookList = switch(bookSelectableType) { + case SAVED -> { + // 저장된 책 목록을 조회하는 로직 + // 예시: return bookRepository.findSavedBooksByUserId(userId); + + } + case JOINING -> { + // 참여 중인 모임 방의 책 목록을 조회하는 로직 + // 예시: return bookRepository.findJoiningBooksByUserId(userId); + + } + + } + } +} From 7945fe07af9f0c28120ce61c50b6c08e3d14b403 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 17:15:15 +0900 Subject: [PATCH 02/26] =?UTF-8?q?[refactor]=20SavedBooks=20=EC=9D=BC?= =?UTF-8?q?=EA=B8=89=20=EC=BB=AC=EB=A0=89=EC=85=98=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?->=20=EC=B1=85=20=EC=A0=80=EC=9E=A5=20=EC=83=81=ED=83=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/BookSavedService.java | 83 ++++++++++--------- .../konkuk/thip/book/domain/SavedBooks.java | 41 --------- .../SavedQueryPersistenceAdapter.java | 12 +-- .../repository/SavedBookJpaRepository.java | 5 +- .../application/port/out/SavedQueryPort.java | 6 +- 5 files changed, 54 insertions(+), 93 deletions(-) delete mode 100644 src/main/java/konkuk/thip/book/domain/SavedBooks.java diff --git a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java index d99f550ac..7208d1a22 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java @@ -8,14 +8,13 @@ import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.BusinessException; -import konkuk.thip.common.exception.EntityNotFoundException; +import konkuk.thip.common.exception.InvalidStateException; import konkuk.thip.saved.application.port.out.SavedCommandPort; import konkuk.thip.saved.application.port.out.SavedQueryPort; -import konkuk.thip.book.domain.SavedBooks; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import static konkuk.thip.common.exception.code.ErrorCode.BOOK_NOT_SAVED_DB_CANNOT_DELETE; +import static konkuk.thip.common.exception.code.ErrorCode.*; @Service @RequiredArgsConstructor @@ -28,52 +27,54 @@ public class BookSavedService implements BookSavedUseCase { @Override @Transactional - public BookIsSavedResult changeSavedBook(String isbn, boolean isSave, Long userId) { + public BookIsSavedResult changeSavedBook(String isbn, boolean isSaveRequest, Long userId) { - Book book; + // Book 조회 시도 + Book book = bookCommandPort.findByIsbn(isbn) + .orElseGet(() -> { + if (!isSaveRequest) { // 삭제 요청인데 책이 없으면 저장하지 않은 책이므로 예외 처리 + throw new BusinessException(BOOK_NOT_SAVED_DB_CANNOT_DELETE); + } + return registerBookByIsbn(isbn); + }); - try { - // Book 조회 시도 - book = bookCommandPort.getByIsbnOrThrow(isbn); - } catch (EntityNotFoundException e) { - // 책이 DB에 없을 때 처리 + boolean alreadySaved = savedQueryPort.existsByUserIdAndBookId(userId, book.getId()); + validateSaveBookAction(isSaveRequest, alreadySaved); - if (!isSave) { - // 삭제 요청인데 책이 없으면 저장하지 않은 책이므로 예외 처리 - throw new BusinessException(BOOK_NOT_SAVED_DB_CANNOT_DELETE); - } - - // 저장 요청이면 네이버 API로 책 정보 조회 후 저장 - NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); - Book newBook = Book.withoutId( - naverResult.title(), - naverResult.isbn(), - naverResult.author(), - false, - naverResult.publisher(), - naverResult.imageUrl(), - null, - naverResult.description()); - - Long newBookId = bookCommandPort.save(newBook); - - book = bookCommandPort.findById(newBookId); - } - - // 유저가 저장한 책 목록 조회 - SavedBooks savedBooks = savedQueryPort.findSavedBooksByUserId(userId); - - if (isSave) { - // 저장 요청 시 이미 저장되어 있으면 예외 발생 - savedBooks.validateNotAlreadySaved(book); + if (isSaveRequest) { savedCommandPort.saveBook(userId, book.getId()); } else { - // 삭제 요청 시 저장되어 있지 않으면 예외 발생 - savedBooks.validateCanDelete(book); savedCommandPort.deleteBook(userId, book.getId()); } - return BookIsSavedResult.of(isbn, isSave); + return BookIsSavedResult.of(isbn, isSaveRequest); + } + + private Book registerBookByIsbn(String isbn) { + // 저장 요청이면 네이버 API로 책 정보 조회 후 저장 + NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); + Book newBook = Book.withoutId( + naverResult.title(), + naverResult.isbn(), + naverResult.author(), + false, + naverResult.publisher(), + naverResult.imageUrl(), + null, + naverResult.description()); + + Long savedBookId = bookCommandPort.save(newBook); + return bookCommandPort.findById(savedBookId); + } + + private void validateSaveBookAction(boolean isSaveRequest, boolean alreadySaved) { + if (isSaveRequest && alreadySaved) { + // 이미 저장되어 있는 책을 다시 저장하려는 경우 예외 처리 + throw new InvalidStateException(BOOK_ALREADY_SAVED); + } else if (!isSaveRequest && !alreadySaved) { + // 저장되지 않은 책을 삭제하려는 경우 예외 처리 + throw new InvalidStateException(BOOK_NOT_SAVED_CANNOT_DELETE); + } } } \ No newline at end of file diff --git a/src/main/java/konkuk/thip/book/domain/SavedBooks.java b/src/main/java/konkuk/thip/book/domain/SavedBooks.java deleted file mode 100644 index 0871a4408..000000000 --- a/src/main/java/konkuk/thip/book/domain/SavedBooks.java +++ /dev/null @@ -1,41 +0,0 @@ -package konkuk.thip.book.domain; - -import konkuk.thip.common.exception.InvalidStateException; -import lombok.Getter; - -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static konkuk.thip.common.exception.code.ErrorCode.*; - -@Getter -public class SavedBooks { - private final Set books; - - public SavedBooks(List books) { - // Set으로 변환해서 중복 여부 검사 - Set bookSet = new HashSet<>(books); - if (bookSet.size() != books.size()) { - throw new InvalidStateException(DUPLICATED_BOOKS_IN_COLLECTION); - } - // 불변 Set으로 저장 (Collections.unmodifiableSet 사용) - this.books = Collections.unmodifiableSet(bookSet); - } - - // 중복 저장 검증 - public void validateNotAlreadySaved(Book book) { - if (books.contains(book)) { - throw new InvalidStateException(BOOK_ALREADY_SAVED); - } - } - - // 삭제 가능 여부 검증 - public void validateCanDelete(Book book) { - if (!books.contains(book)) { - throw new InvalidStateException(BOOK_NOT_SAVED_CANNOT_DELETE); - } - } -} - diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java index 5ad5f28d6..c990a1695 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java @@ -14,7 +14,6 @@ import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.saved.application.port.out.SavedQueryPort; -import konkuk.thip.book.domain.SavedBooks; import konkuk.thip.saved.application.port.out.dto.FeedIdAndTagProjection; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; @@ -45,18 +44,15 @@ public boolean existsByUserIdAndBookId(Long userId, Long bookId) { } @Override - public SavedBooks findSavedBooksByUserId(Long userId) { - + public List findSavedBooksByUserId(Long userId) { UserJpaEntity user = userJpaRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - List savedBookEntities = savedBookJpaRepository.findByUserJpaEntity_UserId(user.getUserId()); - // SavedBookJpaEntity에서 BookJpaEntity를 꺼내 도메인 Book으로 변환 - List books = savedBookEntities.stream() + List savedBookEntities = savedBookJpaRepository.findByUserId(user.getUserId()); + + return savedBookEntities.stream() .map(entity -> bookMapper.toDomainEntity(entity.getBookJpaEntity())) .collect(Collectors.toList()); - - return new SavedBooks(books); } @Override diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java index 05bbe9a31..4f9ff5b2c 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java +++ b/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java @@ -2,11 +2,14 @@ import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; public interface SavedBookJpaRepository extends JpaRepository { boolean existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(Long userId, Long bookId); void deleteByUserJpaEntity_UserIdAndBookJpaEntity_BookId(Long userId, Long bookId); - List findByUserJpaEntity_UserId(Long userId); + + @Query("SELECT s FROM SavedBookJpaEntity s WHERE s.userJpaEntity.userId = :userId") + List findByUserId(Long userId); } \ No newline at end of file diff --git a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java b/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java index e43fd246f..a0503dfc6 100644 --- a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java +++ b/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java @@ -1,13 +1,15 @@ package konkuk.thip.saved.application.port.out; -import konkuk.thip.book.domain.SavedBooks; +import konkuk.thip.book.domain.Book; import konkuk.thip.feed.domain.SavedFeeds; +import java.util.List; import java.util.Set; public interface SavedQueryPort { boolean existsByUserIdAndBookId(Long userId, Long bookId); - SavedBooks findSavedBooksByUserId(Long userId); + + List findSavedBooksByUserId(Long userId); SavedFeeds findSavedFeedsByUserId(Long userId); Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId); From cda7aabb8b80fd47e2e0c058a8e725f0a4774493 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 17:41:09 +0900 Subject: [PATCH 03/26] =?UTF-8?q?[refactor]=20=EC=95=88=EC=93=B0=EB=8A=94?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=9C=EA=B1=B0=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../in/web/SavedBookCommandController.java | 10 ------- .../in/web/SavedBookQueryController.java | 10 ------- .../in/web/SavedFeedCommandController.java | 4 --- .../in/web/SavedFeedQueryController.java | 4 --- .../in/web/request/UserSignupRequest.java | 7 ----- .../in/web/response/DummyResponse.java | 7 ----- .../adapter/out/mapper/SavedBookMapper.java | 29 ------------------- .../adapter/out/mapper/SavedFeedMapper.java | 29 ------------------- 8 files changed, 100 deletions(-) delete mode 100644 src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookCommandController.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookQueryController.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedCommandController.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedQueryController.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/in/web/request/UserSignupRequest.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/in/web/response/DummyResponse.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedBookMapper.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedFeedMapper.java diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookCommandController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookCommandController.java deleted file mode 100644 index 08e68a425..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookCommandController.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequiredArgsConstructor -public class SavedBookCommandController { - -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookQueryController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookQueryController.java deleted file mode 100644 index 1f8c76c18..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedBookQueryController.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequiredArgsConstructor -public class SavedBookQueryController { - -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedCommandController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedCommandController.java deleted file mode 100644 index 0e2c5fd7d..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedCommandController.java +++ /dev/null @@ -1,4 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -public class SavedFeedCommandController { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedQueryController.java b/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedQueryController.java deleted file mode 100644 index 2cab73285..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/SavedFeedQueryController.java +++ /dev/null @@ -1,4 +0,0 @@ -package konkuk.thip.saved.adapter.in.web; - -public class SavedFeedQueryController { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/request/UserSignupRequest.java b/src/main/java/konkuk/thip/saved/adapter/in/web/request/UserSignupRequest.java deleted file mode 100644 index 51b7aa5e6..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/request/UserSignupRequest.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.saved.adapter.in.web.request; - -import lombok.Getter; - -@Getter -public class UserSignupRequest { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/in/web/response/DummyResponse.java b/src/main/java/konkuk/thip/saved/adapter/in/web/response/DummyResponse.java deleted file mode 100644 index c4b9cd168..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/in/web/response/DummyResponse.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.saved.adapter.in.web.response; - -import lombok.Getter; - -@Getter -public class DummyResponse { -} diff --git a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedBookMapper.java b/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedBookMapper.java deleted file mode 100644 index 70e7c7bdc..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedBookMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package konkuk.thip.saved.adapter.out.mapper; - -import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.domain.SavedBook; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import org.springframework.stereotype.Component; - -@Component -public class SavedBookMapper { - - public SavedBookJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, BookJpaEntity bookJpaEntity) { - return SavedBookJpaEntity.builder() - .userJpaEntity(userJpaEntity) - .bookJpaEntity(bookJpaEntity) - .build(); - } - - public SavedBook toDomainEntity(SavedBookJpaEntity savedBookJpaEntity) { - return SavedBook.builder() - .id(savedBookJpaEntity.getSavedId()) - .userId(savedBookJpaEntity.getUserJpaEntity().getUserId()) - .bookId(savedBookJpaEntity.getBookJpaEntity().getBookId()) - .createdAt(savedBookJpaEntity.getCreatedAt()) - .modifiedAt(savedBookJpaEntity.getModifiedAt()) - .status(savedBookJpaEntity.getStatus()) - .build(); - } -} diff --git a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedFeedMapper.java b/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedFeedMapper.java deleted file mode 100644 index 7556c7c0d..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/mapper/SavedFeedMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package konkuk.thip.saved.adapter.out.mapper; - -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.domain.SavedFeed; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import org.springframework.stereotype.Component; - -@Component -public class SavedFeedMapper { - - public SavedFeedJpaEntity toJpaEntity(UserJpaEntity userJpaEntity, FeedJpaEntity feedJpaEntity) { - return SavedFeedJpaEntity.builder() - .userJpaEntity(userJpaEntity) - .feedJpaEntity(feedJpaEntity) - .build(); - } - - public SavedFeed toDomainEntity(SavedFeedJpaEntity savedFeedJpaEntity) { - return SavedFeed.builder() - .id(savedFeedJpaEntity.getSavedId()) - .userId(savedFeedJpaEntity.getUserJpaEntity().getUserId()) - .feedId(savedFeedJpaEntity.getFeedJpaEntity().getPostId()) - .createdAt(savedFeedJpaEntity.getCreatedAt()) - .modifiedAt(savedFeedJpaEntity.getModifiedAt()) - .status(savedFeedJpaEntity.getStatus()) - .build(); - } -} From a534943f21e3041a6fd22a53821dcfb20c315fc7 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 17:42:09 +0900 Subject: [PATCH 04/26] =?UTF-8?q?[refactor]=20SavedBook=EC=9D=84=20Book=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20->?= =?UTF-8?q?=20=EC=A0=80=EC=9E=A5=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=ED=8F=AC=ED=95=A8=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/out/jpa/SavedBookJpaEntity.java | 3 +- .../BookCommandPersistenceAdapter.java | 29 ++++++++++++++-- .../BookQueryPersistenceAdapter.java | 34 +++++++++++++++++++ .../repository/SavedBookJpaRepository.java | 4 +-- .../application/port/out/BookCommandPort.java | 4 +++ .../application/port/out/BookQueryPort.java | 10 ++++++ .../application/service/BookSavedService.java | 12 +++---- .../service/BookSearchService.java | 8 ++--- .../service/BookSelectableListService.java | 24 ++++++------- .../SavedCommandPersistenceAdapter.java | 31 ++--------------- .../SavedQueryPersistenceAdapter.java | 23 ------------- .../port/out/SavedCommandPort.java | 2 -- .../application/port/out/SavedQueryPort.java | 4 --- .../in/web/BookChangeSavedControllerTest.java | 4 +-- .../web/BookDetailSearchControllerTest.java | 4 +-- 15 files changed, 105 insertions(+), 91 deletions(-) rename src/main/java/konkuk/thip/{saved => book}/adapter/out/jpa/SavedBookJpaEntity.java (86%) rename src/main/java/konkuk/thip/{saved => book}/adapter/out/persistence/repository/SavedBookJpaRepository.java (81%) diff --git a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedBookJpaEntity.java b/src/main/java/konkuk/thip/book/adapter/out/jpa/SavedBookJpaEntity.java similarity index 86% rename from src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedBookJpaEntity.java rename to src/main/java/konkuk/thip/book/adapter/out/jpa/SavedBookJpaEntity.java index 449786262..b910d2441 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedBookJpaEntity.java +++ b/src/main/java/konkuk/thip/book/adapter/out/jpa/SavedBookJpaEntity.java @@ -1,7 +1,6 @@ -package konkuk.thip.saved.adapter.out.jpa; +package konkuk.thip.book.adapter.out.jpa; import jakarta.persistence.*; -import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.common.entity.BaseJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import lombok.*; diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java index 3c8ef0358..a8907c2c0 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java @@ -7,20 +7,25 @@ import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.util.Optional; -import static konkuk.thip.common.exception.code.ErrorCode.BOOK_NOT_FOUND; -import static konkuk.thip.common.exception.code.ErrorCode.ROOM_NOT_FOUND; +import static konkuk.thip.common.exception.code.ErrorCode.*; @Repository @RequiredArgsConstructor public class BookCommandPersistenceAdapter implements BookCommandPort { + private final UserJpaRepository userJpaRepository; private final RoomJpaRepository roomJpaRepository; private final BookJpaRepository bookJpaRepository; + private final SavedBookJpaRepository savedBookJpaRepository; private final BookMapper bookMapper; @Override @@ -61,4 +66,24 @@ public Book findBookByRoomId(Long roomId) { ).getBookJpaEntity(); return bookMapper.toDomainEntity(bookJpaEntity); } + + // 사용자가 책을 저장 + @Override + public void saveSavedBook(Long userId, Long bookId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + BookJpaEntity book = bookJpaRepository.findById(bookId) + .orElseThrow(() -> new EntityNotFoundException(BOOK_NOT_FOUND)); + SavedBookJpaEntity entity = SavedBookJpaEntity.builder() + .userJpaEntity(user) + .bookJpaEntity(book) + .build(); + savedBookJpaRepository.save(entity); + } + + // 사용자가 저장한 책을 삭제 + @Override + public void deleteSavedBook(Long userId, Long bookId) { + savedBookJpaRepository.deleteByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); + } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java index 87e41c31b..664ccb4a3 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java @@ -1,16 +1,50 @@ package konkuk.thip.book.adapter.out.persistence; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; import konkuk.thip.book.adapter.out.mapper.BookMapper; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.book.application.port.out.BookQueryPort; +import konkuk.thip.book.domain.Book; +import konkuk.thip.common.exception.EntityNotFoundException; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.stream.Collectors; + +import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; + @Repository @RequiredArgsConstructor public class BookQueryPersistenceAdapter implements BookQueryPort { + private final UserJpaRepository userJpaRepository; private final BookJpaRepository bookJpaRepository; + private final SavedBookJpaRepository savedBookJpaRepository; private final BookMapper bookMapper; + @Override + public boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId) { + return savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); + } + + @Override + public List findSavedBooksByUserId(Long userId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + + List savedBookEntities = savedBookJpaRepository.findByUserId(user.getUserId()); + + return savedBookEntities.stream() + .map(entity -> bookMapper.toDomainEntity(entity.getBookJpaEntity())) + .collect(Collectors.toList()); + } + + @Override + public List findJoiningRoomsBooksByUserId(Long userId) { + return bookJpaRepository.; + } } diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java similarity index 81% rename from src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java rename to src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java index 4f9ff5b2c..dcd08f7dc 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedBookJpaRepository.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java @@ -1,6 +1,6 @@ -package konkuk.thip.saved.adapter.out.persistence.repository; +package konkuk.thip.book.adapter.out.persistence.repository; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java index f6343f700..c3b85b1a8 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookCommandPort.java @@ -23,4 +23,8 @@ default Book getByIsbnOrThrow(String isbn){ void updateForPageCount(Book book); Book findBookByRoomId(Long roomId); + + void saveSavedBook(Long userId, Long bookId); + void deleteSavedBook(Long userId, Long bookId); + } diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java index 186919761..bb9e0ab00 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java @@ -1,4 +1,14 @@ package konkuk.thip.book.application.port.out; +import konkuk.thip.book.domain.Book; + +import java.util.List; + public interface BookQueryPort { + + boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId); + + List findSavedBooksByUserId(Long userId); + + List findJoiningRoomsBooksByUserId(Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java index 7208d1a22..2c8e14935 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java @@ -6,11 +6,10 @@ import konkuk.thip.book.application.port.in.dto.BookIsSavedResult; import konkuk.thip.book.application.port.out.BookApiQueryPort; import konkuk.thip.book.application.port.out.BookCommandPort; +import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.BusinessException; import konkuk.thip.common.exception.InvalidStateException; -import konkuk.thip.saved.application.port.out.SavedCommandPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -22,8 +21,7 @@ public class BookSavedService implements BookSavedUseCase { private final BookApiQueryPort bookApiQueryPort; private final BookCommandPort bookCommandPort; - private final SavedCommandPort savedCommandPort; - private final SavedQueryPort savedQueryPort; + private final BookQueryPort bookQueryPort; @Override @Transactional @@ -38,13 +36,13 @@ public BookIsSavedResult changeSavedBook(String isbn, boolean isSaveRequest, Lon return registerBookByIsbn(isbn); }); - boolean alreadySaved = savedQueryPort.existsByUserIdAndBookId(userId, book.getId()); + boolean alreadySaved = bookQueryPort.existsSavedBookByUserIdAndBookId(userId, book.getId()); validateSaveBookAction(isSaveRequest, alreadySaved); if (isSaveRequest) { - savedCommandPort.saveBook(userId, book.getId()); + bookCommandPort.saveSavedBook(userId, book.getId()); } else { - savedCommandPort.deleteBook(userId, book.getId()); + bookCommandPort.deleteSavedBook(userId, book.getId()); } return BookIsSavedResult.of(isbn, isSaveRequest); diff --git a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java index 0609fb188..8b1c2e9c9 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java @@ -4,8 +4,9 @@ import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; import konkuk.thip.book.application.port.in.BookSearchUseCase; import konkuk.thip.book.application.port.in.dto.BookDetailSearchResult; -import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.application.port.out.BookApiQueryPort; +import konkuk.thip.book.application.port.out.BookCommandPort; +import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.application.port.out.BookRedisCommandPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.BusinessException; @@ -14,7 +15,6 @@ import konkuk.thip.recentSearch.application.port.out.RecentSearchCommandPort; import konkuk.thip.recentSearch.domain.RecentSearch; import konkuk.thip.room.application.port.out.RoomQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import konkuk.thip.user.application.port.out.UserCommandPort; import konkuk.thip.user.application.port.out.UserQueryPort; import konkuk.thip.user.domain.User; @@ -38,7 +38,7 @@ public class BookSearchService implements BookSearchUseCase { private final RoomQueryPort roomQueryPort; private final UserQueryPort userQueryPort; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; + private final BookQueryPort bookQueryPort; private final RecentSearchCommandPort recentSearchCommandPort; private final BookCommandPort bookCommandPort; private final UserCommandPort userCommandPort; @@ -109,7 +109,7 @@ public BookDetailSearchResult searchDetailBooks(String isbn,Long userId) { // 이책에 읽기 참여중인 사용자 수 int readCount = getReadCount(book); // 사용자의 해당 책 저장 여부 - boolean isSaved = savedQueryPort.existsByUserIdAndBookId(user.getId(), book.getId()); + boolean isSaved = bookQueryPort.existsSavedBookByUserIdAndBookId(user.getId(), book.getId()); return BookDetailSearchResult.of( naverDetailBookParseResult, diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index ac6814fcd..ced424b2f 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -3,27 +3,27 @@ import konkuk.thip.book.application.port.in.dto.BookSelectableType; import konkuk.thip.book.adapter.in.web.response.BookSelectableListResponse; import konkuk.thip.book.application.port.in.BookSelectableListUseCase; +import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.domain.Book; +import konkuk.thip.saved.application.port.out.SavedQueryPort; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; import java.util.List; import static konkuk.thip.book.application.port.in.dto.BookSelectableType.*; +@Service +@RequiredArgsConstructor public class BookSelectableListService implements BookSelectableListUseCase { + + private final BookQueryPort bookQueryPort; + @Override public BookSelectableListResponse getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { List bookList = switch(bookSelectableType) { - case SAVED -> { - // 저장된 책 목록을 조회하는 로직 - // 예시: return bookRepository.findSavedBooksByUserId(userId); - - } - case JOINING -> { - // 참여 중인 모임 방의 책 목록을 조회하는 로직 - // 예시: return bookRepository.findJoiningBooksByUserId(userId); - - } - - } + case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); + case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); + }; } } diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java index cc61dd5cf..f9c5e1b7c 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java @@ -1,15 +1,9 @@ package konkuk.thip.saved.adapter.out.persistence; -import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; -import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.mapper.SavedBookMapper; -import konkuk.thip.saved.adapter.out.mapper.SavedFeedMapper; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.saved.application.port.out.SavedCommandPort; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; @@ -17,37 +11,16 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import static konkuk.thip.common.exception.code.ErrorCode.*; +import static konkuk.thip.common.exception.code.ErrorCode.FEED_NOT_FOUND; +import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; @Repository @RequiredArgsConstructor public class SavedCommandPersistenceAdapter implements SavedCommandPort { private final UserJpaRepository userJpaRepository; - private final BookJpaRepository bookJpaRepository; private final FeedJpaRepository feedJpaRepository; - private final SavedBookJpaRepository savedBookJpaRepository; private final SavedFeedJpaRepository savedFeedJpaRepository; - private final SavedBookMapper savedBookMapper; - private final SavedFeedMapper savedFeedMapper; - - @Override - public void saveBook(Long userId, Long bookId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - BookJpaEntity book = bookJpaRepository.findById(bookId) - .orElseThrow(() -> new EntityNotFoundException(BOOK_NOT_FOUND)); - SavedBookJpaEntity entity = SavedBookJpaEntity.builder() - .userJpaEntity(user) - .bookJpaEntity(book) - .build(); - savedBookJpaRepository.save(entity); - } - - @Override - public void deleteBook(Long userId, Long bookId) { - savedBookJpaRepository.deleteByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); - } @Override public void saveFeed(Long userId, Long feedId) { diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java index c990a1695..d8bed4bcd 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java @@ -1,7 +1,5 @@ package konkuk.thip.saved.adapter.out.persistence; -import konkuk.thip.book.adapter.out.mapper.BookMapper; -import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; @@ -9,9 +7,7 @@ import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; import konkuk.thip.feed.domain.Feed; import konkuk.thip.feed.domain.SavedFeeds; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.saved.application.port.out.SavedQueryPort; import konkuk.thip.saved.application.port.out.dto.FeedIdAndTagProjection; @@ -31,30 +27,11 @@ @RequiredArgsConstructor public class SavedQueryPersistenceAdapter implements SavedQueryPort { - private final SavedBookJpaRepository savedBookJpaRepository; private final SavedFeedJpaRepository savedFeedJpaRepository; private final UserJpaRepository userJpaRepository; - private final BookMapper bookMapper; private final FeedMapper feedMapper; private final FeedTagJpaRepository feedTagJpaRepository; - @Override - public boolean existsByUserIdAndBookId(Long userId, Long bookId) { - return savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); - } - - @Override - public List findSavedBooksByUserId(Long userId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - - List savedBookEntities = savedBookJpaRepository.findByUserId(user.getUserId()); - - return savedBookEntities.stream() - .map(entity -> bookMapper.toDomainEntity(entity.getBookJpaEntity())) - .collect(Collectors.toList()); - } - @Override public SavedFeeds findSavedFeedsByUserId(Long userId) { UserJpaEntity user = userJpaRepository.findById(userId) diff --git a/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java b/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java index 21e26934f..e1d3b512c 100644 --- a/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java +++ b/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java @@ -2,8 +2,6 @@ public interface SavedCommandPort { - void saveBook(Long userId, Long bookId); - void deleteBook(Long userId, Long bookId); void saveFeed(Long userId, Long feedId); void deleteFeed(Long userId, Long feedId); } diff --git a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java b/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java index a0503dfc6..0ae5574fe 100644 --- a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java +++ b/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java @@ -1,15 +1,11 @@ package konkuk.thip.saved.application.port.out; -import konkuk.thip.book.domain.Book; import konkuk.thip.feed.domain.SavedFeeds; -import java.util.List; import java.util.Set; public interface SavedQueryPort { - boolean existsByUserIdAndBookId(Long userId, Long bookId); - List findSavedBooksByUserId(Long userId); SavedFeeds findSavedFeedsByUserId(Long userId); Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId); diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java index 8e4058e9c..712a43f81 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java @@ -7,8 +7,8 @@ import konkuk.thip.common.exception.code.ErrorCode; import konkuk.thip.common.security.util.JwtUtil; import konkuk.thip.common.util.TestEntityFactory; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserRole; diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java index fa61ec37e..8bba70a64 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchControllerTest.java @@ -13,8 +13,8 @@ import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; import konkuk.thip.room.adapter.out.persistence.repository.roomparticipant.RoomParticipantJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedBookJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserRole; From 1384b40424a56b01cc7d9c3bcdbcfc03487522a2 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 17:46:48 +0900 Subject: [PATCH 05/26] =?UTF-8?q?[refactor]=20=EC=95=88=EC=93=B0=EB=8A=94?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=A0=9C=EA=B1=B0=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../saved/application/port/in/DummyUseCase.java | 5 ----- .../application/port/in/dto/DummyCommand.java | 10 ---------- .../application/port/in/dto/DummyQuery.java | 9 --------- .../application/port/in/dto/DummyResult.java | 10 ---------- .../application/service/SavedBookService.java | 11 ----------- .../application/service/SavedFeedService.java | 9 --------- .../java/konkuk/thip/saved/domain/SavedBook.java | 16 ---------------- .../java/konkuk/thip/saved/domain/SavedFeed.java | 16 ---------------- 8 files changed, 86 deletions(-) delete mode 100644 src/main/java/konkuk/thip/saved/application/port/in/DummyUseCase.java delete mode 100644 src/main/java/konkuk/thip/saved/application/port/in/dto/DummyCommand.java delete mode 100644 src/main/java/konkuk/thip/saved/application/port/in/dto/DummyQuery.java delete mode 100644 src/main/java/konkuk/thip/saved/application/port/in/dto/DummyResult.java delete mode 100644 src/main/java/konkuk/thip/saved/application/service/SavedBookService.java delete mode 100644 src/main/java/konkuk/thip/saved/application/service/SavedFeedService.java delete mode 100644 src/main/java/konkuk/thip/saved/domain/SavedBook.java delete mode 100644 src/main/java/konkuk/thip/saved/domain/SavedFeed.java diff --git a/src/main/java/konkuk/thip/saved/application/port/in/DummyUseCase.java b/src/main/java/konkuk/thip/saved/application/port/in/DummyUseCase.java deleted file mode 100644 index 72c0dffcf..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/DummyUseCase.java +++ /dev/null @@ -1,5 +0,0 @@ -package konkuk.thip.saved.application.port.in; - -public interface DummyUseCase { - -} diff --git a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyCommand.java b/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyCommand.java deleted file mode 100644 index d41e92a5b..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyCommand.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyCommand { - -} diff --git a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyQuery.java b/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyQuery.java deleted file mode 100644 index b77840db8..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyQuery.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.saved.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Builder -@Getter -public class DummyQuery { -} diff --git a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyResult.java b/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyResult.java deleted file mode 100644 index f8ea5bd63..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/in/dto/DummyResult.java +++ /dev/null @@ -1,10 +0,0 @@ -package konkuk.thip.saved.application.port.in.dto; - -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class DummyResult { - -} diff --git a/src/main/java/konkuk/thip/saved/application/service/SavedBookService.java b/src/main/java/konkuk/thip/saved/application/service/SavedBookService.java deleted file mode 100644 index 7b44ae989..000000000 --- a/src/main/java/konkuk/thip/saved/application/service/SavedBookService.java +++ /dev/null @@ -1,11 +0,0 @@ -package konkuk.thip.saved.application.service; - -import konkuk.thip.saved.application.port.in.DummyUseCase; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class SavedBookService implements DummyUseCase { - -} diff --git a/src/main/java/konkuk/thip/saved/application/service/SavedFeedService.java b/src/main/java/konkuk/thip/saved/application/service/SavedFeedService.java deleted file mode 100644 index 1ecf7ea0f..000000000 --- a/src/main/java/konkuk/thip/saved/application/service/SavedFeedService.java +++ /dev/null @@ -1,9 +0,0 @@ -package konkuk.thip.saved.application.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class SavedFeedService { -} diff --git a/src/main/java/konkuk/thip/saved/domain/SavedBook.java b/src/main/java/konkuk/thip/saved/domain/SavedBook.java deleted file mode 100644 index 180a06f86..000000000 --- a/src/main/java/konkuk/thip/saved/domain/SavedBook.java +++ /dev/null @@ -1,16 +0,0 @@ -package konkuk.thip.saved.domain; - -import konkuk.thip.common.entity.BaseDomainEntity; -import lombok.Getter; -import lombok.experimental.SuperBuilder; - -@Getter -@SuperBuilder -public class SavedBook extends BaseDomainEntity { - - private Long id; - - private Long userId; - - private Long bookId; -} diff --git a/src/main/java/konkuk/thip/saved/domain/SavedFeed.java b/src/main/java/konkuk/thip/saved/domain/SavedFeed.java deleted file mode 100644 index 540b7b454..000000000 --- a/src/main/java/konkuk/thip/saved/domain/SavedFeed.java +++ /dev/null @@ -1,16 +0,0 @@ -package konkuk.thip.saved.domain; - -import konkuk.thip.common.entity.BaseDomainEntity; -import lombok.Getter; -import lombok.experimental.SuperBuilder; - -@Getter -@SuperBuilder -public class SavedFeed extends BaseDomainEntity { - - private Long id; - - private Long userId; - - private Long feedId; -} From 5a2bfb7dfbe56c0df900b892e21bf77fbed91cba Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 19:59:05 +0900 Subject: [PATCH 06/26] =?UTF-8?q?[refactor]=20JPQL=20=EC=BF=BC=EB=A6=AC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/BookCommandPersistenceAdapter.java | 2 +- .../repository/SavedBookJpaRepository.java | 13 +++++++------ .../in/web/BookChangeSavedControllerTest.java | 6 +++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java index a8907c2c0..aef360221 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookCommandPersistenceAdapter.java @@ -84,6 +84,6 @@ public void saveSavedBook(Long userId, Long bookId) { // 사용자가 저장한 책을 삭제 @Override public void deleteSavedBook(Long userId, Long bookId) { - savedBookJpaRepository.deleteByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); + savedBookJpaRepository.deleteByUserIdAndBookId(userId, bookId); } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java index dcd08f7dc..19202fb07 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/SavedBookJpaRepository.java @@ -2,14 +2,15 @@ import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; -import java.util.List; - public interface SavedBookJpaRepository extends JpaRepository { - boolean existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(Long userId, Long bookId); - void deleteByUserJpaEntity_UserIdAndBookJpaEntity_BookId(Long userId, Long bookId); + @Query("SELECT CASE WHEN COUNT(s) > 0 THEN true ELSE false END FROM SavedBookJpaEntity s " + + "WHERE s.userJpaEntity.userId = :userId AND s.bookJpaEntity.bookId = :bookId") + boolean existsByUserIdAndBookId(Long userId, Long bookId); - @Query("SELECT s FROM SavedBookJpaEntity s WHERE s.userJpaEntity.userId = :userId") - List findByUserId(Long userId); + @Modifying + @Query("DELETE FROM SavedBookJpaEntity s WHERE s.userJpaEntity.userId = :userId AND s.bookJpaEntity.bookId = :bookId") + void deleteByUserIdAndBookId(Long userId, Long bookId); } \ No newline at end of file diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java index 712a43f81..5675a01c9 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookChangeSavedControllerTest.java @@ -122,7 +122,7 @@ void saveBook_success() throws Exception { // 실제 저장됐는지 검증 Optional bookJpaEntity = bookJpaRepository.findByIsbn(testIsbn); - boolean exists = savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookJpaEntity.get().getBookId()); + boolean exists = savedBookJpaRepository.existsByUserIdAndBookId(userId, bookJpaEntity.get().getBookId()); assertThat(exists).isTrue(); } @@ -148,7 +148,7 @@ void saveBook_whenBookNotExist_thenSaveAndSuccess() throws Exception { // 실제 저장됐는지 검증 Optional bookJpaEntity = bookJpaRepository.findByIsbn(newIsbn); assertThat(bookJpaEntity).isPresent(); - boolean exists = savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookJpaEntity.get().getBookId()); + boolean exists = savedBookJpaRepository.existsByUserIdAndBookId(userId, bookJpaEntity.get().getBookId()); assertThat(exists).isTrue(); } @@ -203,7 +203,7 @@ void deleteBook_success() throws Exception { .andExpect(jsonPath("$.data.isSaved").value(false)); // 실제 삭제됐는지 검증 - boolean exists = savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(user.getUserId(), book.getBookId()); + boolean exists = savedBookJpaRepository.existsByUserIdAndBookId(user.getUserId(), book.getBookId()); assertThat(exists).isFalse(); } From c2de19a4751d408830d38ed4b517e4207a41b9f3 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 19:59:32 +0900 Subject: [PATCH 07/26] =?UTF-8?q?[feat]=20=EC=A0=80=EC=9E=A5=ED=95=9C=20?= =?UTF-8?q?=EC=B1=85=20=EB=98=90=EB=8A=94=20=EC=B0=B8=EC=97=AC=20=EC=A4=91?= =?UTF-8?q?=20=EB=AA=A8=EC=9E=84=EC=9D=98=20=EC=B1=85=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20api=20=EA=B5=AC=ED=98=84=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/BookQueryController.java | 4 ++- .../response/BookSelectableListResponse.java | 14 ++++------- .../BookQueryPersistenceAdapter.java | 25 +++++++++++-------- .../repository/BookJpaRepository.java | 16 +++++++++++- .../application/mapper/BookQueryMapper.java | 13 ++++++++++ .../port/in/BookSelectableListUseCase.java | 6 +++-- .../application/port/in/dto/BookInfo.java | 11 ++++++++ .../application/port/out/BookQueryPort.java | 6 ++--- .../service/BookSelectableListService.java | 16 ++++++------ 9 files changed, 77 insertions(+), 34 deletions(-) create mode 100644 src/main/java/konkuk/thip/book/application/port/in/dto/BookInfo.java diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index 463252764..d09f66e6c 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -96,7 +96,9 @@ public BaseResponse showSelectableBookList( @Parameter(description = "저장한 책 또는 참여 중인 모임의 책을 구분하는 필드 (SAVED : 저장한 책 / JOINING : 모임 방의 책)", example = "SAVED") @RequestParam final String type, @Parameter(hidden = true) @UserId final Long userId ) { - return BaseResponse.ok(bookSelectableListUseCase.getSelectableBookList(BookSelectableType.from(type), userId)); + return BaseResponse.ok( + BookSelectableListResponse.of(bookSelectableListUseCase.getSelectableBookList(BookSelectableType.from(type), userId)) + ); } } diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java index 42fa9c9e1..a8eede590 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java @@ -1,17 +1,13 @@ package konkuk.thip.book.adapter.in.web.response; +import konkuk.thip.book.application.port.in.dto.BookInfo; + import java.util.List; public record BookSelectableListResponse( - List bookList + List bookList ) { - public record BookDto( - Long bookId, - String bookTitle, - String authorName, - String publisher, - String bookImageUrl, - String isbn - ) { + public static BookSelectableListResponse of(List bookInfos) { + return new BookSelectableListResponse(bookInfos); } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java index 664ccb4a3..f2e5b28b1 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java @@ -1,6 +1,5 @@ package konkuk.thip.book.adapter.out.persistence; -import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; import konkuk.thip.book.adapter.out.mapper.BookMapper; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; @@ -12,7 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; @@ -28,23 +27,27 @@ public class BookQueryPersistenceAdapter implements BookQueryPort { @Override public boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId) { - return savedBookJpaRepository.existsByUserJpaEntity_UserIdAndBookJpaEntity_BookId(userId, bookId); + return savedBookJpaRepository.existsByUserIdAndBookId(userId, bookId); } @Override - public List findSavedBooksByUserId(Long userId) { + public Set findSavedBooksByUserId(Long userId) { UserJpaEntity user = userJpaRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - List savedBookEntities = savedBookJpaRepository.findByUserId(user.getUserId()); - - return savedBookEntities.stream() - .map(entity -> bookMapper.toDomainEntity(entity.getBookJpaEntity())) - .collect(Collectors.toList()); + return bookJpaRepository.findSavedBooksByUserId(user.getUserId()).stream() + .map(bookMapper::toDomainEntity) + .collect(Collectors.toSet()); } @Override - public List findJoiningRoomsBooksByUserId(Long userId) { - return bookJpaRepository.; + public Set findJoiningRoomsBooksByUserId(Long userId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + + return bookJpaRepository.findJoiningRoomsBooksByUserId(user.getUserId()) + .stream() + .map(bookMapper::toDomainEntity) + .collect(Collectors.toSet()); } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java index c7b763298..66ee4e4f2 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java @@ -2,11 +2,25 @@ import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.util.List; import java.util.Optional; public interface BookJpaRepository extends JpaRepository { Optional findByIsbn(String isbn); - List findByIsbnIn(List isbnList); + + @Query("SELECT b FROM BookJpaEntity b " + + "JOIN SavedBookJpaEntity s ON s.bookJpaEntity.bookId = b.bookId " + + "WHERE s.userJpaEntity.userId = :userId " + + "ORDER BY s.createdAt DESC") + List findSavedBooksByUserId(Long userId); + + @Query("SELECT b FROM BookJpaEntity b " + + "JOIN RoomJpaEntity r ON r.bookJpaEntity.bookId = b.bookId " + + "JOIN RoomParticipantJpaEntity rp ON rp.roomJpaEntity.roomId = r.roomId " + + "WHERE rp.userJpaEntity.userId = :userId " + + "AND r.status = 'ACTIVE' " + + "AND r.startDate <= CURRENT_TIMESTAMP") // 진행 중인 방만 조회 (모집 중 / 만료된 방 x) + List findJoiningRoomsBooksByUserId(Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java index 9ff727b82..061d430d0 100644 --- a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java +++ b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java @@ -1,6 +1,8 @@ package konkuk.thip.book.application.mapper; import konkuk.thip.book.adapter.in.web.response.BookRecruitingRoomsResponse; +import konkuk.thip.book.application.port.in.dto.BookInfo; +import konkuk.thip.book.domain.Book; import konkuk.thip.common.util.DateUtil; import konkuk.thip.room.application.port.out.dto.RoomQueryDto; import org.mapstruct.Mapper; @@ -8,6 +10,7 @@ import org.mapstruct.ReportingPolicy; import java.util.List; +import java.util.Set; @Mapper( componentModel = "spring", @@ -23,4 +26,14 @@ public interface BookQueryMapper { BookRecruitingRoomsResponse.RecruitingRoomDto toRecruitingRoomDto(RoomQueryDto dto); List toRecruitingRoomDtoList(List roomDtos); + + @Mapping(target = "bookId", source = "book.id") + @Mapping(target = "bookTitle", source = "book.title") + @Mapping(target = "authorName", source = "book.authorName") + @Mapping(target = "publisher", source = "book.publisher") + @Mapping(target = "bookImageUrl", source = "book.imageUrl") + @Mapping(target = "isbn", source = "book.isbn") + BookInfo toBookInfo(Book book); + + List toBookInfoList(Set books); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java index 6ee48c0fa..24f439901 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java +++ b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java @@ -1,8 +1,10 @@ package konkuk.thip.book.application.port.in; +import konkuk.thip.book.application.port.in.dto.BookInfo; import konkuk.thip.book.application.port.in.dto.BookSelectableType; -import konkuk.thip.book.adapter.in.web.response.BookSelectableListResponse; + +import java.util.List; public interface BookSelectableListUseCase { - BookSelectableListResponse getSelectableBookList(BookSelectableType bookSelectableType, Long userId); + List getSelectableBookList(BookSelectableType bookSelectableType, Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/dto/BookInfo.java b/src/main/java/konkuk/thip/book/application/port/in/dto/BookInfo.java new file mode 100644 index 000000000..aff341cd9 --- /dev/null +++ b/src/main/java/konkuk/thip/book/application/port/in/dto/BookInfo.java @@ -0,0 +1,11 @@ +package konkuk.thip.book.application.port.in.dto; + +public record BookInfo( + Long bookId, + String bookTitle, + String authorName, + String publisher, + String bookImageUrl, + String isbn +) { +} diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java index bb9e0ab00..3fb443f63 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java @@ -2,13 +2,13 @@ import konkuk.thip.book.domain.Book; -import java.util.List; +import java.util.Set; public interface BookQueryPort { boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId); - List findSavedBooksByUserId(Long userId); + Set findSavedBooksByUserId(Long userId); - List findJoiningRoomsBooksByUserId(Long userId); + Set findJoiningRoomsBooksByUserId(Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index ced424b2f..df75d30fc 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -1,29 +1,31 @@ package konkuk.thip.book.application.service; -import konkuk.thip.book.application.port.in.dto.BookSelectableType; -import konkuk.thip.book.adapter.in.web.response.BookSelectableListResponse; +import konkuk.thip.book.application.mapper.BookQueryMapper; import konkuk.thip.book.application.port.in.BookSelectableListUseCase; +import konkuk.thip.book.application.port.in.dto.BookInfo; +import konkuk.thip.book.application.port.in.dto.BookSelectableType; import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.domain.Book; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; - -import static konkuk.thip.book.application.port.in.dto.BookSelectableType.*; +import java.util.Set; @Service @RequiredArgsConstructor public class BookSelectableListService implements BookSelectableListUseCase { private final BookQueryPort bookQueryPort; + private final BookQueryMapper bookQueryMapper; @Override - public BookSelectableListResponse getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { - List bookList = switch(bookSelectableType) { + public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { + Set bookSet = switch(bookSelectableType) { case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); }; + + return bookQueryMapper.toBookInfoList(bookSet); } } From 44216df6198bdfe5a713d80e3aef96c3f226ae6c Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 20:05:11 +0900 Subject: [PATCH 08/26] =?UTF-8?q?[test]=20=EC=A0=80=EC=9E=A5=ED=95=9C=20?= =?UTF-8?q?=EC=B1=85=20=EB=98=90=EB=8A=94=20=EC=B0=B8=EC=97=AC=20=EC=A4=91?= =?UTF-8?q?=20=EB=AA=A8=EC=9E=84=EC=9D=98=20=EC=B1=85=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20api=20=ED=86=B5=ED=95=A9=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20(#1?= =?UTF-8?q?56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/in/web/BookSelectableApiTest.java | 93 +++++++++++++++++++ .../thip/common/util/TestEntityFactory.java | 8 ++ 2 files changed, 101 insertions(+) create mode 100644 src/test/java/konkuk/thip/book/adapter/in/web/BookSelectableApiTest.java diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookSelectableApiTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookSelectableApiTest.java new file mode 100644 index 000000000..c0b157062 --- /dev/null +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookSelectableApiTest.java @@ -0,0 +1,93 @@ +package konkuk.thip.book.adapter.in.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; +import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; +import konkuk.thip.common.util.TestEntityFactory; +import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomJpaEntity; +import konkuk.thip.room.adapter.out.jpa.RoomParticipantRole; +import konkuk.thip.room.adapter.out.persistence.repository.RoomJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; +import konkuk.thip.room.adapter.out.persistence.repository.roomparticipant.RoomParticipantJpaRepository; +import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; +import konkuk.thip.user.adapter.out.persistence.repository.alias.AliasJpaRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@SpringBootTest +@AutoConfigureMockMvc(addFilters = false) +@ActiveProfiles("test") +@Transactional +@DisplayName("[통합] 저장한 책 및 참여 중 책 리스트 조회 API 통합 테스트") +class BookSelectableApiTest { + + @Autowired private MockMvc mockMvc; + @Autowired private ObjectMapper objectMapper; + + @Autowired private UserJpaRepository userJpaRepository; + @Autowired private AliasJpaRepository aliasJpaRepository; + @Autowired private BookJpaRepository bookJpaRepository; + @Autowired private SavedBookJpaRepository savedBookJpaRepository; + @Autowired private CategoryJpaRepository categoryJpaRepository; + @Autowired private RoomJpaRepository roomJpaRepository; + @Autowired private RoomParticipantJpaRepository roomParticipantJpaRepository; + + private UserJpaEntity user; + private BookJpaEntity savedBook; + private BookJpaEntity joiningBook; + + @BeforeEach + void setUp() { + AliasJpaEntity alias = aliasJpaRepository.save(TestEntityFactory.createLiteratureAlias()); + user = userJpaRepository.save(TestEntityFactory.createUser(alias)); + savedBook = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("1111111111111")); + joiningBook = bookJpaRepository.save(TestEntityFactory.createBookWithISBN("2222222222222")); + + // SAVED 책 등록 + savedBookJpaRepository.save(TestEntityFactory.createSavedBook(user, savedBook)); + + // JOINING 책용 방 생성 + CategoryJpaEntity category = categoryJpaRepository.save(TestEntityFactory.createLiteratureCategory(alias)); + RoomJpaEntity room = roomJpaRepository.save(TestEntityFactory.createRoom(joiningBook, category)); + roomParticipantJpaRepository.save(TestEntityFactory.createRoomParticipant(room, user, RoomParticipantRole.HOST, 0.0)); + } + + @Test + @DisplayName("SAVED 타입 - 저장한 책 리스트 조회 성공") + void getSelectableBooks_saved_success() throws Exception { + mockMvc.perform(get("/books/selectable-list") + .param("type", "SAVED") + .requestAttr("userId", user.getUserId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.bookList").isArray()) + .andExpect(jsonPath("$.data.bookList.length()").value(1)) + .andExpect(jsonPath("$.data.bookList[0].isbn").value(savedBook.getIsbn())); + } + + @Test + @DisplayName("JOINING 타입 - 참여 중인 책 리스트 조회 성공") + void getSelectableBooks_joining_success() throws Exception { + mockMvc.perform(get("/books/selectable-list") + .param("type", "JOINING") + .requestAttr("userId", user.getUserId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.bookList").isArray()) + .andExpect(jsonPath("$.data.bookList.length()").value(1)) + .andExpect(jsonPath("$.data.bookList[0].isbn").value(joiningBook.getIsbn())); + } +} \ No newline at end of file diff --git a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java index dd9fc44fe..3d03b7fb2 100644 --- a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java +++ b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java @@ -1,6 +1,7 @@ package konkuk.thip.common.util; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; +import konkuk.thip.book.adapter.out.jpa.SavedBookJpaEntity; import konkuk.thip.comment.adapter.out.jpa.CommentJpaEntity; import konkuk.thip.comment.adapter.out.jpa.CommentLikeJpaEntity; import konkuk.thip.common.post.PostType; @@ -313,4 +314,11 @@ public static VoteParticipantJpaEntity createVoteParticipant(UserJpaEntity user, .voteItemJpaEntity(item) .build(); } + + public static SavedBookJpaEntity createSavedBook(UserJpaEntity user, BookJpaEntity book) { + return SavedBookJpaEntity.builder() + .userJpaEntity(user) + .bookJpaEntity(book) + .build(); + } } \ No newline at end of file From d100c521f3019f86761d4c51de90ef1ac631cf63 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 20:21:45 +0900 Subject: [PATCH 09/26] =?UTF-8?q?[refactor]=20=ED=94=BC=EB=93=9C=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EA=B4=80=EB=A0=A8=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=ED=94=BC=EB=93=9C=20=ED=8C=A8=ED=82=A4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/out/jpa/SavedFeedJpaEntity.java | 3 +- .../FeedCommandPersistenceAdapter.java | 27 +++++-- .../FeedQueryPersistenceAdapter.java | 55 ++++++++++++++ .../FeedTag/FeedTagJpaRepository.java | 2 +- .../repository/SavedFeedJpaRepository.java | 4 +- .../application/port/out/FeedCommandPort.java | 3 + .../application/port/out/FeedQueryPort.java | 8 +++ .../port/out/dto/FeedIdAndTagProjection.java | 2 +- .../service/BasicFeedShowAllService.java | 4 +- .../application/service/FeedSavedService.java | 12 ++-- .../service/FeedShowAllOfUserService.java | 4 +- .../service/FeedShowSingleService.java | 9 +-- .../FollowingPriorityFeedShowAllService.java | 4 +- .../SavedCommandPersistenceAdapter.java | 44 ------------ .../SavedQueryPersistenceAdapter.java | 71 ------------------- .../port/out/SavedCommandPort.java | 7 -- .../application/port/out/SavedQueryPort.java | 12 ---- .../thip/common/util/TestEntityFactory.java | 2 +- .../in/web/BasicFeedShowAllApiTest.java | 4 +- .../in/web/FeedChangeSavedAPITest.java | 4 +- .../adapter/in/web/FeedShowMineApiTest.java | 2 +- .../adapter/in/web/FeedShowSingleApiTest.java | 4 +- .../in/web/FeedShowSpecificUserApiTest.java | 4 +- .../FollowingPriorityFeedShowAllApiTest.java | 6 +- 24 files changed, 116 insertions(+), 181 deletions(-) rename src/main/java/konkuk/thip/{saved => feed}/adapter/out/jpa/SavedFeedJpaEntity.java (86%) rename src/main/java/konkuk/thip/{saved => feed}/adapter/out/persistence/repository/SavedFeedJpaRepository.java (89%) rename src/main/java/konkuk/thip/{saved => feed}/application/port/out/dto/FeedIdAndTagProjection.java (75%) delete mode 100644 src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java delete mode 100644 src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java delete mode 100644 src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java delete mode 100644 src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java diff --git a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedFeedJpaEntity.java b/src/main/java/konkuk/thip/feed/adapter/out/jpa/SavedFeedJpaEntity.java similarity index 86% rename from src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedFeedJpaEntity.java rename to src/main/java/konkuk/thip/feed/adapter/out/jpa/SavedFeedJpaEntity.java index b71090707..2e1f1ea7a 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/jpa/SavedFeedJpaEntity.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/jpa/SavedFeedJpaEntity.java @@ -1,8 +1,7 @@ -package konkuk.thip.saved.adapter.out.jpa; +package konkuk.thip.feed.adapter.out.jpa; import jakarta.persistence.*; import konkuk.thip.common.entity.BaseJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import lombok.*; diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java index 84c0d789e..196d91c38 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedCommandPersistenceAdapter.java @@ -3,14 +3,12 @@ import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.feed.adapter.out.jpa.ContentJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.FeedTagJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.*; import konkuk.thip.feed.adapter.out.mapper.ContentMapper; import konkuk.thip.feed.adapter.out.mapper.FeedMapper; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.Tag.TagJpaRepository; import konkuk.thip.feed.application.port.out.FeedCommandPort; import konkuk.thip.feed.domain.Feed; @@ -34,10 +32,11 @@ public class FeedCommandPersistenceAdapter implements FeedCommandPort { private final BookJpaRepository bookJpaRepository; private final TagJpaRepository tagJpaRepository; private final FeedTagJpaRepository feedTagJpaRepository; + private final SavedFeedJpaRepository savedFeedJpaRepository; + private final FeedMapper feedMapper; private final ContentMapper contentMapper; - @Override public Optional findById(Long id) { return feedJpaRepository.findById(id) @@ -108,4 +107,22 @@ private void applyFeedTags(Feed feed, FeedJpaEntity feedJpaEntity) { } } + @Override + public void saveSavedFeed(Long userId, Long feedId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + FeedJpaEntity feed = feedJpaRepository.findById(feedId) + .orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND)); + SavedFeedJpaEntity entity = SavedFeedJpaEntity.builder() + .userJpaEntity(user) + .feedJpaEntity(feed) + .build(); + savedFeedJpaRepository.save(entity); + } + + @Override + public void deleteSavedFeed(Long userId, Long feedId) { + savedFeedJpaRepository.deleteByUserIdAndFeedId(userId, feedId); + } + } diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java index d0b9829ea..9774e14c5 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/FeedQueryPersistenceAdapter.java @@ -1,24 +1,42 @@ package konkuk.thip.feed.adapter.out.persistence; import konkuk.thip.common.entity.StatusType; +import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.common.util.Cursor; import konkuk.thip.common.util.CursorBasedList; +import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; import konkuk.thip.feed.adapter.out.mapper.FeedMapper; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; +import konkuk.thip.feed.domain.Feed; +import konkuk.thip.feed.domain.SavedFeeds; +import konkuk.thip.feed.application.port.out.dto.FeedIdAndTagProjection; +import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; +import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.List; +import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; + +import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; @Repository @RequiredArgsConstructor public class FeedQueryPersistenceAdapter implements FeedQueryPort { private final FeedJpaRepository feedJpaRepository; + private final FeedTagJpaRepository feedTagJpaRepository; + private final UserJpaRepository userJpaRepository; + private final SavedFeedJpaRepository savedFeedJpaRepository; private final FeedMapper feedMapper; @Override @@ -92,4 +110,41 @@ public int countAllFeedsByUserId(Long userId) { public int countPublicFeedsByUserId(Long userId) { return (int) feedJpaRepository.countPublicFeedsByUserId(userId, StatusType.ACTIVE); } + + @Override + public SavedFeeds findSavedFeedsByUserId(Long userId) { + UserJpaEntity user = userJpaRepository.findById(userId) + .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); + + List savedFeedEntities = + savedFeedJpaRepository.findAllByUserId(user.getUserId()); + + List feedIds = savedFeedEntities.stream() + .map(entity -> entity.getFeedJpaEntity().getPostId()) + .toList(); + + // Projection 기반 조회 + List results = feedTagJpaRepository.findFeedIdAndTagsByFeedIds(feedIds); + + Map> feedTagsMap = results.stream() + .collect(Collectors.groupingBy( + FeedIdAndTagProjection::getFeedId, + Collectors.mapping(FeedIdAndTagProjection::getTagJpaEntity, Collectors.toList()) + )); + + List feeds = savedFeedEntities.stream() + .map(entity -> { + FeedJpaEntity feedJpa = entity.getFeedJpaEntity(); + List tags = feedTagsMap.getOrDefault(feedJpa.getPostId(), List.of()); + return feedMapper.toDomainEntity(feedJpa, tags); + }) + .toList(); + + return new SavedFeeds(feeds); + } + + @Override + public Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId) { + return savedFeedJpaRepository.findSavedFeedIdsByUserIdAndFeedIds(userId, feedIds); + } } diff --git a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java index cf5519abc..84e27e2ba 100644 --- a/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/FeedTag/FeedTagJpaRepository.java @@ -2,7 +2,7 @@ import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.feed.adapter.out.jpa.FeedTagJpaEntity; -import konkuk.thip.saved.application.port.out.dto.FeedIdAndTagProjection; +import konkuk.thip.feed.application.port.out.dto.FeedIdAndTagProjection; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java similarity index 89% rename from src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java rename to src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java index e98baf1f3..d9b76cfab 100644 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/repository/SavedFeedJpaRepository.java +++ b/src/main/java/konkuk/thip/feed/adapter/out/persistence/repository/SavedFeedJpaRepository.java @@ -1,6 +1,6 @@ -package konkuk.thip.saved.adapter.out.persistence.repository; +package konkuk.thip.feed.adapter.out.persistence.repository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java b/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java index 31c86b5c2..c34b0d631 100644 --- a/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java +++ b/src/main/java/konkuk/thip/feed/application/port/out/FeedCommandPort.java @@ -16,4 +16,7 @@ default Feed getByIdOrThrow(Long id) { return findById(id) .orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND)); } + + void saveSavedFeed(Long userId, Long feedId); + void deleteSavedFeed(Long userId, Long feedId); } diff --git a/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java b/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java index c98f201b3..102ee5035 100644 --- a/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java +++ b/src/main/java/konkuk/thip/feed/application/port/out/FeedQueryPort.java @@ -3,6 +3,7 @@ import konkuk.thip.common.util.Cursor; import konkuk.thip.common.util.CursorBasedList; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; +import konkuk.thip.feed.domain.SavedFeeds; import java.util.Set; @@ -29,4 +30,11 @@ public interface FeedQueryPort { int countAllFeedsByUserId(Long userId); int countPublicFeedsByUserId(Long userId); + + /** + * 저장된 피드 조회 + */ + SavedFeeds findSavedFeedsByUserId(Long userId); + + Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId); } diff --git a/src/main/java/konkuk/thip/saved/application/port/out/dto/FeedIdAndTagProjection.java b/src/main/java/konkuk/thip/feed/application/port/out/dto/FeedIdAndTagProjection.java similarity index 75% rename from src/main/java/konkuk/thip/saved/application/port/out/dto/FeedIdAndTagProjection.java rename to src/main/java/konkuk/thip/feed/application/port/out/dto/FeedIdAndTagProjection.java index ab1ef2c5d..b4de5ae3b 100644 --- a/src/main/java/konkuk/thip/saved/application/port/out/dto/FeedIdAndTagProjection.java +++ b/src/main/java/konkuk/thip/feed/application/port/out/dto/FeedIdAndTagProjection.java @@ -1,4 +1,4 @@ -package konkuk.thip.saved.application.port.out.dto; +package konkuk.thip.feed.application.port.out.dto; import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; diff --git a/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java b/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java index deac8cfdf..de9f126ec 100644 --- a/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java +++ b/src/main/java/konkuk/thip/feed/application/service/BasicFeedShowAllService.java @@ -8,7 +8,6 @@ import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; @@ -33,7 +32,6 @@ public class BasicFeedShowAllService implements FeedShowAllUseCase { private static final int PAGE_SIZE = 10; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; private final PostLikeQueryPort postLikeQueryPort; private final FeedQueryMapper feedQueryMapper; @@ -50,7 +48,7 @@ public FeedShowAllResponse showAllFeeds(Long userId, String cursor) { .collect(Collectors.toUnmodifiableSet()); // 3. 유저가 저장한 피드들, 좋아한 피드들 조회 - Set savedFeedIdsByUser = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); + Set savedFeedIdsByUser = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); Set likedFeedIdsByUser = postLikeQueryPort.findPostIdsLikedByUser(feedIds, userId); // 4. response 로의 매핑 diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java b/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java index 9825e8d78..6236b2d15 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedSavedService.java @@ -5,10 +5,9 @@ import konkuk.thip.feed.application.port.in.dto.FeedIsSavedCommand; import konkuk.thip.feed.application.port.in.dto.FeedIsSavedResult; import konkuk.thip.feed.application.port.out.FeedCommandPort; +import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.domain.Feed; import konkuk.thip.feed.domain.SavedFeeds; -import konkuk.thip.saved.application.port.out.SavedCommandPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -17,8 +16,7 @@ public class FeedSavedService implements FeedSavedUseCase { private final FeedCommandPort feedCommandPort; - private final SavedCommandPort savedCommandPort; - private final SavedQueryPort savedQueryPort; + private final FeedQueryPort feedQueryPort; @Override @Transactional @@ -28,16 +26,16 @@ public FeedIsSavedResult changeSavedFeed(FeedIsSavedCommand feedIsSavedCommand) Feed feed = feedCommandPort.getByIdOrThrow(feedIsSavedCommand.feedId()); // 2. 유저가 저장한 피드 목록 조회 - SavedFeeds savedFeeds = savedQueryPort.findSavedFeedsByUserId(feedIsSavedCommand.userId()); + SavedFeeds savedFeeds = feedQueryPort.findSavedFeedsByUserId(feedIsSavedCommand.userId()); if (feedIsSavedCommand.isSaved()) { // 저장 요청 시 이미 저장되어 있으면 예외 발생 savedFeeds.validateNotAlreadySaved(feed); - savedCommandPort.saveFeed(feedIsSavedCommand.userId(), feed.getId()); + feedCommandPort.saveSavedFeed(feedIsSavedCommand.userId(), feed.getId()); } else { // 삭제 요청 시 저장되어 있지 않으면 예외 발생 savedFeeds.validateCanDelete(feed); - savedCommandPort.deleteFeed(feedIsSavedCommand.userId(), feed.getId()); + feedCommandPort.deleteSavedFeed(feedIsSavedCommand.userId(), feed.getId()); } return FeedIsSavedResult.of(feed.getId(), feedIsSavedCommand.isSaved()); diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java b/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java index fe2b9cf27..9fb50f692 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedShowAllOfUserService.java @@ -9,7 +9,6 @@ import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +23,6 @@ public class FeedShowAllOfUserService implements FeedShowAllOfUserUseCase { private static final int PAGE_SIZE = 10; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; private final PostLikeQueryPort postLikeQueryPort; private final FeedQueryMapper feedQueryMapper; @@ -57,7 +55,7 @@ public FeedShowByUserResponse showPublicFeedsOfFeedOwner(Long userId, Long feedO .collect(Collectors.toUnmodifiableSet()); // 3. 유저가 저장한 피드들, 좋아한 피드들 조회 - Set savedFeedIdsByUser = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); + Set savedFeedIdsByUser = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); Set likedFeedIdsByUser = postLikeQueryPort.findPostIdsLikedByUser(feedIds, userId); // 4. response 로의 매핑 diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java b/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java index b6dc7ed7c..788c43023 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedShowSingleService.java @@ -2,14 +2,13 @@ import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.domain.Book; -import konkuk.thip.common.exception.BusinessException; import konkuk.thip.feed.adapter.in.web.response.FeedShowSingleResponse; import konkuk.thip.feed.application.mapper.FeedQueryMapper; import konkuk.thip.feed.application.port.in.FeedShowSingleUseCase; import konkuk.thip.feed.application.port.out.FeedCommandPort; +import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.domain.Feed; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import konkuk.thip.user.application.port.out.UserCommandPort; import konkuk.thip.user.domain.User; import lombok.RequiredArgsConstructor; @@ -17,8 +16,6 @@ import java.util.Set; -import static konkuk.thip.common.exception.code.ErrorCode.FEED_CAN_NOT_SHOW_PRIVATE_ONE; - @Service @RequiredArgsConstructor public class FeedShowSingleService implements FeedShowSingleUseCase { @@ -27,7 +24,7 @@ public class FeedShowSingleService implements FeedShowSingleUseCase { private final UserCommandPort userCommandPort; private final BookCommandPort bookCommandPort; private final PostLikeQueryPort postLikeQueryPort; - private final SavedQueryPort savedQueryPort; + private final FeedQueryPort feedQueryPort; private final FeedQueryMapper feedQueryMapper; @Override @@ -43,7 +40,7 @@ public FeedShowSingleResponse showSingleFeed(Long feedId, Long userId) { Book book = bookCommandPort.findById(feed.getTargetBookId()); // 4. 유저가 해당 피드를 좋아하는지, 저장했는지 여부 조회 - Set savedFeedIds = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(Set.of(feedId), userId); + Set savedFeedIds = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(Set.of(feedId), userId); Set likedFeedIds = postLikeQueryPort.findPostIdsLikedByUser(Set.of(feedId), userId); boolean isSaved = savedFeedIds.contains(feedId); boolean isLiked = likedFeedIds.contains(feedId); diff --git a/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java b/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java index b0d4681f9..3319ec6fe 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FollowingPriorityFeedShowAllService.java @@ -8,7 +8,6 @@ import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.feed.application.port.out.dto.FeedQueryDto; import konkuk.thip.post.application.port.out.PostLikeQueryPort; -import konkuk.thip.saved.application.port.out.SavedQueryPort; import lombok.RequiredArgsConstructor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; @@ -32,7 +31,6 @@ public class FollowingPriorityFeedShowAllService implements FeedShowAllUseCase { private static final int PAGE_SIZE = 10; private final FeedQueryPort feedQueryPort; - private final SavedQueryPort savedQueryPort; private final PostLikeQueryPort postLikeQueryPort; private final FeedQueryMapper feedQueryMapper; @@ -49,7 +47,7 @@ public FeedShowAllResponse showAllFeeds(Long userId, String cursor) { .collect(Collectors.toUnmodifiableSet()); // 3. 유저가 저장한 피드들, 좋아한 피드들 조회 - Set savedFeedIdsByUser = savedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); + Set savedFeedIdsByUser = feedQueryPort.findSavedFeedIdsByUserIdAndFeedIds(feedIds, userId); Set likedFeedIdsByUser = postLikeQueryPort.findPostIdsLikedByUser(feedIds, userId); // 4. response 로의 매핑 diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java deleted file mode 100644 index f9c5e1b7c..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedCommandPersistenceAdapter.java +++ /dev/null @@ -1,44 +0,0 @@ -package konkuk.thip.saved.adapter.out.persistence; - -import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; -import konkuk.thip.saved.application.port.out.SavedCommandPort; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import static konkuk.thip.common.exception.code.ErrorCode.FEED_NOT_FOUND; -import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; - -@Repository -@RequiredArgsConstructor -public class SavedCommandPersistenceAdapter implements SavedCommandPort { - - private final UserJpaRepository userJpaRepository; - private final FeedJpaRepository feedJpaRepository; - private final SavedFeedJpaRepository savedFeedJpaRepository; - - @Override - public void saveFeed(Long userId, Long feedId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - FeedJpaEntity feed = feedJpaRepository.findById(feedId) - .orElseThrow(() -> new EntityNotFoundException(FEED_NOT_FOUND)); - SavedFeedJpaEntity entity = SavedFeedJpaEntity.builder() - .userJpaEntity(user) - .feedJpaEntity(feed) - .build(); - savedFeedJpaRepository.save(entity); - } - - @Override - public void deleteFeed(Long userId, Long feedId) { - savedFeedJpaRepository.deleteByUserIdAndFeedId(userId, feedId); - } - - -} \ No newline at end of file diff --git a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java deleted file mode 100644 index d8bed4bcd..000000000 --- a/src/main/java/konkuk/thip/saved/adapter/out/persistence/SavedQueryPersistenceAdapter.java +++ /dev/null @@ -1,71 +0,0 @@ -package konkuk.thip.saved.adapter.out.persistence; - -import konkuk.thip.common.exception.EntityNotFoundException; -import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; -import konkuk.thip.feed.adapter.out.jpa.TagJpaEntity; -import konkuk.thip.feed.adapter.out.mapper.FeedMapper; -import konkuk.thip.feed.adapter.out.persistence.repository.FeedTag.FeedTagJpaRepository; -import konkuk.thip.feed.domain.Feed; -import konkuk.thip.feed.domain.SavedFeeds; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; -import konkuk.thip.saved.application.port.out.SavedQueryPort; -import konkuk.thip.saved.application.port.out.dto.FeedIdAndTagProjection; -import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; -import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; - -@Repository -@RequiredArgsConstructor -public class SavedQueryPersistenceAdapter implements SavedQueryPort { - - private final SavedFeedJpaRepository savedFeedJpaRepository; - private final UserJpaRepository userJpaRepository; - private final FeedMapper feedMapper; - private final FeedTagJpaRepository feedTagJpaRepository; - - @Override - public SavedFeeds findSavedFeedsByUserId(Long userId) { - UserJpaEntity user = userJpaRepository.findById(userId) - .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); - - List savedFeedEntities = - savedFeedJpaRepository.findAllByUserId(user.getUserId()); - - List feedIds = savedFeedEntities.stream() - .map(entity -> entity.getFeedJpaEntity().getPostId()) - .toList(); - - // Projection 기반 조회 - List results = feedTagJpaRepository.findFeedIdAndTagsByFeedIds(feedIds); - - Map> feedTagsMap = results.stream() - .collect(Collectors.groupingBy( - FeedIdAndTagProjection::getFeedId, - Collectors.mapping(FeedIdAndTagProjection::getTagJpaEntity, Collectors.toList()) - )); - - List feeds = savedFeedEntities.stream() - .map(entity -> { - FeedJpaEntity feedJpa = entity.getFeedJpaEntity(); - List tags = feedTagsMap.getOrDefault(feedJpa.getPostId(), List.of()); - return feedMapper.toDomainEntity(feedJpa, tags); - }) - .toList(); - - return new SavedFeeds(feeds); - } - - @Override - public Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId) { - return savedFeedJpaRepository.findSavedFeedIdsByUserIdAndFeedIds(userId, feedIds); - } -} diff --git a/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java b/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java deleted file mode 100644 index e1d3b512c..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/out/SavedCommandPort.java +++ /dev/null @@ -1,7 +0,0 @@ -package konkuk.thip.saved.application.port.out; - - -public interface SavedCommandPort { - void saveFeed(Long userId, Long feedId); - void deleteFeed(Long userId, Long feedId); -} diff --git a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java b/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java deleted file mode 100644 index 0ae5574fe..000000000 --- a/src/main/java/konkuk/thip/saved/application/port/out/SavedQueryPort.java +++ /dev/null @@ -1,12 +0,0 @@ -package konkuk.thip.saved.application.port.out; - -import konkuk.thip.feed.domain.SavedFeeds; - -import java.util.Set; - -public interface SavedQueryPort { - - SavedFeeds findSavedFeedsByUserId(Long userId); - - Set findSavedFeedIdsByUserIdAndFeedIds(Set feedIds, Long userId); -} diff --git a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java index 3d03b7fb2..01d4686ae 100644 --- a/src/test/java/konkuk/thip/common/util/TestEntityFactory.java +++ b/src/test/java/konkuk/thip/common/util/TestEntityFactory.java @@ -16,7 +16,7 @@ import konkuk.thip.room.adapter.out.jpa.RoomParticipantJpaEntity; import konkuk.thip.room.adapter.out.jpa.RoomParticipantRole; import konkuk.thip.room.domain.Category; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.FollowingJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java index 93f0478c9..06a32f674 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/BasicFeedShowAllApiTest.java @@ -8,8 +8,8 @@ import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.jpa.PostLikeJpaEntity; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java index 76b976d60..9d14f1d86 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedChangeSavedAPITest.java @@ -11,8 +11,8 @@ import konkuk.thip.feed.adapter.out.persistence.repository.Tag.TagJpaRepository; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java index ce32c3aab..d3c039f14 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowMineApiTest.java @@ -7,7 +7,7 @@ import konkuk.thip.feed.adapter.out.persistence.repository.Content.ContentJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java index fdd138f04..361e21191 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSingleApiTest.java @@ -13,8 +13,8 @@ import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; import konkuk.thip.room.adapter.out.jpa.CategoryJpaEntity; import konkuk.thip.room.adapter.out.persistence.repository.category.CategoryJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java index 4051a58f7..9568412a3 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FeedShowSpecificUserApiTest.java @@ -8,8 +8,8 @@ import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.jpa.PostLikeJpaEntity; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; diff --git a/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java b/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java index eb8ac5476..03b730420 100644 --- a/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java +++ b/src/test/java/konkuk/thip/feed/adapter/in/web/FollowingPriorityFeedShowAllApiTest.java @@ -1,17 +1,15 @@ package konkuk.thip.feed.adapter.in.web; -import com.amazonaws.services.cloudformation.model.StackResourceDriftInformation; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; -import konkuk.thip.common.util.Cursor; import konkuk.thip.common.util.TestEntityFactory; import konkuk.thip.feed.adapter.out.jpa.FeedJpaEntity; import konkuk.thip.feed.adapter.out.persistence.repository.Content.ContentJpaRepository; import konkuk.thip.feed.adapter.out.persistence.repository.FeedJpaRepository; import konkuk.thip.post.adapter.out.jpa.PostLikeJpaEntity; import konkuk.thip.post.adapter.out.persistence.PostLikeJpaRepository; -import konkuk.thip.saved.adapter.out.jpa.SavedFeedJpaEntity; -import konkuk.thip.saved.adapter.out.persistence.repository.SavedFeedJpaRepository; +import konkuk.thip.feed.adapter.out.jpa.SavedFeedJpaEntity; +import konkuk.thip.feed.adapter.out.persistence.repository.SavedFeedJpaRepository; import konkuk.thip.user.adapter.out.jpa.AliasJpaEntity; import konkuk.thip.user.adapter.out.jpa.UserJpaEntity; import konkuk.thip.user.adapter.out.persistence.repository.UserJpaRepository; From c043a82822b2dfa9d37a195103776edc4e3aa4da Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 20:22:56 +0900 Subject: [PATCH 10/26] =?UTF-8?q?[test]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...SelectableApiTest.java => BookGetSelectableListApiTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/konkuk/thip/book/adapter/in/web/{BookSelectableApiTest.java => BookGetSelectableListApiTest.java} (99%) diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookSelectableApiTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java similarity index 99% rename from src/test/java/konkuk/thip/book/adapter/in/web/BookSelectableApiTest.java rename to src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java index c0b157062..31c325470 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookSelectableApiTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java @@ -34,7 +34,7 @@ @ActiveProfiles("test") @Transactional @DisplayName("[통합] 저장한 책 및 참여 중 책 리스트 조회 API 통합 테스트") -class BookSelectableApiTest { +class BookGetSelectableListApiTest { @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; From d1116262744197c0f38bd92c340e9264f78d2a06 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 20:23:11 +0900 Subject: [PATCH 11/26] =?UTF-8?q?[test]=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/adapter/in/web/BookGetSelectableListApiTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java index 31c325470..5bdd6f766 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java @@ -1,6 +1,5 @@ package konkuk.thip.book.adapter.in.web; -import com.fasterxml.jackson.databind.ObjectMapper; import konkuk.thip.book.adapter.out.jpa.BookJpaEntity; import konkuk.thip.book.adapter.out.persistence.repository.BookJpaRepository; import konkuk.thip.book.adapter.out.persistence.repository.SavedBookJpaRepository; @@ -37,7 +36,6 @@ class BookGetSelectableListApiTest { @Autowired private MockMvc mockMvc; - @Autowired private ObjectMapper objectMapper; @Autowired private UserJpaRepository userJpaRepository; @Autowired private AliasJpaRepository aliasJpaRepository; From 280b5890988b8109c1c54304489edce85e20a34d Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 20:40:16 +0900 Subject: [PATCH 12/26] =?UTF-8?q?[refactor]=20Optional=20=EB=B0=98?= =?UTF-8?q?=ED=99=98=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A5=BC=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=B4=EC=84=9C=20try=20catch=20=EC=A0=9C=EA=B1=B0?= =?UTF-8?q?=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/BookMostSearchRankService.java | 45 ++++++++-------- .../service/BookSearchService.java | 51 +++++++++---------- .../service/FeedCreateService.java | 9 ++-- .../service/RoomCreateService.java | 17 +++---- 4 files changed, 59 insertions(+), 63 deletions(-) diff --git a/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java b/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java index 3df7edc9c..9711da9bf 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java @@ -6,8 +6,6 @@ import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.application.port.out.BookRedisCommandPort; import konkuk.thip.book.application.port.out.BookRedisQueryPort; -import konkuk.thip.book.domain.Book; -import konkuk.thip.common.exception.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Scheduled; @@ -17,6 +15,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @Service @@ -53,26 +52,30 @@ public void updateDailySearchRank() { // Top 5 상세정보 저장 추가 List bookRankDetails = new ArrayList<>(); - int rank = 1; + AtomicInteger rank = new AtomicInteger(1); for (String isbn : isbns) { - try { - Book book = bookCommandPort.getByIsbnOrThrow(isbn); - bookRankDetails.add(BookMostSearchResult.BookRankInfo.builder() - .rank(rank++) - .title(book.getTitle()) - .imageUrl(book.getImageUrl()) - .isbn(isbn) - .build()); - } catch (EntityNotFoundException e) { - // DB에 없으면 Naver API에서 상세 정보 조회 - NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); - bookRankDetails.add(BookMostSearchResult.BookRankInfo.builder() - .rank(rank++) - .title(naverResult.title()) - .imageUrl(naverResult.imageUrl()) - .isbn(isbn) - .build()); - } + bookCommandPort.findByIsbn(isbn).ifPresentOrElse( + book -> bookRankDetails.add( + BookMostSearchResult.BookRankInfo.builder() + .rank(rank.getAndIncrement()) + .title(book.getTitle()) + .imageUrl(book.getImageUrl()) + .isbn(isbn) + .build() + ), + () -> { + // DB에 없으면 Naver API에서 상세 정보 조회 + NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); + bookRankDetails.add( + BookMostSearchResult.BookRankInfo.builder() + .rank(rank.getAndIncrement()) + .title(naverResult.title()) + .imageUrl(naverResult.imageUrl()) + .isbn(isbn) + .build() + ); + } + ); } bookRedisCommandPort.saveBookSearchRankDetail(bookRankDetails, yesterday); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java index 8b1c2e9c9..d1826a244 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java @@ -10,7 +10,6 @@ import konkuk.thip.book.application.port.out.BookRedisCommandPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.BusinessException; -import konkuk.thip.common.exception.EntityNotFoundException; import konkuk.thip.feed.application.port.out.FeedQueryPort; import konkuk.thip.recentSearch.application.port.out.RecentSearchCommandPort; import konkuk.thip.recentSearch.domain.RecentSearch; @@ -90,32 +89,30 @@ public BookDetailSearchResult searchDetailBooks(String isbn,Long userId) { //책 검색순위 정보 업데이트 bookRedisCommandPort.incrementBookSearchCount(isbn,LocalDate.now()); - Book book; - try { - // DB에서 책 정보 조회 (없으면 예외 발생) - book = bookCommandPort.getByIsbnOrThrow(isbn); - } catch (EntityNotFoundException e) { - // 책이 DB에 없으면 기본값으로 반환 - return BookDetailSearchResult.of( - naverDetailBookParseResult, - 0, // 모집 중인 방 개수 - 0, // 읽기 참여자 수 - false // 저장 여부 - ); - } - - //이책에 모집중인 모임방 개수 - int recruitingRoomCount = getRecruitingRoomCount(book); - // 이책에 읽기 참여중인 사용자 수 - int readCount = getReadCount(book); - // 사용자의 해당 책 저장 여부 - boolean isSaved = bookQueryPort.existsSavedBookByUserIdAndBookId(user.getId(), book.getId()); - - return BookDetailSearchResult.of( - naverDetailBookParseResult, - recruitingRoomCount, - readCount, - isSaved); + return bookCommandPort.findByIsbn(isbn) + .map(book -> { + //이책에 모집중인 모임방 개수 + int recruitingRoomCount = getRecruitingRoomCount(book); + // 이책에 읽기 참여중인 사용자 수 + int readCount = getReadCount(book); + // 사용자의 해당 책 저장 여부 + boolean isSaved = bookQueryPort.existsSavedBookByUserIdAndBookId(user.getId(), book.getId()); + + return BookDetailSearchResult.of( + naverDetailBookParseResult, + recruitingRoomCount, + readCount, + isSaved + ); + }) + .orElseGet(() -> + BookDetailSearchResult.of( + naverDetailBookParseResult, + 0, // 모집 중인 방 개수 + 0, // 읽기 참여자 수 + false // 저장 여부 + ) + ); } private int getRecruitingRoomCount(Book book) { diff --git a/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java b/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java index 4404d9343..09fe96bac 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java @@ -68,12 +68,9 @@ public Long createFeed(FeedCreateCommand command, List images) { * ISBN으로 책을 조회하고, 없으면 외부 API(Naver)에서 상세 정보를 조회해 새로 저장 후 ID 반환 */ private Long findOrCreateBookByIsbn(String isbn) { - try { - Book existing = bookCommandPort.getByIsbnOrThrow(isbn); - return existing.getId(); - } catch (EntityNotFoundException e) { - return saveNewBookWithFromExternalApi(isbn); - } + return bookCommandPort.findByIsbn(isbn) + .map(Book::getId) + .orElseGet(() -> saveNewBookWithFromExternalApi(isbn)); } /** diff --git a/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java b/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java index b6a8b1f2f..48ae56693 100644 --- a/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java +++ b/src/main/java/konkuk/thip/room/application/service/RoomCreateService.java @@ -50,15 +50,14 @@ public Long createRoom(RoomCreateCommand command, Long userId) { } private Long resolveBookAndEnsurePage(String isbn) { - try { - Book existing = bookCommandPort.getByIsbnOrThrow(isbn); - if (!existing.hasPageCount()) { - updateBookPageCount(existing); - } - return existing.getId(); - } catch (EntityNotFoundException e) { - return saveNewBookWithPageCount(isbn); - } + return bookCommandPort.findByIsbn(isbn) + .map(book -> { + if (!book.hasPageCount()) { + updateBookPageCount(book); + } + return book.getId(); + }) + .orElseGet(() -> saveNewBookWithPageCount(isbn)); } private void updateBookPageCount(Book book) { From 89dd638636ff888ecded68d3dd425d6e87004a1b Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 20:41:20 +0900 Subject: [PATCH 13/26] =?UTF-8?q?[refactor]=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=88=98=EC=A0=95=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/application/service/BookSavedService.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java index 2c8e14935..cfbdc97f7 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java @@ -9,7 +9,6 @@ import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.domain.Book; import konkuk.thip.common.exception.BusinessException; -import konkuk.thip.common.exception.InvalidStateException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -68,10 +67,10 @@ private Book registerBookByIsbn(String isbn) { private void validateSaveBookAction(boolean isSaveRequest, boolean alreadySaved) { if (isSaveRequest && alreadySaved) { // 이미 저장되어 있는 책을 다시 저장하려는 경우 예외 처리 - throw new InvalidStateException(BOOK_ALREADY_SAVED); + throw new BusinessException(BOOK_ALREADY_SAVED); } else if (!isSaveRequest && !alreadySaved) { // 저장되지 않은 책을 삭제하려는 경우 예외 처리 - throw new InvalidStateException(BOOK_NOT_SAVED_CANNOT_DELETE); + throw new BusinessException(BOOK_NOT_SAVED_CANNOT_DELETE); } } From cc24720465bbde82b2e2fe54511437d6ea35bee9 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 20:53:30 +0900 Subject: [PATCH 14/26] =?UTF-8?q?[docs]=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20?= =?UTF-8?q?=EB=AA=85=EC=84=B8=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/book/adapter/in/web/BookQueryController.java | 3 ++- .../konkuk/thip/common/swagger/SwaggerResponseDescription.java | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java index d09f66e6c..37b99f167 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/BookQueryController.java @@ -4,12 +4,12 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.Pattern; -import konkuk.thip.book.application.port.in.dto.BookSelectableType; import konkuk.thip.book.adapter.in.web.response.*; import konkuk.thip.book.application.port.in.BookMostSearchUseCase; import konkuk.thip.book.application.port.in.BookRecruitingRoomsUseCase; import konkuk.thip.book.application.port.in.BookSearchUseCase; import konkuk.thip.book.application.port.in.BookSelectableListUseCase; +import konkuk.thip.book.application.port.in.dto.BookSelectableType; import konkuk.thip.common.dto.BaseResponse; import konkuk.thip.common.security.annotation.UserId; import konkuk.thip.common.swagger.annotation.ExceptionDescription; @@ -91,6 +91,7 @@ public BaseResponse showRecruitingRoomsWithBook( summary = "저장한 책 또는 참여 중 모임의 책 조회", description = "저장한 책 또는 참여 중인 모임의 책을 조회합니다. (방 생성시 책 선택 화면 페이지)" ) + @ExceptionDescription(BOOK_SELECTABLE_LIST) @GetMapping("/books/selectable-list") public BaseResponse showSelectableBookList( @Parameter(description = "저장한 책 또는 참여 중인 모임의 책을 구분하는 필드 (SAVED : 저장한 책 / JOINING : 모임 방의 책)", example = "SAVED") @RequestParam final String type, diff --git a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java index 54be1162f..1a0035517 100644 --- a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java +++ b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java @@ -238,6 +238,9 @@ public enum SwaggerResponseDescription { USER_NOT_FOUND, JSON_PROCESSING_ERROR ))), + BOOK_SELECTABLE_LIST(new LinkedHashSet<>(Set.of( + USER_NOT_FOUND + ))), // Recent Search RECENT_SEARCH_DELETE(new LinkedHashSet<>(Set.of( From f5ad9a2ad94c40384c5e94ef6f2410e771337ff9 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Thu, 7 Aug 2025 21:47:58 +0900 Subject: [PATCH 15/26] =?UTF-8?q?[refactor]=20=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EB=B0=A9=20=EC=B1=85=EC=9D=84=20=EB=B0=A9=20=EC=A7=84=ED=96=89?= =?UTF-8?q?=EB=8F=84=20=EB=86=92=EC=9D=80=20=EC=88=9C=20=EC=A0=95=EB=A0=AC?= =?UTF-8?q?=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../adapter/out/persistence/repository/BookJpaRepository.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java index 66ee4e4f2..1754fa15d 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java @@ -21,6 +21,7 @@ public interface BookJpaRepository extends JpaRepository { "JOIN RoomParticipantJpaEntity rp ON rp.roomJpaEntity.roomId = r.roomId " + "WHERE rp.userJpaEntity.userId = :userId " + "AND r.status = 'ACTIVE' " + - "AND r.startDate <= CURRENT_TIMESTAMP") // 진행 중인 방만 조회 (모집 중 / 만료된 방 x) + "AND r.startDate <= CURRENT_TIMESTAMP " + // 진행 중인 방만 조회 (모집 중 / 만료된 방 x) + "ORDER BY r.roomPercentage DESC") // 방의 진행률이 높은 순서로 정렬 List findJoiningRoomsBooksByUserId(Long userId); } From eb5f1973672ffe43f5ff0b9dc156428cf410447a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 8 Aug 2025 02:44:37 +0900 Subject: [PATCH 16/26] =?UTF-8?q?[chore]=20ci-workflow=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 --- .github/workflows/ci-workflow.yml | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 5edd5da69..b210a1166 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -8,6 +8,8 @@ on: permissions: contents: read + checks: write + pull-requests: write env: RESOURCE_PATH: src/main/resources @@ -19,13 +21,6 @@ jobs: steps: - uses: actions/checkout@v4 -# - name: Cache Gradle packages -# uses: actions/cache@v3 -# with: -# path: ~/.gradle/caches -# key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} -# restore-keys: gradle-${{ runner.os }}- - - name: Set up JDK 17 uses: actions/setup-java@v3 with: @@ -50,4 +45,17 @@ jobs: redis-version: 7 - name: 🐘 build with Gradle - run: ./gradlew build \ No newline at end of file + run: ./gradlew clean build --stacktrace + + - name: ✉️ Post test results as a comment on the PR + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + files: '**/build/test-results/test/TEST-*.xml' + + - name: 🙁 Annotate failed test cases on changed lines + uses: mikepenz/action-junit-report@v3 + if: always() + with: + report_paths: '**/build/test-results/test/TEST-*.xml' + token: ${{ github.token }} \ No newline at end of file From 10b2123b7383b07bb0fdaef3022220f70d18b847 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 8 Aug 2025 02:52:23 +0900 Subject: [PATCH 17/26] =?UTF-8?q?[chore]=20ci-workflow=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 --- .github/workflows/ci-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index b210a1166..8cb45cb01 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -45,7 +45,7 @@ jobs: redis-version: 7 - name: 🐘 build with Gradle - run: ./gradlew clean build --stacktrace + run: ./gradlew build --stacktrace - name: ✉️ Post test results as a comment on the PR uses: EnricoMi/publish-unit-test-result-action@v2 From bb52613f6d1307503ed8c3aed541220a993a7b2d Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 8 Aug 2025 02:59:25 +0900 Subject: [PATCH 18/26] =?UTF-8?q?[chore]=20ci=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/adapter/in/web/BookGetSelectableListApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java index 5bdd6f766..4468ca4e8 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java @@ -85,7 +85,7 @@ void getSelectableBooks_joining_success() throws Exception { .requestAttr("userId", user.getUserId())) .andExpect(status().isOk()) .andExpect(jsonPath("$.data.bookList").isArray()) - .andExpect(jsonPath("$.data.bookList.length()").value(1)) + .andExpect(jsonPath("$.data.bookList.length()").value(2)) .andExpect(jsonPath("$.data.bookList[0].isbn").value(joiningBook.getIsbn())); } } \ No newline at end of file From 4e6b00f2a0078236e8451f5fdc7ec4eb114423f3 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 8 Aug 2025 03:03:00 +0900 Subject: [PATCH 19/26] =?UTF-8?q?[chore]=20ci=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../thip/book/adapter/in/web/BookGetSelectableListApiTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java index 4468ca4e8..5bdd6f766 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookGetSelectableListApiTest.java @@ -85,7 +85,7 @@ void getSelectableBooks_joining_success() throws Exception { .requestAttr("userId", user.getUserId())) .andExpect(status().isOk()) .andExpect(jsonPath("$.data.bookList").isArray()) - .andExpect(jsonPath("$.data.bookList.length()").value(2)) + .andExpect(jsonPath("$.data.bookList.length()").value(1)) .andExpect(jsonPath("$.data.bookList[0].isbn").value(joiningBook.getIsbn())); } } \ No newline at end of file From 358766033702f5db2929c1a531a6ff2c2c282b36 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Fri, 8 Aug 2025 03:33:52 +0900 Subject: [PATCH 20/26] =?UTF-8?q?[chore]=20=EB=B9=8C=EB=93=9C=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EB=8B=A8=EC=B6=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci-workflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml index 8cb45cb01..525ecbb7f 100644 --- a/.github/workflows/ci-workflow.yml +++ b/.github/workflows/ci-workflow.yml @@ -45,7 +45,7 @@ jobs: redis-version: 7 - name: 🐘 build with Gradle - run: ./gradlew build --stacktrace + run: ./gradlew build --parallel --stacktrace - name: ✉️ Post test results as a comment on the PR uses: EnricoMi/publish-unit-test-result-action@v2 From 5b4835307d1d1e1a62402c8b38e7a587eec65fef Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 9 Aug 2025 02:13:34 +0900 Subject: [PATCH 21/26] =?UTF-8?q?[refactor]=20Set=20=EC=9E=90=EB=A3=8C?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=8C=80=EC=8B=A0=20DISTINCT=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=9C=EA=B1=B0=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../out/persistence/BookQueryPersistenceAdapter.java | 10 +++++----- .../out/persistence/repository/BookJpaRepository.java | 4 ++-- .../thip/book/application/port/out/BookQueryPort.java | 6 +++--- .../application/service/BookSelectableListService.java | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java index f2e5b28b1..26075f1f5 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/BookQueryPersistenceAdapter.java @@ -11,7 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; -import java.util.Set; +import java.util.List; import java.util.stream.Collectors; import static konkuk.thip.common.exception.code.ErrorCode.USER_NOT_FOUND; @@ -31,23 +31,23 @@ public boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId) { } @Override - public Set findSavedBooksByUserId(Long userId) { + public List findSavedBooksByUserId(Long userId) { UserJpaEntity user = userJpaRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); return bookJpaRepository.findSavedBooksByUserId(user.getUserId()).stream() .map(bookMapper::toDomainEntity) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); } @Override - public Set findJoiningRoomsBooksByUserId(Long userId) { + public List findJoiningRoomsBooksByUserId(Long userId) { UserJpaEntity user = userJpaRepository.findById(userId) .orElseThrow(() -> new EntityNotFoundException(USER_NOT_FOUND)); return bookJpaRepository.findJoiningRoomsBooksByUserId(user.getUserId()) .stream() .map(bookMapper::toDomainEntity) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java index 1754fa15d..9bca9cf9b 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java +++ b/src/main/java/konkuk/thip/book/adapter/out/persistence/repository/BookJpaRepository.java @@ -10,13 +10,13 @@ public interface BookJpaRepository extends JpaRepository { Optional findByIsbn(String isbn); - @Query("SELECT b FROM BookJpaEntity b " + + @Query("SELECT DISTINCT b FROM BookJpaEntity b " + "JOIN SavedBookJpaEntity s ON s.bookJpaEntity.bookId = b.bookId " + "WHERE s.userJpaEntity.userId = :userId " + "ORDER BY s.createdAt DESC") List findSavedBooksByUserId(Long userId); - @Query("SELECT b FROM BookJpaEntity b " + + @Query("SELECT DISTINCT b FROM BookJpaEntity b " + "JOIN RoomJpaEntity r ON r.bookJpaEntity.bookId = b.bookId " + "JOIN RoomParticipantJpaEntity rp ON rp.roomJpaEntity.roomId = r.roomId " + "WHERE rp.userJpaEntity.userId = :userId " + diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java index 3fb443f63..bb9e0ab00 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookQueryPort.java @@ -2,13 +2,13 @@ import konkuk.thip.book.domain.Book; -import java.util.Set; +import java.util.List; public interface BookQueryPort { boolean existsSavedBookByUserIdAndBookId(Long userId, Long bookId); - Set findSavedBooksByUserId(Long userId); + List findSavedBooksByUserId(Long userId); - Set findJoiningRoomsBooksByUserId(Long userId); + List findJoiningRoomsBooksByUserId(Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index df75d30fc..cd047f6ee 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -21,7 +21,7 @@ public class BookSelectableListService implements BookSelectableListUseCase { @Override public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { - Set bookSet = switch(bookSelectableType) { + List bookSet = switch(bookSelectableType) { case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); }; From 2d330a395ebf57e99928f0a027928f26ed955e94 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 9 Aug 2025 02:14:01 +0900 Subject: [PATCH 22/26] =?UTF-8?q?[refactor]=20Set=20=EC=9E=90=EB=A3=8C?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=8C=80=EC=8B=A0=20DISTINCT=20=ED=82=A4?= =?UTF-8?q?=EC=9B=8C=EB=93=9C=EB=A5=BC=20=ED=86=B5=ED=95=B4=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EC=A0=9C=EA=B1=B0=20(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/book/application/mapper/BookQueryMapper.java | 3 +-- .../book/application/service/BookSelectableListService.java | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java index 061d430d0..085d7cbd5 100644 --- a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java +++ b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java @@ -10,7 +10,6 @@ import org.mapstruct.ReportingPolicy; import java.util.List; -import java.util.Set; @Mapper( componentModel = "spring", @@ -35,5 +34,5 @@ public interface BookQueryMapper { @Mapping(target = "isbn", source = "book.isbn") BookInfo toBookInfo(Book book); - List toBookInfoList(Set books); + List toBookInfoList(List books); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index cd047f6ee..1ce58e6c8 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -10,7 +10,6 @@ import org.springframework.stereotype.Service; import java.util.List; -import java.util.Set; @Service @RequiredArgsConstructor @@ -21,11 +20,11 @@ public class BookSelectableListService implements BookSelectableListUseCase { @Override public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { - List bookSet = switch(bookSelectableType) { + List bookList = switch(bookSelectableType) { case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); }; - return bookQueryMapper.toBookInfoList(bookSet); + return bookQueryMapper.toBookInfoList(bookList); } } From 11055e0a5e28d7e46169addb8fbd88d0769ce56a Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 9 Aug 2025 02:19:53 +0900 Subject: [PATCH 23/26] [refactor] BookInfo -> BookSelectableListResult (#156) --- .../in/web/response/BookSelectableListResponse.java | 8 ++++---- .../thip/book/application/mapper/BookQueryMapper.java | 6 +++--- .../application/port/in/BookSelectableListUseCase.java | 4 ++-- .../dto/{BookInfo.java => BookSelectableListResult.java} | 2 +- .../application/service/BookSelectableListService.java | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) rename src/main/java/konkuk/thip/book/application/port/in/dto/{BookInfo.java => BookSelectableListResult.java} (83%) diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java index a8eede590..e3329dc3e 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java @@ -1,13 +1,13 @@ package konkuk.thip.book.adapter.in.web.response; -import konkuk.thip.book.application.port.in.dto.BookInfo; +import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; import java.util.List; public record BookSelectableListResponse( - List bookList + List bookList ) { - public static BookSelectableListResponse of(List bookInfos) { - return new BookSelectableListResponse(bookInfos); + public static BookSelectableListResponse of(List bookSelectableListResults) { + return new BookSelectableListResponse(bookSelectableListResults); } } diff --git a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java index 085d7cbd5..4d1a91ff9 100644 --- a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java +++ b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java @@ -1,7 +1,7 @@ package konkuk.thip.book.application.mapper; import konkuk.thip.book.adapter.in.web.response.BookRecruitingRoomsResponse; -import konkuk.thip.book.application.port.in.dto.BookInfo; +import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; import konkuk.thip.book.domain.Book; import konkuk.thip.common.util.DateUtil; import konkuk.thip.room.application.port.out.dto.RoomQueryDto; @@ -32,7 +32,7 @@ public interface BookQueryMapper { @Mapping(target = "publisher", source = "book.publisher") @Mapping(target = "bookImageUrl", source = "book.imageUrl") @Mapping(target = "isbn", source = "book.isbn") - BookInfo toBookInfo(Book book); + BookSelectableListResult toBookInfo(Book book); - List toBookInfoList(List books); + List toBookInfoList(List books); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java index 24f439901..286eab685 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java +++ b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java @@ -1,10 +1,10 @@ package konkuk.thip.book.application.port.in; -import konkuk.thip.book.application.port.in.dto.BookInfo; +import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; import konkuk.thip.book.application.port.in.dto.BookSelectableType; import java.util.List; public interface BookSelectableListUseCase { - List getSelectableBookList(BookSelectableType bookSelectableType, Long userId); + List getSelectableBookList(BookSelectableType bookSelectableType, Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/dto/BookInfo.java b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableListResult.java similarity index 83% rename from src/main/java/konkuk/thip/book/application/port/in/dto/BookInfo.java rename to src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableListResult.java index aff341cd9..a7ed683e0 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/dto/BookInfo.java +++ b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableListResult.java @@ -1,6 +1,6 @@ package konkuk.thip.book.application.port.in.dto; -public record BookInfo( +public record BookSelectableListResult( Long bookId, String bookTitle, String authorName, diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index 1ce58e6c8..01462d975 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -2,7 +2,7 @@ import konkuk.thip.book.application.mapper.BookQueryMapper; import konkuk.thip.book.application.port.in.BookSelectableListUseCase; -import konkuk.thip.book.application.port.in.dto.BookInfo; +import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; import konkuk.thip.book.application.port.in.dto.BookSelectableType; import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.domain.Book; @@ -19,7 +19,7 @@ public class BookSelectableListService implements BookSelectableListUseCase { private final BookQueryMapper bookQueryMapper; @Override - public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { + public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { List bookList = switch(bookSelectableType) { case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); From 9531892df231973a71ca67c48b912eea86ffa5c5 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 9 Aug 2025 02:22:12 +0900 Subject: [PATCH 24/26] [refactor] BookInfo -> BookSelectableResult (#156) --- .../in/web/response/BookSelectableListResponse.java | 8 ++++---- .../thip/book/application/mapper/BookQueryMapper.java | 6 +++--- .../application/port/in/BookSelectableListUseCase.java | 4 ++-- ...electableListResult.java => BookSelectableResult.java} | 2 +- .../application/service/BookSelectableListService.java | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) rename src/main/java/konkuk/thip/book/application/port/in/dto/{BookSelectableListResult.java => BookSelectableResult.java} (83%) diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java index e3329dc3e..b45bae078 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSelectableListResponse.java @@ -1,13 +1,13 @@ package konkuk.thip.book.adapter.in.web.response; -import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; import java.util.List; public record BookSelectableListResponse( - List bookList + List bookList ) { - public static BookSelectableListResponse of(List bookSelectableListResults) { - return new BookSelectableListResponse(bookSelectableListResults); + public static BookSelectableListResponse of(List bookSelectableResults) { + return new BookSelectableListResponse(bookSelectableResults); } } diff --git a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java index 4d1a91ff9..5f6082eeb 100644 --- a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java +++ b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java @@ -1,7 +1,7 @@ package konkuk.thip.book.application.mapper; import konkuk.thip.book.adapter.in.web.response.BookRecruitingRoomsResponse; -import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; import konkuk.thip.book.domain.Book; import konkuk.thip.common.util.DateUtil; import konkuk.thip.room.application.port.out.dto.RoomQueryDto; @@ -32,7 +32,7 @@ public interface BookQueryMapper { @Mapping(target = "publisher", source = "book.publisher") @Mapping(target = "bookImageUrl", source = "book.imageUrl") @Mapping(target = "isbn", source = "book.isbn") - BookSelectableListResult toBookInfo(Book book); + BookSelectableResult toBookInfo(Book book); - List toBookInfoList(List books); + List toBookInfoList(List books); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java index 286eab685..794453c23 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java +++ b/src/main/java/konkuk/thip/book/application/port/in/BookSelectableListUseCase.java @@ -1,10 +1,10 @@ package konkuk.thip.book.application.port.in; -import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; import konkuk.thip.book.application.port.in.dto.BookSelectableType; import java.util.List; public interface BookSelectableListUseCase { - List getSelectableBookList(BookSelectableType bookSelectableType, Long userId); + List getSelectableBookList(BookSelectableType bookSelectableType, Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableListResult.java b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableResult.java similarity index 83% rename from src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableListResult.java rename to src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableResult.java index a7ed683e0..986767beb 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableListResult.java +++ b/src/main/java/konkuk/thip/book/application/port/in/dto/BookSelectableResult.java @@ -1,6 +1,6 @@ package konkuk.thip.book.application.port.in.dto; -public record BookSelectableListResult( +public record BookSelectableResult( Long bookId, String bookTitle, String authorName, diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index 01462d975..e54e1c4d3 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -2,7 +2,7 @@ import konkuk.thip.book.application.mapper.BookQueryMapper; import konkuk.thip.book.application.port.in.BookSelectableListUseCase; -import konkuk.thip.book.application.port.in.dto.BookSelectableListResult; +import konkuk.thip.book.application.port.in.dto.BookSelectableResult; import konkuk.thip.book.application.port.in.dto.BookSelectableType; import konkuk.thip.book.application.port.out.BookQueryPort; import konkuk.thip.book.domain.Book; @@ -19,7 +19,7 @@ public class BookSelectableListService implements BookSelectableListUseCase { private final BookQueryMapper bookQueryMapper; @Override - public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { + public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { List bookList = switch(bookSelectableType) { case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); From cccc547bc736d2bd03ffb482b30ff1619eb275d5 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sat, 9 Aug 2025 02:58:13 +0900 Subject: [PATCH 25/26] =?UTF-8?q?[refactor]=20Transactional=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80=20(#1?= =?UTF-8?q?56)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../book/application/service/BookSelectableListService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index e54e1c4d3..a44b80320 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -8,6 +8,7 @@ import konkuk.thip.book.domain.Book; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; @@ -19,6 +20,7 @@ public class BookSelectableListService implements BookSelectableListUseCase { private final BookQueryMapper bookQueryMapper; @Override + @Transactional(readOnly = true) public List getSelectableBookList(BookSelectableType bookSelectableType, Long userId) { List bookList = switch(bookSelectableType) { case SAVED -> bookQueryPort.findSavedBooksByUserId(userId); From 1593313f40fbfb6ae8539a5c09d4ef430a2f8f45 Mon Sep 17 00:00:00 2001 From: janghyunjun Date: Sun, 10 Aug 2025 01:43:24 +0900 Subject: [PATCH 26/26] =?UTF-8?q?[refactor]=20mapper=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#156)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../konkuk/thip/book/application/mapper/BookQueryMapper.java | 4 ++-- .../book/application/service/BookSelectableListService.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java index 5f6082eeb..e65264d07 100644 --- a/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java +++ b/src/main/java/konkuk/thip/book/application/mapper/BookQueryMapper.java @@ -32,7 +32,7 @@ public interface BookQueryMapper { @Mapping(target = "publisher", source = "book.publisher") @Mapping(target = "bookImageUrl", source = "book.imageUrl") @Mapping(target = "isbn", source = "book.isbn") - BookSelectableResult toBookInfo(Book book); + BookSelectableResult toBookSelectableResult(Book book); - List toBookInfoList(List books); + List toBookSelectableResultList(List books); } diff --git a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java index a44b80320..af3a13f97 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSelectableListService.java @@ -27,6 +27,6 @@ public List getSelectableBookList(BookSelectableType bookS case JOINING -> bookQueryPort.findJoiningRoomsBooksByUserId(userId); }; - return bookQueryMapper.toBookInfoList(bookList); + return bookQueryMapper.toBookSelectableResultList(bookList); } }