From 775bf14776c7ac39ca85750df7533e68e85942ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 2 Aug 2026 23:14:32 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[fix]=20=EB=84=A4=EC=9D=B4=EB=B2=84=20?= =?UTF-8?q?=EC=B1=85=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20=ED=8C=8C?= =?UTF-8?q?=EC=8B=B1=20=EC=8B=A4=ED=8C=A8=20=EC=8B=9C=20null=20=EB=8C=80?= =?UTF-8?q?=EC=8B=A0=20=EC=98=88=EC=99=B8=EB=A5=BC=20=EB=8D=98=EC=A7=80?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20(#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parseBookDetail()이 channel이 없거나 item이 비어있는 비정상 응답을 받으면 null을 반환해 호출부에서 NPE로 이어지던 문제를 수정. ExternalApiException을 명시적으로 던지도록 변경. Co-Authored-By: Claude Sonnet 5 --- .../out/api/naver/NaverBookXmlParser.java | 81 +++++++++---------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverBookXmlParser.java b/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverBookXmlParser.java index e01a1e1c3..88b5a1e30 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverBookXmlParser.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverBookXmlParser.java @@ -1,7 +1,7 @@ package konkuk.thip.book.adapter.out.api.naver; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import konkuk.thip.common.exception.BusinessException; import konkuk.thip.common.exception.ExternalApiException; import org.apache.commons.lang3.StringEscapeUtils; @@ -19,8 +19,8 @@ public class NaverBookXmlParser { private static final int MAX_DESCRIPTION_LENGTH = 3000; private static final String DESCRIPTION_SUFFIX = "..."; - public static NaverBookParseResult parseBookList(String xml) { - List naverBooks = new ArrayList<>(); + public static BookSearchResult parseBookList(String xml) { + List books = new ArrayList<>(); int total = -1; int start = -1; try { @@ -35,65 +35,64 @@ public static NaverBookParseResult parseBookList(String xml) { String author = getTagValue(item, "author"); String publisher = getTagValue(item, "publisher"); String isbn = getTagValue(item, "isbn"); - NaverBookParseResult.NaverBook naverBook = NaverBookParseResult.NaverBook.builder() + BookSearchResult.BookSummary naverBook = BookSearchResult.BookSummary.builder() .title(title) .imageUrl(imageUrl) .author(author) .publisher(publisher) .isbn(isbn) .build(); - naverBooks.add(naverBook); + books.add(naverBook); } } } catch (Exception e) { throw new ExternalApiException(BOOK_NAVER_API_PARSING_ERROR); } - return NaverBookParseResult.of(naverBooks, total, start); + return BookSearchResult.of(books, total, start); } - public static NaverDetailBookParseResult parseBookDetail(String xml) { + public static BookDetailResult parseBookDetail(String xml) { try { Element channel = getFirstChannel(xml); - if (channel != null) { - int total = 0; - String totalStr = getTagValue(channel, "total"); - if (totalStr != null) total = Integer.parseInt(totalStr); + if (channel == null) throw new ExternalApiException(BOOK_NAVER_API_PARSING_ERROR); - // total이 0이면 isbn에 해당하는 책이 없음(잘못 넘어온 isbn 예외처리) - if (total == 0) throw new BusinessException(BOOK_NAVER_API_ISBN_NOT_FOUND); + int total = 0; + String totalStr = getTagValue(channel, "total"); + if (totalStr != null) total = Integer.parseInt(totalStr); - List items = getItemElements(channel); - if (!items.isEmpty()) { - Element item = items.get(0); - String title = StringEscapeUtils.unescapeHtml4(getTagValue(item, "title")); - String imageUrl = getTagValue(item, "image"); - String author = getTagValue(item, "author"); - String publisher = getTagValue(item, "publisher"); - String isbn = getTagValue(item, "isbn"); - String rawDescription = StringEscapeUtils.unescapeHtml4(getTagValue(item, "description")); - String description; - if (rawDescription.length() > MAX_DESCRIPTION_LENGTH) { - description = rawDescription.substring(0, MAX_DESCRIPTION_LENGTH - DESCRIPTION_SUFFIX.length()) + DESCRIPTION_SUFFIX; - } else { - description = rawDescription; - } - - return NaverDetailBookParseResult.builder() - .title(title) - .imageUrl(imageUrl) - .author(author) - .publisher(publisher) - .isbn(isbn) - .description(description) - .build(); - } + // total이 0이면 isbn에 해당하는 책이 없음(잘못 넘어온 isbn 예외처리) + if (total == 0) throw new BusinessException(BOOK_NAVER_API_ISBN_NOT_FOUND); + + List items = getItemElements(channel); + if (items.isEmpty()) throw new ExternalApiException(BOOK_NAVER_API_PARSING_ERROR); + + Element item = items.get(0); + String title = StringEscapeUtils.unescapeHtml4(getTagValue(item, "title")); + String imageUrl = getTagValue(item, "image"); + String author = getTagValue(item, "author"); + String publisher = getTagValue(item, "publisher"); + String isbn = getTagValue(item, "isbn"); + String rawDescription = StringEscapeUtils.unescapeHtml4(getTagValue(item, "description")); + String description; + if (rawDescription.length() > MAX_DESCRIPTION_LENGTH) { + description = rawDescription.substring(0, MAX_DESCRIPTION_LENGTH - DESCRIPTION_SUFFIX.length()) + DESCRIPTION_SUFFIX; + } else { + description = rawDescription; } - } catch (BusinessException e) { + + return BookDetailResult.builder() + .title(title) + .imageUrl(imageUrl) + .author(author) + .publisher(publisher) + .isbn(isbn) + .description(description) + .build(); + } catch (BusinessException | ExternalApiException e) { throw e; } catch (Exception e) { throw new ExternalApiException(BOOK_NAVER_API_PARSING_ERROR); } - return null; } private static Document parseXml(String xml) throws Exception { From c6889383a64847d2ed47eac5e9398fe4edd9f5fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 2 Aug 2026 23:14:47 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[fix]=20=EC=A2=85=EB=A3=8C=EB=90=9C=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B2=84=20=EC=B1=85=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?API=EB=A5=BC=20=EC=95=8C=EB=9D=BC=EB=94=98=20API=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20(#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 네이버가 책 검색 Open API(book.xml, book_adv.xml)를 종료하여 (SE05: 존재하지 않는 검색 api) 책 검색/상세조회가 500으로 실패하던 문제 해결. - AladinApiUtil에 ItemSearch(키워드 검색) 호출/파싱 추가, ItemLookUp 파싱을 title/author/publisher/cover/description까지 확장 - AladinApiClient/CompositeBookApiAdapter가 naverApiClient 대신 aladinApiClient를 호출하도록 교체 (NaverApiClient/NaverBookXmlParser 등 네이버 관련 클래스는 삭제하지 않고 그대로 유지) - loadBookWithPageByIsbn()이 상세정보+페이지수를 알라딘 ItemLookUp 1회 호출로 함께 조회하도록 통합 (기존엔 naver+aladin 2회 호출) - RestTemplate에 사전 인코딩된 키워드를 그대로 넘기면 이중 인코딩되어 검색이 깨지는 문제가 있어, URI 템플릿 변수로 전달하도록 수정 - 알라딘 정책상 검색 결과가 200건까지만 조회 가능하여, totalElements를 200으로 캡핑해서 페이지네이션 last 플래그가 정확한 지점에서 true가 되도록 처리 Co-Authored-By: Claude Sonnet 5 --- .../out/api/CompositeBookApiAdapter.java | 36 +++---- .../out/api/aladin/AladinApiClient.java | 14 +++ .../out/api/aladin/AladinApiParam.java | 4 +- .../adapter/out/api/aladin/AladinApiUtil.java | 98 +++++++++++++++++-- 4 files changed, 123 insertions(+), 29 deletions(-) diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java index 64f4b1671..f91422292 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java @@ -1,9 +1,9 @@ package konkuk.thip.book.adapter.out.api; import konkuk.thip.book.adapter.out.api.aladin.AladinApiClient; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; -import konkuk.thip.book.adapter.out.api.naver.NaverApiClient; +import konkuk.thip.book.adapter.out.api.aladin.AladinApiUtil; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import konkuk.thip.book.application.port.out.BookApiQueryPort; import konkuk.thip.book.domain.Book; import lombok.RequiredArgsConstructor; @@ -13,17 +13,16 @@ @RequiredArgsConstructor public class CompositeBookApiAdapter implements BookApiQueryPort { - private final NaverApiClient naverApiClient; private final AladinApiClient aladinApiClient; @Override - public NaverBookParseResult findBooksByKeyword(String keyword, int start) { - return naverApiClient.findBooksByKeyword(keyword, start); + public BookSearchResult findBooksByKeyword(String keyword, int start) { + return aladinApiClient.findBooksByKeyword(keyword, start); } @Override - public NaverDetailBookParseResult findDetailBookByIsbn(String isbn) { - return naverApiClient.findDetailBookByIsbn(isbn); + public BookDetailResult 랑(String isbn) { + return aladinApiClient.findDetailBookByIsbn(isbn); } @Override @@ -33,22 +32,19 @@ public Integer findPageCountByIsbn(String isbn) { @Override public Book loadBookWithPageByIsbn(String isbn) { - // 1. naver 상세정보 조회 api 로 책 상세정보(without page) load - NaverDetailBookParseResult detailBookByKeyword = findDetailBookByIsbn(isbn); + // 상세정보 + page 정보를 알라딘 ItemLookUp 한 번의 호출로 함께 조회 + AladinApiUtil.AladinDetailResult result = aladinApiClient.findDetailBookWithPageCountByIsbn(isbn); + BookDetailResult detail = result.detail(); - // 2. 알라딘으로부터 책 page 정보 load - Integer pageCount = findPageCountByIsbn(isbn); - - // 3. pageCount 정보를 포함한 Book 반환 return Book.withoutId( - detailBookByKeyword.title(), + detail.title(), isbn, - detailBookByKeyword.author(), + detail.author(), false, // TODO : 추후 BestSeller 도입되면 고려해야함 - detailBookByKeyword.publisher(), - detailBookByKeyword.imageUrl(), - pageCount, - detailBookByKeyword.description() + detail.publisher(), + detail.imageUrl(), + result.pageCount(), + detail.description() ); } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiClient.java b/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiClient.java index 3b6b12abd..96b67b6a4 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiClient.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiClient.java @@ -1,5 +1,7 @@ package konkuk.thip.book.adapter.out.api.aladin; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -9,7 +11,19 @@ public class AladinApiClient { private final AladinApiUtil aladinApiUtil; + public BookSearchResult findBooksByKeyword(String keyword, int start) { + return aladinApiUtil.searchBooks(keyword, start); + } + + public BookDetailResult findDetailBookByIsbn(String isbn) { + return aladinApiUtil.getBookDetail(isbn); + } + public Integer findPageCountByIsbn(String isbn) { return aladinApiUtil.getPageCount(isbn); } + + public AladinApiUtil.AladinDetailResult findDetailBookWithPageCountByIsbn(String isbn) { + return aladinApiUtil.getBookDetailWithPageCount(isbn); + } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiParam.java b/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiParam.java index a6c51c27a..559f6755f 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiParam.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiParam.java @@ -6,7 +6,9 @@ public enum AladinApiParam { OUTPUT("js"), API_VERSION("20131101"), SUB_INFO_PARSING_KEY("subInfo"), - PAGE_COUNT_PARSING_KEY("itemPage"); + PAGE_COUNT_PARSING_KEY("itemPage"), + QUERY_TYPE("Keyword"), + SEARCH_TARGET("Book"); private final String value; diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java b/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java index 45d936d64..721a2e3a1 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java @@ -2,6 +2,8 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import konkuk.thip.common.exception.ExternalApiException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -10,16 +12,21 @@ import org.springframework.web.client.RestTemplate; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import static konkuk.thip.book.adapter.out.api.aladin.AladinApiParam.*; -import static konkuk.thip.common.exception.code.ErrorCode.BOOK_ALADIN_API_ISBN_NOT_FOUND; -import static konkuk.thip.common.exception.code.ErrorCode.BOOK_ALADIN_API_PARSING_ERROR; +import static konkuk.thip.book.adapter.out.api.dto.BookSearchResult.PAGE_SIZE; +import static konkuk.thip.common.exception.code.ErrorCode.*; @Component @RequiredArgsConstructor @Slf4j public class AladinApiUtil { + // 알라딘 정책상 한 검색어당 실제로 조회 가능한 결과는 최대 200건까지로 제한된다. + private static final int MAX_TOTAL_RESULTS = 200; + private final RestTemplate restTemplate; private final ObjectMapper objectMapper; @@ -29,6 +36,10 @@ public class AladinApiUtil { @Value("${aladin.baseUrl}") private String baseUrl; + @Value("${aladin.searchUrl}") + private String searchUrl; + + public record AladinDetailResult(BookDetailResult detail, Integer pageCount) {} private String buildLookupUrl(String isbn) { return String.format( @@ -41,7 +52,34 @@ private String buildLookupUrl(String isbn) { ); } + // Query 값은 RestTemplate가 URI 템플릿 변수로 한 번만 인코딩하도록 {query} 플레이스홀더로 남겨둔다. + // 여기서 미리 URLEncoder로 인코딩해버리면 RestTemplate이 다시 인코딩(이중 인코딩)하여 키워드가 깨진다. + private String buildSearchUrlTemplate(int page) { + return String.format( + searchUrl + "ttbkey=%s&Query={query}&QueryType=%s&SearchTarget=%s&MaxResults=%s&start=%d&output=%s&Version=%s", + ttbKey, + QUERY_TYPE.getValue(), + SEARCH_TARGET.getValue(), + PAGE_SIZE, + page, + OUTPUT.getValue(), + API_VERSION.getValue() + ); + } + public Integer getPageCount(String isbn) { + return fetchDetail(isbn).pageCount(); + } + + public BookDetailResult getBookDetail(String isbn) { + return fetchDetail(isbn).detail(); + } + + public AladinDetailResult getBookDetailWithPageCount(String isbn) { + return fetchDetail(isbn); + } + + private AladinDetailResult fetchDetail(String isbn) { String url = buildLookupUrl(isbn); String response = restTemplate.getForObject(url, String.class); @@ -50,19 +88,63 @@ public Integer getPageCount(String isbn) { JsonNode items = jsonNode.path("item"); // json 응답 결과에 item 키값이 없는 경우 + // TODO : 알라딘으로부터 page 정보가 없으면 ?? + // 보상 시나리오 : 유저에게 "page 정보를 찾을 수 없는 책입니다. 직접 page 정보를 입력하세요" 라고 안내 + // 일단 지금은 exception throw 만 진행 if (!items.isArray() || items.isEmpty()) { - // TODO : 알라딘으로부터 page 정보가 없으면 ?? - // 보상 시나리오 : 유저에게 "page 정보를 찾을 수 없는 책입니다. 직접 page 정보를 입력하세요" 라고 안내 - // 일단 지금은 exception throw 만 진행 throw new ExternalApiException(BOOK_ALADIN_API_ISBN_NOT_FOUND); } - JsonNode subInfo = items.get(0).path(SUB_INFO_PARSING_KEY.getValue()); + JsonNode item = items.get(0); + JsonNode subInfo = item.path(SUB_INFO_PARSING_KEY.getValue()); + int pageCount = subInfo.path(PAGE_COUNT_PARSING_KEY.getValue()).asInt(); - return subInfo.path(PAGE_COUNT_PARSING_KEY.getValue()).asInt(); + BookDetailResult detail = BookDetailResult.builder() + .title(item.path("title").asText()) + .imageUrl(item.path("cover").asText()) + .author(item.path("author").asText()) + .publisher(item.path("publisher").asText()) + .isbn(item.path("isbn13").asText()) + .description(item.path("description").asText()) + .build(); + + return new AladinDetailResult(detail, pageCount); } catch (IOException e) { throw new ExternalApiException(BOOK_ALADIN_API_PARSING_ERROR); } } -} + public BookSearchResult searchBooks(String keyword, int start) { + // BookSearchService가 넘기는 start는 (page-1)*PAGE_SIZE+1 형태의 아이템 오프셋이므로 + // 알라딘이 요구하는 "페이지 번호"로 역산한다. + int page = ((start - 1) / PAGE_SIZE) + 1; + String urlTemplate = buildSearchUrlTemplate(page); + String response = restTemplate.getForObject(urlTemplate, String.class, keyword); + + try { + JsonNode jsonNode = objectMapper.readTree(response); + // totalResults는 전체 매칭 건수를 그대로 보고하지만 실제로 조회 가능한 건 MAX_TOTAL_RESULTS까지뿐이므로, + // 페이지네이션(totalPages/last)이 정확히 그 지점에서 끝나도록 캡핑한다. + int total = Math.min(jsonNode.path("totalResults").asInt(), MAX_TOTAL_RESULTS); + int startIndex = jsonNode.path("startIndex").asInt(); + + List books = new ArrayList<>(); + JsonNode items = jsonNode.path("item"); + if (items.isArray()) { + for (JsonNode item : items) { + books.add(BookSearchResult.BookSummary.builder() + .title(item.path("title").asText()) + .imageUrl(item.path("cover").asText()) + .author(item.path("author").asText()) + .publisher(item.path("publisher").asText()) + .isbn(item.path("isbn13").asText()) + .build()); + } + } + + return BookSearchResult.of(books, total, startIndex); + } catch (IOException e) { + throw new ExternalApiException(BOOK_ALADIN_API_PARSING_ERROR); + } + } +} From 3d93566a15b28a59163be04805b066cf9bc6c583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 2 Aug 2026 23:15:04 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[refactor]=20=EC=B1=85=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=20API=20DTO=EB=A5=BC=20provider-=EC=A4=91=EB=A6=BD=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EC=9D=B4=EB=A6=84=EC=9C=BC=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BookApiQueryPort는 provider(네이버/알라딘)에 무관해야 하는데 반환 타입에 "Naver"가 박혀있어 실제로는 알라딘 데이터를 담는데도 이름이 헷갈리는 문제가 있었음. - NaverBookParseResult -> BookSearchResult, 중첩 타입 NaverBook -> BookSummary - NaverDetailBookParseResult -> BookDetailResult - 위 리네이밍에 따라 다운스트림 전체(BookSearchService, BookApiQueryPort, BookDetailSearchResult 등) 및 테스트 코드의 import/타입명 갱신 - 리네이밍 김에 naverDetailBook/naverResult/naverBook 등 남아있던 변수·필드명도 provider-중립적으로 함께 정리 로직 변경 없음, 순수 리네이밍. Co-Authored-By: Claude Sonnet 5 --- .../response/BookDetailSearchResponse.java | 12 +++++------ .../web/response/BookSearchListResponse.java | 20 +++++++++---------- ...ParseResult.java => BookDetailResult.java} | 2 +- ...ParseResult.java => BookSearchResult.java} | 13 +++++++----- .../adapter/out/api/naver/NaverApiClient.java | 8 ++++---- .../port/in/BookSearchUseCase.java | 4 ++-- .../port/in/dto/BookDetailSearchResult.java | 8 ++++---- .../port/out/BookApiQueryPort.java | 8 ++++---- .../service/BookMostSearchRankService.java | 10 +++++----- .../service/BookSearchService.java | 16 +++++++-------- .../in/web/BookDetailSearchApiTest.java | 2 +- 11 files changed, 53 insertions(+), 50 deletions(-) rename src/main/java/konkuk/thip/book/adapter/out/api/dto/{NaverDetailBookParseResult.java => BookDetailResult.java} (86%) rename src/main/java/konkuk/thip/book/adapter/out/api/dto/{NaverBookParseResult.java => BookSearchResult.java} (51%) diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDetailSearchResponse.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDetailSearchResponse.java index 522fb354d..9dbac1b31 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDetailSearchResponse.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookDetailSearchResponse.java @@ -18,12 +18,12 @@ public record BookDetailSearchResponse( public static BookDetailSearchResponse of(BookDetailSearchResult result) { return new BookDetailSearchResponse( - result.naverDetailBook().title(), - result.naverDetailBook().imageUrl(), - result.naverDetailBook().author(), - result.naverDetailBook().publisher(), - result.naverDetailBook().isbn(), - result.naverDetailBook().description(), + result.bookDetail().title(), + result.bookDetail().imageUrl(), + result.bookDetail().author(), + result.bookDetail().publisher(), + result.bookDetail().isbn(), + result.bookDetail().description(), result.recruitingRoomCount(), result.readCount(), result.isSaved()); diff --git a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSearchListResponse.java b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSearchListResponse.java index be5e8431a..4c3b8bc61 100644 --- a/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSearchListResponse.java +++ b/src/main/java/konkuk/thip/book/adapter/in/web/response/BookSearchListResponse.java @@ -1,10 +1,10 @@ package konkuk.thip.book.adapter.in.web.response; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; import java.util.List; -import static konkuk.thip.book.adapter.out.api.naver.NaverApiUtil.PAGE_SIZE; +import static konkuk.thip.book.adapter.out.api.dto.BookSearchResult.PAGE_SIZE; public record BookSearchListResponse( List searchResult, // 책 목록 @@ -15,13 +15,13 @@ public record BookSearchListResponse( boolean last, // 마지막 페이지 여부 boolean first // 첫 페이지 여부 ) { - public static BookSearchListResponse of(NaverBookParseResult result, int page) { + public static BookSearchListResponse of(BookSearchResult result, int page) { int totalElements = result.total(); int totalPages = (int) Math.ceil((double) totalElements / PAGE_SIZE); boolean last = (page >= totalPages); boolean first = (page == 1); - List bookSearchDtos = result.naverBooks().stream() + List bookSearchDtos = result.books().stream() .map(BookSearchDto::of) .toList(); @@ -42,13 +42,13 @@ public record BookSearchDto( String publisher, String isbn ) { - public static BookSearchDto of(NaverBookParseResult.NaverBook naverBook) { + public static BookSearchDto of(BookSearchResult.BookSummary book) { return new BookSearchDto( - naverBook.title(), - naverBook.imageUrl(), - naverBook.author(), - naverBook.publisher(), - naverBook.isbn() + book.title(), + book.imageUrl(), + book.author(), + book.publisher(), + book.isbn() ); } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/dto/NaverDetailBookParseResult.java b/src/main/java/konkuk/thip/book/adapter/out/api/dto/BookDetailResult.java similarity index 86% rename from src/main/java/konkuk/thip/book/adapter/out/api/dto/NaverDetailBookParseResult.java rename to src/main/java/konkuk/thip/book/adapter/out/api/dto/BookDetailResult.java index 511455a40..2d7addd67 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/dto/NaverDetailBookParseResult.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/dto/BookDetailResult.java @@ -4,7 +4,7 @@ import lombok.Builder; @Builder -public record NaverDetailBookParseResult( +public record BookDetailResult( String title, String imageUrl, String author, diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/dto/NaverBookParseResult.java b/src/main/java/konkuk/thip/book/adapter/out/api/dto/BookSearchResult.java similarity index 51% rename from src/main/java/konkuk/thip/book/adapter/out/api/dto/NaverBookParseResult.java rename to src/main/java/konkuk/thip/book/adapter/out/api/dto/BookSearchResult.java index 1ee7ec7af..f52dc6e5f 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/dto/NaverBookParseResult.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/dto/BookSearchResult.java @@ -5,19 +5,22 @@ import java.util.List; -public record NaverBookParseResult( - List naverBooks, +public record BookSearchResult( + List books, int total, int start) { + + public static final int PAGE_SIZE = 10; + @Builder - public record NaverBook( + public record BookSummary( String title, String imageUrl, String author, String publisher, String isbn ) {} - public static NaverBookParseResult of(List books, int total, int start) { - return new NaverBookParseResult(books, total, start); + public static BookSearchResult of(List books, int total, int start) { + return new BookSearchResult(books, total, start); } } diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverApiClient.java b/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverApiClient.java index 54d29ba8e..be1bd956a 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverApiClient.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverApiClient.java @@ -1,7 +1,7 @@ package konkuk.thip.book.adapter.out.api.naver; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -11,12 +11,12 @@ public class NaverApiClient { private final NaverApiUtil naverApiUtil; - public NaverBookParseResult findBooksByKeyword(String keyword, int start) { + public BookSearchResult findBooksByKeyword(String keyword, int start) { String xml = naverApiUtil.searchBook(keyword, start); // 네이버 API 호출 return NaverBookXmlParser.parseBookList(xml); // XML 파싱 + 페이징 정보 포함 } - public NaverDetailBookParseResult findDetailBookByIsbn(String isbn) { + public BookDetailResult findDetailBookByIsbn(String isbn) { String xml = naverApiUtil.detailSearchBook(isbn); // 네이버 API 호출 return NaverBookXmlParser.parseBookDetail(xml); // XML 파싱 } diff --git a/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java b/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java index 4f7cdfed9..563c5725a 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java +++ b/src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java @@ -1,10 +1,10 @@ package konkuk.thip.book.application.port.in; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; import konkuk.thip.book.application.port.in.dto.BookDetailSearchResult; public interface BookSearchUseCase { - NaverBookParseResult searchBooks(String keyword, int page, Long userId, boolean isFinalized); + BookSearchResult searchBooks(String keyword, int page, Long userId, boolean isFinalized); BookDetailSearchResult searchDetailBooks(String isbn, Long userId); } diff --git a/src/main/java/konkuk/thip/book/application/port/in/dto/BookDetailSearchResult.java b/src/main/java/konkuk/thip/book/application/port/in/dto/BookDetailSearchResult.java index 17ffe4a8e..136f15091 100644 --- a/src/main/java/konkuk/thip/book/application/port/in/dto/BookDetailSearchResult.java +++ b/src/main/java/konkuk/thip/book/application/port/in/dto/BookDetailSearchResult.java @@ -1,22 +1,22 @@ package konkuk.thip.book.application.port.in.dto; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; public record BookDetailSearchResult( - NaverDetailBookParseResult naverDetailBook, + BookDetailResult bookDetail, int recruitingRoomCount, int readCount, boolean isSaved ) { - public static BookDetailSearchResult of(NaverDetailBookParseResult naverDetailBook , + public static BookDetailSearchResult of(BookDetailResult bookDetail, int recruitingRoomCount, int readCount, boolean isSaved) { return new BookDetailSearchResult( - naverDetailBook, + bookDetail, recruitingRoomCount, readCount, isSaved); diff --git a/src/main/java/konkuk/thip/book/application/port/out/BookApiQueryPort.java b/src/main/java/konkuk/thip/book/application/port/out/BookApiQueryPort.java index 803f57b82..64ac1b654 100644 --- a/src/main/java/konkuk/thip/book/application/port/out/BookApiQueryPort.java +++ b/src/main/java/konkuk/thip/book/application/port/out/BookApiQueryPort.java @@ -1,14 +1,14 @@ package konkuk.thip.book.application.port.out; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import konkuk.thip.book.domain.Book; public interface BookApiQueryPort { - NaverBookParseResult findBooksByKeyword(String keyword, int start); + BookSearchResult findBooksByKeyword(String keyword, int start); - NaverDetailBookParseResult findDetailBookByIsbn(String isbn); + BookDetailResult findDetailBookByIsbn(String isbn); Integer findPageCountByIsbn(String isbn); 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 50d90c1a5..25818d777 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java @@ -1,6 +1,6 @@ package konkuk.thip.book.application.service; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import konkuk.thip.book.application.port.in.dto.BookMostSearchResult; import konkuk.thip.book.application.port.out.BookApiQueryPort; import konkuk.thip.book.application.port.out.BookCommandPort; @@ -64,13 +64,13 @@ public void updateDailySearchRank() { .build() ), () -> { - // DB에 없으면 Naver API에서 상세 정보 조회 - NaverDetailBookParseResult naverResult = bookApiQueryPort.findDetailBookByIsbn(isbn); + // DB에 없으면 외부 API에서 상세 정보 조회 + BookDetailResult bookDetail = bookApiQueryPort.findDetailBookByIsbn(isbn); bookRankDetails.add( BookMostSearchResult.BookRankInfo.builder() .rank(rank.getAndIncrement()) - .title(naverResult.title()) - .imageUrl(naverResult.imageUrl()) + .title(bookDetail.title()) + .imageUrl(bookDetail.imageUrl()) .isbn(isbn) .build() ); 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 617a27d4d..65702d1cf 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSearchService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSearchService.java @@ -1,7 +1,7 @@ package konkuk.thip.book.application.service; -import konkuk.thip.book.adapter.out.api.dto.NaverBookParseResult; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; +import konkuk.thip.book.adapter.out.api.dto.BookSearchResult; +import konkuk.thip.book.adapter.out.api.dto.BookDetailResult; import konkuk.thip.book.application.port.in.BookSearchUseCase; import konkuk.thip.book.application.port.in.dto.BookDetailSearchResult; import konkuk.thip.book.application.port.out.BookApiQueryPort; @@ -25,7 +25,7 @@ import java.util.HashSet; import java.util.Set; -import static konkuk.thip.book.adapter.out.api.naver.NaverApiUtil.PAGE_SIZE; +import static konkuk.thip.book.adapter.out.api.dto.BookSearchResult.PAGE_SIZE; import static konkuk.thip.common.exception.code.ErrorCode.*; import static konkuk.thip.recentSearch.domain.value.RecentSearchType.BOOK_SEARCH; @@ -47,7 +47,7 @@ public class BookSearchService implements BookSearchUseCase { @Override @Transactional - public NaverBookParseResult searchBooks(String keyword, int page, Long userId, boolean isFinalized) { + public BookSearchResult searchBooks(String keyword, int page, Long userId, boolean isFinalized) { if (keyword == null || keyword.isBlank()) { throw new BusinessException(BOOK_KEYWORD_REQUIRED); @@ -59,7 +59,7 @@ public NaverBookParseResult searchBooks(String keyword, int page, Long userId, b int start = (page - 1) * PAGE_SIZE + 1; //검색 시작 위치 - NaverBookParseResult result = bookApiQueryPort.findBooksByKeyword(keyword, start); + BookSearchResult result = bookApiQueryPort.findBooksByKeyword(keyword, start); int totalElements = result.total(); int totalPages = (totalElements + PAGE_SIZE - 1) / PAGE_SIZE; @@ -81,7 +81,7 @@ public BookDetailSearchResult searchDetailBooks(String isbn,Long userId) { User user = userCommandPort.findById(userId); //책 상세정보 - NaverDetailBookParseResult naverDetailBookParseResult = bookApiQueryPort.findDetailBookByIsbn(isbn); + BookDetailResult bookDetailResult = bookApiQueryPort.findDetailBookByIsbn(isbn); //책 검색순위 정보 업데이트 bookRedisCommandPort.incrementBookSearchCount(isbn,LocalDate.now()); @@ -96,7 +96,7 @@ public BookDetailSearchResult searchDetailBooks(String isbn,Long userId) { boolean isSaved = bookQueryPort.existsSavedBookByUserIdAndBookId(user.getId(), book.getId()); return BookDetailSearchResult.of( - naverDetailBookParseResult, + bookDetailResult, recruitingRoomCount, readCount, isSaved @@ -104,7 +104,7 @@ public BookDetailSearchResult searchDetailBooks(String isbn,Long userId) { }) .orElseGet(() -> BookDetailSearchResult.of( - naverDetailBookParseResult, + bookDetailResult, 0, // 모집 중인 방 개수 0, // 읽기 참여자 수 false // 저장 여부 diff --git a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchApiTest.java b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchApiTest.java index 608f77ece..adb10fde5 100644 --- a/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchApiTest.java +++ b/src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchApiTest.java @@ -131,7 +131,7 @@ void searchDetailBooks_ReturnsCorrectResult() { assertThat(result.recruitingRoomCount()).isEqualTo(1); assertThat(result.readCount()).isEqualTo(1); assertThat(result.isSaved()).isTrue(); - assertThat(result.naverDetailBook()).isNotNull(); + assertThat(result.bookDetail()).isNotNull(); } @Test From a91cfc5e28fd07136bd1004a4444a7bdeb73a7d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 2 Aug 2026 23:15:20 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[refactor]=20=EC=B1=85=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=20=EC=8B=9C=20=EC=95=8C=EB=9D=BC=EB=94=98=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98=EA=B9=8C=EC=A7=80=20?= =?UTF-8?q?=ED=95=9C=20=EB=B2=88=EC=97=90=20=EC=A1=B0=ED=9A=8C=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20(#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FeedCreateService/BookSavedService가 책을 새로 저장할 때 상세정보만 조회하고 페이지 수는 null로 저장한 뒤, 방 생성 시점에 별도로 채워넣는 구조였음. 네이버(상세)+알라딘(페이지)로 provider가 나뉘어 있던 시절엔 합리적인 절약이었으나, 이제 알라딘 ItemLookUp 하나가 상세정보+페이지수를 함께 주기 때문에 더 이상 아낄 이유가 없음. - 두 서비스 모두 findDetailBookByIsbn() 대신 이미 있던 bookApiQueryPort.loadBookWithPageByIsbn(isbn)을 사용하도록 변경 (API 호출 횟수는 그대로 1회, 페이지 수까지 함께 저장됨) - RoomCreateService의 페이지 수 백필 로직은 기존 DB 데이터(페이지 수 null인 책들)를 위한 안전장치로 유지 Co-Authored-By: Claude Sonnet 5 --- .../application/service/BookSavedService.java | 14 ++------------ .../application/service/FeedCreateService.java | 16 +++------------- 2 files changed, 5 insertions(+), 25 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 3cf3a9196..6f393b38b 100644 --- a/src/main/java/konkuk/thip/book/application/service/BookSavedService.java +++ b/src/main/java/konkuk/thip/book/application/service/BookSavedService.java @@ -1,6 +1,5 @@ package konkuk.thip.book.application.service; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; import konkuk.thip.book.application.port.in.BookSavedUseCase; import konkuk.thip.book.application.port.in.dto.BookIsSavedResult; import konkuk.thip.book.application.port.out.BookApiQueryPort; @@ -48,17 +47,8 @@ public BookIsSavedResult changeSavedBook(String isbn, boolean isSaveRequest, Lon } 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()); + // 저장 요청이면 외부 API로 책 정보 + 페이지 수를 함께 조회 후 저장 + Book newBook = bookApiQueryPort.loadBookWithPageByIsbn(isbn); Long savedBookId = bookCommandPort.save(newBook); return bookCommandPort.findById(savedBookId); 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 b439dfcb5..9e7e66b72 100644 --- a/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java +++ b/src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java @@ -1,6 +1,5 @@ package konkuk.thip.feed.application.service; -import konkuk.thip.book.adapter.out.api.dto.NaverDetailBookParseResult; import konkuk.thip.book.application.port.out.BookApiQueryPort; import konkuk.thip.book.application.port.out.BookCommandPort; import konkuk.thip.book.domain.Book; @@ -76,7 +75,7 @@ private void sendNotifications(FeedCreateCommand command, Long savedFeedId) { } /** - * ISBN으로 책을 조회하고, 없으면 외부 API(Naver)에서 상세 정보를 조회해 새로 저장 후 ID 반환 + * ISBN으로 책을 조회하고, 없으면 외부 API에서 상세 정보(+페이지 수)를 조회해 새로 저장 후 ID 반환 */ private Long findOrCreateBookByIsbn(String isbn) { return bookCommandPort.findByIsbn(isbn) @@ -85,19 +84,10 @@ private Long findOrCreateBookByIsbn(String isbn) { } /** - * 외부 API(Naver)를 통해 상세 책 정보를 조회하고 Book 도메인으로 저장 + * 외부 API를 통해 상세 책 정보와 페이지 수를 함께 조회하여 Book 도메인으로 저장 */ private Long saveNewBookWithFromExternalApi(String isbn) { - NaverDetailBookParseResult detailBookByKeyword = bookApiQueryPort.findDetailBookByIsbn(isbn); - Book newBook = Book.withoutId( - detailBookByKeyword.title(), - detailBookByKeyword.isbn(), - detailBookByKeyword.author(), - false, // TODO : 추후 BestSeller 도입 시 로직 수정 - detailBookByKeyword.publisher(), - detailBookByKeyword.imageUrl(), - null, - detailBookByKeyword.description()); + Book newBook = bookApiQueryPort.loadBookWithPageByIsbn(isbn); return bookCommandPort.save(newBook); } } From 4af46fd6ba57b67cd49aa3a11c49d59acf24ff03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 2 Aug 2026 23:15:31 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[chore]=20=EC=95=8C=EB=9D=BC=EB=94=98=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=EC=97=90=20=EB=A7=9E=EC=B6=B0=20Swagger=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=EC=BD=94=EB=93=9C=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=EC=A0=95=EC=A0=95=20(#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FEED_CREATE/BOOK_SEARCH/BOOK_DETAIL_SEARCH API 문서에 BOOK_NAVER_API_* 에러코드가 나열되어 있었는데, 이제 이 API들은 네이버를 호출하지 않아 해당 에러를 절대 던질 수 없음. 실제로 발생 가능한 BOOK_ALADIN_API_PARSING_ERROR / BOOK_ALADIN_API_ISBN_NOT_FOUND로 교체 (키워드 검색은 ISBN_NOT_FOUND를 던지지 않으므로 PARSING_ERROR만 남김). Co-Authored-By: Claude Sonnet 5 --- .../common/swagger/SwaggerResponseDescription.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java index fa5b606cd..de9dde63c 100644 --- a/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java +++ b/src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java @@ -223,7 +223,8 @@ public enum SwaggerResponseDescription { TAG_NAME_NOT_MATCH, TAG_SHOULD_BE_UNIQUE, TAG_LIST_SIZE_OVERFLOW, - BOOK_NAVER_API_PARSING_ERROR, + BOOK_ALADIN_API_PARSING_ERROR, + BOOK_ALADIN_API_ISBN_NOT_FOUND, URL_INVALID_DOMAIN, URL_USER_ID_MISMATCH ))), @@ -322,15 +323,12 @@ public enum SwaggerResponseDescription { BOOK_SEARCH_PAGE_OUT_OF_RANGE, BOOK_KEYWORD_REQUIRED, BOOK_PAGE_NUMBER_INVALID, - BOOK_NAVER_API_PARSING_ERROR, - BOOK_NAVER_API_URL_HTTP_CONNECT_FAILED, - BOOK_NAVER_API_RESPONSE_ERROR + BOOK_ALADIN_API_PARSING_ERROR ))), BOOK_DETAIL_SEARCH(new LinkedHashSet<>(Set.of( BOOK_NOT_FOUND, - BOOK_NAVER_API_PARSING_ERROR, - BOOK_NAVER_API_ISBN_NOT_FOUND, - BOOK_NAVER_API_URL_HTTP_CONNECT_FAILED + BOOK_ALADIN_API_PARSING_ERROR, + BOOK_ALADIN_API_ISBN_NOT_FOUND ))), POPULAR_BOOK_SEARCH(new LinkedHashSet<>(Set.of( USER_NOT_FOUND, From d245fdf9e21bfbf7ebb66acbe6cef0943e6d07fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=ED=9D=AC=EC=A7=84?= Date: Sun, 2 Aug 2026 23:19:56 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[fix]=20CompositeBookApiAdapter=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=EB=AA=85=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#372)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit findDetailBookByIsbn() 메서드명이 편집 과정에서 깨져 있어 컴파일이 되지 않던 문제 수정. c6889383에서 함께 반영됐어야 할 내용. Co-Authored-By: Claude Sonnet 5 --- .../thip/book/adapter/out/api/CompositeBookApiAdapter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java b/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java index f91422292..789bba183 100644 --- a/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java +++ b/src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java @@ -21,7 +21,7 @@ public BookSearchResult findBooksByKeyword(String keyword, int start) { } @Override - public BookDetailResult 랑(String isbn) { + public BookDetailResult findDetailBookByIsbn(String isbn) { return aladinApiClient.findDetailBookByIsbn(isbn); }