Skip to content

[fix] 종료된 네이버 책 API를 알라딘으로 전환 (#372) - #373

Merged
hd0rable merged 6 commits into
developfrom
fix/#372-naver-to-aladin-book-api
Aug 2, 2026
Merged

[fix] 종료된 네이버 책 API를 알라딘으로 전환 (#372)#373
hd0rable merged 6 commits into
developfrom
fix/#372-naver-to-aladin-book-api

Conversation

@hd0rable

@hd0rable hd0rable commented Aug 2, 2026

Copy link
Copy Markdown
Member

#️⃣ 연관된 이슈

closes #372

📝 작업 내용

문제 상황

2026-08-02 17:35:28, 유저(id=332)의 /books/{isbn} 상세검색 요청에서 NPE 발생.
조사 결과 네이버가 책 검색 Open API(book.xml, book_adv.xml)를 완전히 종료한 것으로 확인됨
(등록된 client ID로 호출해도 errorCode: SE05, 존재하지 않는 검색 api 응답). 책 검색(/books),
책 상세조회(/books/{isbn}) 두 API 모두 정상 동작하지 않는 상태였음.

원인

NaverBookXmlParser.parseBookDetail()이 파싱 실패(channel 없음/item 없음) 시 예외 없이 null
반환했고, 이 nullBookDetailSearchResult.naverDetailBook에 그대로 담겨 컨트롤러 응답 생성
단계에서 NPE로 이어짐. 근본 원인은 네이버 API 자체가 더 이상 응답하지 않는다는 것.

해결

이미 부분적으로 연동되어 있던 알라딘(Aladin) Open API로 책 검색/상세조회를 전면 전환함.

  • 네이버 파싱 실패 시 null 대신 예외를 던지도록 수정 (NaverBookXmlParser)
  • 알라딘 ItemSearch(키워드 검색) 연동 추가, ItemLookUp(상세조회) 파싱을 title/author/publisher/
    cover/description까지 확장 (AladinApiUtil/AladinApiClient)
  • CompositeBookApiAdapter가 네이버 대신 알라딘을 호출하도록 교체.
    NaverApiClient/NaverBookXmlParser 등 네이버 관련 클래스는 삭제하지 않고 그대로 유지
    (롤백 대비 및 향후 폴백 연동 시 참고용)
  • 책 상세조회 + 페이지 수 조회를 알라딘 ItemLookUp 1회 호출로 통합 (기존 naver+aladin 2회 호출 → 1회)
  • RestTemplate에 사전 인코딩된 키워드를 그대로 넘기면 이중 인코딩되어 검색이 깨지는 버그 발견 및 수정
  • 알라딘 정책상 검색 결과가 최대 200건까지만 조회 가능하여, totalElements를 200으로 캡핑해서
    페이지네이션 last 플래그가 정확한 지점에서 true가 되도록 처리
  • BookApiQueryPort 반환 DTO를 provider-중립적인 이름으로 변경
    (NaverBookParseResultBookSearchResult, NaverDetailBookParseResultBookDetailResult 등)
  • FeedCreateService/BookSavedService가 책을 새로 저장할 때 페이지 수를 null로 저장하던 것을,
    알라딘이 상세정보+페이지수를 한 번에 주므로 loadBookWithPageByIsbn()으로 함께 저장하도록 변경
  • Swagger 문서에 남아있던 BOOK_NAVER_API_* 에러코드를 실제로 발생 가능한
    BOOK_ALADIN_API_*로 정정

API 응답 필드 구조(JSON shape)는 변경하지 않음. 테스트 501개 전체 통과 확인.

📸 스크린샷

💬 리뷰 요구사항

  • 프론트 확인 필요: 알라딘 전환으로 책 표지 이미지 도메인이 image.aladin.co.kr로 바뀝니다.
    이미지 도메인 화이트리스트(Next.js remotePatterns, CSP img-src 등)에 추가 필요.
  • 프론트 확인 필요: 알라딘 응답의 author 필드는 "J.K. 롤링 (지은이), 강동혁 (옮긴이)"처럼
    역할 표시가 괄호로 붙어서 옵니다. 저자명을 그대로 노출하는 화면이 있다면 확인 필요.
  • 프론트 확인 필요: BOOK_NAVER_API_* (80001~80009) 에러코드는 이제 발생하지 않고, 대신
    BOOK_ALADIN_API_PARSING_ERROR(80015), BOOK_ALADIN_API_ISBN_NOT_FOUND(80016)로 옵니다.
    프론트 에러코드 매핑에 반영 필요.
  • 알라딘은 단일 장애점(SPOF)이라, 추후 카카오/국립중앙도서관 서지정보 API로의 폴백 연동을 고려해볼 만합니다 (지금 PR 범위 아님).

📌 PR 진행 시 이러한 점들을 참고해 주세요

* P1 : 꼭 반영해 주세요 (Request Changes) - 이슈가 발생하거나 취약점이 발견되는 케이스 등
* P2 : 반영을 적극적으로 고려해 주시면 좋을 것 같아요 (Comment)
* P3 : 이런 방법도 있을 것 같아요~ 등의 사소한 의견입니다 (Chore)

Summary by CodeRabbit

  • 새 기능

    • 도서 검색 및 상세 조회가 Aladin 기반으로 제공됩니다.
    • ISBN 조회 시 도서 정보와 페이지 수를 함께 확인할 수 있습니다.
    • 도서 검색 결과의 공통 형식이 적용되었습니다.
  • 버그 수정

    • 도서 상세 정보와 검색 목록이 일관된 결과를 표시하도록 개선되었습니다.
    • ISBN이 없거나 외부 응답을 처리할 수 없는 경우 관련 오류 안내가 정비되었습니다.

hd0rable and others added 6 commits August 2, 2026 23:14
parseBookDetail()이 channel이 없거나 item이 비어있는 비정상 응답을 받으면
null을 반환해 호출부에서 NPE로 이어지던 문제를 수정. ExternalApiException을
명시적으로 던지도록 변경.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
네이버가 책 검색 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 <noreply@anthropic.com>
BookApiQueryPort는 provider(네이버/알라딘)에 무관해야 하는데 반환 타입에
"Naver"가 박혀있어 실제로는 알라딘 데이터를 담는데도 이름이 헷갈리는 문제가 있었음.

- NaverBookParseResult -> BookSearchResult, 중첩 타입 NaverBook -> BookSummary
- NaverDetailBookParseResult -> BookDetailResult
- 위 리네이밍에 따라 다운스트림 전체(BookSearchService, BookApiQueryPort,
  BookDetailSearchResult 등) 및 테스트 코드의 import/타입명 갱신
- 리네이밍 김에 naverDetailBook/naverResult/naverBook 등 남아있던
  변수·필드명도 provider-중립적으로 함께 정리

로직 변경 없음, 순수 리네이밍.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FeedCreateService/BookSavedService가 책을 새로 저장할 때 상세정보만 조회하고
페이지 수는 null로 저장한 뒤, 방 생성 시점에 별도로 채워넣는 구조였음.
네이버(상세)+알라딘(페이지)로 provider가 나뉘어 있던 시절엔 합리적인 절약이었으나,
이제 알라딘 ItemLookUp 하나가 상세정보+페이지수를 함께 주기 때문에 더 이상
아낄 이유가 없음.

- 두 서비스 모두 findDetailBookByIsbn() 대신 이미 있던
  bookApiQueryPort.loadBookWithPageByIsbn(isbn)을 사용하도록 변경
  (API 호출 횟수는 그대로 1회, 페이지 수까지 함께 저장됨)
- RoomCreateService의 페이지 수 백필 로직은 기존 DB 데이터(페이지 수 null인
  책들)를 위한 안전장치로 유지

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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 <noreply@anthropic.com>
findDetailBookByIsbn() 메서드명이 편집 과정에서 깨져 있어 컴파일이
되지 않던 문제 수정. c6889383에서 함께 반영됐어야 할 내용.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Naver 전용 도서 검색·상세 DTO를 공통 DTO로 변경했습니다. 검색과 ISBN 상세 조회를 Aladin API에 위임했습니다. 상세 정보와 페이지 수를 한 번에 조회하도록 변경하고, 서비스·웹 응답·오류 코드·테스트를 새 결과 타입에 맞게 수정했습니다.

Changes

도서 API 전환

Layer / File(s) Summary
Aladin API 조회 및 결과 모델
src/main/java/konkuk/thip/book/adapter/out/api/aladin/*, src/main/java/konkuk/thip/book/adapter/out/api/dto/Book*Result.java
Aladin 키워드 검색과 ISBN 상세 조회를 추가했습니다. 상세 정보와 페이지 수를 함께 파싱합니다. 검색 결과는 최대 200건으로 제한합니다.
공통 결과 타입과 API 어댑터 연결
src/main/java/konkuk/thip/book/application/port/out/BookApiQueryPort.java, src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java, src/main/java/konkuk/thip/book/adapter/out/api/naver/*
Naver 전용 반환 타입을 BookSearchResultBookDetailResult로 변경했습니다. CompositeBookApiAdapter는 검색과 상세 조회를 Aladin에 위임합니다.
서비스 도서 조회 흐름 갱신
src/main/java/konkuk/thip/book/application/port/in/*, src/main/java/konkuk/thip/book/application/service/*, src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java
검색·상세 서비스가 공통 결과 타입을 사용합니다. 저장 및 피드 생성은 상세 정보와 페이지 수를 함께 조회한 Book을 사용합니다.
웹 응답과 오류 계약 갱신
src/main/java/konkuk/thip/book/adapter/in/web/response/*, src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java, src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchApiTest.java
검색 목록과 상세 응답을 새 필드에 연결했습니다. Swagger 오류 코드를 Aladin 기준으로 변경하고 상세 검색 테스트를 갱신했습니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant BookQueryController
  participant CompositeBookApiAdapter
  participant AladinApiClient
  participant AladinApiUtil
  Client->>BookQueryController: 도서 검색 또는 ISBN 상세 조회
  BookQueryController->>CompositeBookApiAdapter: 도서 조회 요청
  CompositeBookApiAdapter->>AladinApiClient: Aladin 조회 위임
  AladinApiClient->>AladinApiUtil: 검색·상세 파싱 요청
  AladinApiUtil-->>AladinApiClient: 공통 도서 결과 반환
  AladinApiClient-->>CompositeBookApiAdapter: 검색 결과 또는 상세 결과 반환
  CompositeBookApiAdapter-->>BookQueryController: 응답 데이터 반환
  BookQueryController-->>Client: 도서 응답 반환
Loading

Possibly related PRs

Suggested labels: 🍀 refactor

Suggested reviewers: buzz0331, seongjunnoh

Poem

깡충 토끼가 API 숲을 건너
알라딘 책장을 펼쳤어요.
검색 결과는 공통 상자에 담고
상세 정보와 쪽수도 함께 챙겼죠.
네이버 낡은 길은 이제 쉬어요. 🐇

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning 핵심 전환은 구현했지만 네이버 DTO 유지 요구와 환경별 알라딘 검색 설정 추가 요구를 충족하지 않습니다. 네이버 전용 DTO를 유지하거나 호환 계층을 추가하고, test·dev·prod 환경의 ItemSearch 설정을 추가하십시오.
Docstring Coverage ⚠️ Warning Docstring coverage is 18.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목이 네이버 책 API를 알라딘 API로 전환하는 주요 변경 사항을 정확히 설명합니다.
Out of Scope Changes check ✅ Passed 검색·상세조회 전환, 페이지 수 통합, 응답 매핑, Swagger 오류 코드 변경은 연결 이슈의 범위에 포함됩니다.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/#372-naver-to-aladin-book-api

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Test Results

498 tests   498 ✅  47s ⏱️
148 suites    0 💤
148 files      0 ❌

Results for commit d245fdf.

♻️ This comment has been updated with latest results.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java (1)

82-149: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Aladin JSON 응답 전처리를 추가하세요.

fetchDetailsearchBooksreadTree(response)를 호출하기 직전에 response가 null이면 NullPointerException이 발생하지만, 현재 예외 핸들링은 이 경우를 처리하지 못합니다. 또한 output=js 응답은 JSON 외부 세미콜론을 포함할 수 있어 파서를 넘기기 전에 원본 문자열을 정제해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java`
around lines 82 - 149, Update fetchDetail and searchBooks to preprocess response
before objectMapper.readTree(response): handle null responses without allowing
NullPointerException, and remove any trailing semicolon from output=js responses
before parsing. Route these invalid-response cases through the existing
ExternalApiException(BOOK_ALADIN_API_PARSING_ERROR) handling while preserving
normal JSON parsing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java`:
- Around line 90-115: Update the Aladin response parsing around subInfo and
PAGE_COUNT_PARSING_KEY so a missing itemPage is not converted to pageCount 0 by
asInt(). Explicitly detect the absent page value and apply the intended
compensation behavior, such as throwing the existing ExternalApiException, while
preserving normal parsing when itemPage is present.
- Around line 102-109: Update the author mapping in the BookDetailResult
construction within AladinApiUtil so Aladin’s combined author string is
normalized to the required author role, or use the API’s role-specific field
when available. Apply the same rule consistently to both lookup and search
result mappings, preserving the other detail fields unchanged.
- Around line 70-80: Update the flows in BookSearchService and
BookMostSearchRankService so page-count retrieval reuses the detail data already
loaded by findDetailBookByIsbn instead of calling findPageCountByIsbn and
triggering fetchDetail twice. Prefer the existing loadBookWithPageByIsbn path,
or pass/cache the fetched AladinDetailResult through the flow while preserving
the current result behavior.

---

Outside diff comments:
In `@src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java`:
- Around line 82-149: Update fetchDetail and searchBooks to preprocess response
before objectMapper.readTree(response): handle null responses without allowing
NullPointerException, and remove any trailing semicolon from output=js responses
before parsing. Route these invalid-response cases through the existing
ExternalApiException(BOOK_ALADIN_API_PARSING_ERROR) handling while preserving
normal JSON parsing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d469ea0-b595-4ba3-a9be-cffed63f601e

📥 Commits

Reviewing files that changed from the base of the PR and between 10ed529 and d245fdf.

📒 Files selected for processing (19)
  • src/main/java/konkuk/thip/book/adapter/in/web/response/BookDetailSearchResponse.java
  • src/main/java/konkuk/thip/book/adapter/in/web/response/BookSearchListResponse.java
  • src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java
  • src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiClient.java
  • src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiParam.java
  • src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java
  • src/main/java/konkuk/thip/book/adapter/out/api/dto/BookDetailResult.java
  • src/main/java/konkuk/thip/book/adapter/out/api/dto/BookSearchResult.java
  • src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverApiClient.java
  • src/main/java/konkuk/thip/book/adapter/out/api/naver/NaverBookXmlParser.java
  • src/main/java/konkuk/thip/book/application/port/in/BookSearchUseCase.java
  • src/main/java/konkuk/thip/book/application/port/in/dto/BookDetailSearchResult.java
  • src/main/java/konkuk/thip/book/application/port/out/BookApiQueryPort.java
  • src/main/java/konkuk/thip/book/application/service/BookMostSearchRankService.java
  • src/main/java/konkuk/thip/book/application/service/BookSavedService.java
  • src/main/java/konkuk/thip/book/application/service/BookSearchService.java
  • src/main/java/konkuk/thip/common/swagger/SwaggerResponseDescription.java
  • src/main/java/konkuk/thip/feed/application/service/FeedCreateService.java
  • src/test/java/konkuk/thip/book/adapter/in/web/BookDetailSearchApiTest.java

Comment on lines 70 to +80
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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: findDetailBookByIsbn과 findPageCountByIsbn이 동일 흐름에서 함께 호출되는지 확인
rg -n -C 5 'findDetailBookByIsbn|findPageCountByIsbn|findDetailBookWithPageCountByIsbn|loadBookWithPageByIsbn' --type=java

Repository: THIP-TextHip/THIP-Server

Length of output: 162


🏁 Script executed:

#!/bin/bash
set -e

echo "== repo files matching relevant names =="
git ls-files | rg '(^|/)(AladinApiUtil|.*ApiAdapter|.*Book.*(Service|Controller|Repository)|.*Detail|.*Page).*\.java$|book|aladin|api)' | head -200

echo
echo "== Java method definitions/names containing relevant terms =="
rg -n -C 3 'getPageCount|getBookDetail|getBookDetailWithPageCount|fetchDetail|ItemLookUp|AladinDetailResult|BookDetailResult|pageCount|findDetail|findPage|aloadBook|loadBook' --type=java . | head -300

echo
echo "== AladinApiUtil snippet =="
cat -n src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java | sed -n '1,140p'

echo
echo "== call sites for AladinApiUtil methods (text search) =="
rg -n -C 4 'aladin(\.|)(get|find|getPage|getBook)::*|AladinApiUtil|getPageCount\(|getBookDetail\(|getBookDetailWithPageCount\(' --type=java .

Repository: THIP-TextHip/THIP-Server

Length of output: 50380


🏁 Script executed:

#!/bin/bash
set -e

echo "== BookApiQueryPort =="
cat -n src/main/java/konkuk/thip/book/application/port/out/BookApiQueryPort.java | sed -n '1,140p'

echo
echo "== CompositeBookApiAdapter =="
cat -n src/main/java/konkuk/thip/book/adapter/out/api/CompositeBookApiAdapter.java | sed -n '1,120p'

echo
echo "== AladinApiClient =="
cat -n src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiClient.java | sed -n '1,80p'

echo
echo "== Direct invocations of BookApiQueryPort query methods =="
rg -n -C 4 '\b(\b(BookApiQueryPort|#|\.))?\s*(findDetailBookByIsbn|findPageCountByIsbn|loadBookWithPageByIsbn)\(' --type=java .

Repository: THIP-TextHip/THIP-Server

Length of output: 17701


한 번 호출한 상세 정보는 페이지 수 조회에 재사용하세요.

BookSearchService, BookMostSearchRankServicefindDetailBookByIsbn으로 상세정보만 조회합니다. 이후 이 같은 흐름에서 findPageCountByIsbn을 또 호출하면 같은 ISBN으로 CompositeBookApiAdapterfetchDetail을 두 번 호출해서 알라딘 ItemLookUp 요청이 중복됩니다. 해당 경로에서는 loadBookWithPageByIsbn을 사용하거나, 상세정보를 캐시/전달해 중복 호출을 막아야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java`
around lines 70 - 80, Update the flows in BookSearchService and
BookMostSearchRankService so page-count retrieval reuses the detail data already
loaded by findDetailBookByIsbn instead of calling findPageCountByIsbn and
triggering fetchDetail twice. Prefer the existing loadBookWithPageByIsbn path,
or pass/cache the fetched AladinDetailResult through the flow while preserving
the current result behavior.

Comment on lines 90 to 115
// 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);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

subInfo/itemPage 누락 시 pageCount가 조용히 0으로 저장됩니다.

코드 내 TODO 주석은 "알라딘으로부터 page 정보가 없으면" 사용자에게 안내하거나 예외를 던지는 보상 시나리오를 계획하고 있다고 설명합니다. 그러나 실제 구현은 items가 배열이 아니거나 비어 있는 경우(ISBN 자체를 찾지 못한 경우)만 예외를 던집니다. item은 존재하지만 subInfo.itemPage가 없는 경우, subInfo.path(PAGE_COUNT_PARSING_KEY.getValue()).asInt()는 조용히 0을 반환하고, 이 값이 그대로 pageCount로 반환되어 Book에 저장됩니다.

TODO 주석이 명시한 의도와 실제 동작이 다릅니다. pageCount가 0으로 저장된 책은 이후 진행률 계산 등에서 예상치 못한 결과를 낼 수 있습니다. itemPage 존재 여부를 확인하고, 없을 경우 명시적으로 처리(null 허용, 별도 플래그, 또는 예외)하는 것을 권장합니다.

이 TODO 주석에서 계획한 보상 시나리오(사용자 안내 또는 예외 처리)를 구현하는 코드를 생성해 드릴까요?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java`
around lines 90 - 115, Update the Aladin response parsing around subInfo and
PAGE_COUNT_PARSING_KEY so a missing itemPage is not converted to pageCount 0 by
asInt(). Explicitly detect the absent page value and apply the intended
compensation behavior, such as throwing the existing ExternalApiException, while
preserving normal parsing when itemPage is present.

Comment on lines +102 to +109
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();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

알라딘 API author 필드 지은이 옮긴이 형식

💡 Result:

알라딘 OpenAPI의 author 필드는 별도의 구조체로 분리되어 있지 않으며, 저자명, 옮긴이 등이 포함된 하나의 문자열(String) 형식으로 제공됩니다 [1][2][3]. 주요 특징은 다음과 같습니다: 1. 데이터 형식: author 필드는 작가가 여러 명인 경우를 포함하여 모든 저자 정보를 하나의 문자열로 묶어서 반환합니다 [3]. 2. 구성: 지은이(저자)와 옮긴이 등이 구분 기호와 함께 하나의 필드 내에 포함되어 있습니다 [3]. 별도의 '옮긴이' 필드가 독립적으로 존재하는 것이 아니므로, 필요한 경우 문자열 파싱(parsing)을 통해 데이터를 가공해야 합니다 [3]. 3. 추가 정보: 기본 API 응답 외에 더 상세한 작가 정보가 필요한 경우, API 설계에 따라 authors와 같은 별도의 상세 정보 목록을 요청할 수 있는 경우가 있으나, 기본적인 검색 및 상품 조회 결과에서 author 필드는 단일 문자열로 취급됩니다 [2][4]. 따라서 알라딘 API를 연동하여 지은이와 옮긴이를 각각 구분해야 한다면, 응답받은 author 문자열 내의 특정 구분자를 기준으로 파싱하는 처리가 필요합니다 [3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
fd -a 'AladinApiUtil\.java|BookDetailResult\.java|BookSearchResult\.java' . | sed 's#^\./##'

echo
echo "== relevant lines =="
cat -n src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java | sed -n '70,170p'

echo
echo "== result author usages =="
rg -n "author\(|author:|BookDetailResult|BookSearchResult" src/main/java/konkuk/thip/book -S

Repository: THIP-TextHip/THIP-Server

Length of output: 13611


알라딘 author 필드 정규화하거나 역할별 필드 사용하세요.

알라딘의 author는 지은이/옮긴이 정보가 하나의 문자열로 넘어올 수 있으므로, AladinApiUtil.java의 조회 및 검색 저자 매핑에 그대로 저장하면 저장·표시되는 저자 형식이 불일치할 수 있습니다. 필요 저자만 추출하도록 파싱하거나, API 문서의 역할 구분 필드는 다른 필드로 매핑하도록 변경하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/konkuk/thip/book/adapter/out/api/aladin/AladinApiUtil.java`
around lines 102 - 109, Update the author mapping in the BookDetailResult
construction within AladinApiUtil so Aladin’s combined author string is
normalized to the required author role, or use the API’s role-specific field
when available. Apply the same rule consistently to both lookup and search
result mappings, preserving the other detail fields unchanged.

@hd0rable
hd0rable merged commit 3a7af51 into develop Aug 2, 2026
4 of 6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] 네이버 책 검색 API 종료로 인한 책 검색/상세조회 500 에러 — 알라딘 API로 전환

1 participant