Skip to content

Conversation

@move-hoon
Copy link
Member

@move-hoon move-hoon commented Aug 3, 2025

🔗 관련 이슈

📘 작업 유형

  • ✨ Feature (기능 추가)
  • 🐞 Bugfix (버그 수정)
  • 🔧 Refactor (코드 리팩토링)
  • ⚙️ Chore (환경 설정)
  • 📝 Docs (문서 작성 및 수정)
  • ✅ Test (기능 테스트)
  • 🎨 style (코드 스타일 수정)

📙 작업 내역

📚 BookDetailResponse에 사용자 도서 상태 정보 추가

주요 변경사항*

  1. BookDetailResponse 구조 개선

    • userBookStatus: BookStatus 필드 추가로 사용자의 실제 도서 상태 제공
    • totalPage: Int? 필드를 통해 책의 총 페이지 수 정보 제공
    • withUserBookStatus() 메서드 추가로 상태 업데이트 지원
    • 기존 from() 메서드에 userBookStatus 매개변수 추가 (기본값: BEFORE_REGISTRATION)
  2. 도메인 서비스 레이어 확장

    • UserBookDomainService.findByUserIdAndBookIsbn() 메서드 추가
    • 단일 ISBN으로 사용자의 UserBook 정보 조회 기능 구현
    • 중요: UserBook이 없을 경우 예외 대신 null 반환으로 자연스러운 처리
  3. 서비스 레이어 기능 추가

    • UserBookService.findUserBookStatusByIsbn() 메서드 추가
    • userId와 ISBN을 통해 해당 책의 BookStatus만 조회하는 기능
    • UserBook이 없을 경우 null 반환하여 정상적인 케이스로 처리
  4. UseCase 로직 개선

    • BookUseCase.getBookDetail()에서 실제 사용자 라이브러리 상태 확인 로직 추가
    • ISBN13이 있을 경우: 사용자 라이브러리에서 실제 상태 조회
    • ISBN13이 없거나 UserBook이 없을 경우: BEFORE_REGISTRATION 반환
    • 예외 처리 로직 제거로 성능 및 코드 가독성 향상

동작 방식

  1. 클라이언트가 책 상세 조회 API 호출
  2. 외부 API(Aladin)에서 책 정보 조회
  3. 해당 책의 ISBN13으로 사용자의 UserBook 테이블 조회
  4. 조회 결과에 따른 BookStatus 결정:
  • 라이브러리에 있음: BEFORE_READING, READING, COMPLETED 중 실제 상태
  • 라이브러리에 없음: BEFORE_REGISTRATION
  1. BookDetailResponse에 올바른 userBookStatus 포함하여 응답

API 응답 예시

  "version": "20131101",
  "title": "안녕이라 그랬어",
  "link": "https://www.aladin.co.kr/shop/wproduct.aspx?ItemId=365665217&partner=openAPI&start=api",
  "author": "김애란",
  "pubDate": "2025-06-20",
  "description": "소설가 김애란이 『바깥은 여름』 이후 팔 년 만에 새 소설집으로 돌아왔다. 2022 김승옥문학상 우수상을 수상한 「홈 파티」와 2022 오영수문학상 수상작인 「좋은 이웃」을 비롯해 총 일곱 편의 단편이 수록되어 있다.",
  "isbn13": "9791141602376",
  "mallType": "BOOK",
  "cover": "https://image.aladin.co.kr/product/36566/52/cover200/k462039240_1.jpg",
  "categoryName": "국내도서>소설/시/희곡>한국소설>2000년대 이후 한국소설",
  "publisher": "문학동네",
  "totalPage": 320,
  "userBookStatus": "BEFORE_REGISTRATION"
}

기대 효과

  • 클라이언트에서 책이 이미 등록되어 있는지 즉시 확인 가능
  • 중복 등록 방지 및 적절한 UI 상태 표시 가능 (등록 버튼 vs 상태 변경 버튼)
  • 사용자 경험 개선 (등록된 책의 현재 읽기 상태 및 페이지 정보 확인 가능)
  • 성능 최적화 (예외 처리 로직 제거로 불필요한 오버헤드 감소)

🧪 테스트 내역

  • 브라우저/기기에서 동작 확인
  • 엣지 케이스 테스트 완료
    • ISBN13이 null인 경우 BEFORE_REGISTRATION 반환 확인
    • UserBook이 없는 경우 예외 없이 BEFORE_REGISTRATION 반환 확인
    • 등록된 UserBook의 실제 상태(BEFORE_READING, READING, COMPLETED) 반환 확인
    • totalPage 정보가 올바르게 반환되는지 확인
  • 기존 기능 영향 없음
    • BookSearchResponse의 기존 mergeWithUserBookStatus 로직 유지
    • BookCreateRequest.from() 메서드 호환성 확인

✅ PR 체크리스트

  • 커밋 메시지가 명확합니다
  • PR 제목이 컨벤션에 맞습니다
  • 관련 이슈 번호를 작성했습니다
  • 기능이 정상적으로 작동합니다
  • 불필요한 코드를 제거했습니다

💬 추가 설명 or 리뷰 포인트

🔍 주요 리뷰 포인트

  1. 예외 처리 방식 개선

    • 기존: UserBook이 없을 때 UserBookNotFoundException 예외 발생
    • 개선: UserBook이 없는 것은 정상적인 상황이므로 null 반환으로 변경
    • 장점: 성능 향상, 코드 가독성 개선, 자연스러운 비즈니스 로직
  2. 성능 고려사항

    • BookDetail: 단일 조회이므로 개별 쿼리 사용 (findUserBookStatusByIsbn)
    • BookSearch: 여러 책이므로 기존의 배치 조회(getUserBookStatusMap) 방식 유지
    • 각각의 사용 사례에 최적화된 접근 방식 적용
  3. API 스펙 변경 영향도

    • BookDetailResponse에 userBookStatus 필드 추가
    • totalPage 필드 정보 제공 강화
    • 클라이언트에서 추가 API 호출 없이 필요한 모든 정보 한번에 확인 가능
  4. 도메인 모델 설계

    • UserBookDomainService에 단일 조회 메서드 추가가 적절한 위치
    • null 반환 방식으로 Optional한 데이터 표현
    • 비즈니스 로직과 예외 처리의 명확한 분리

🚀 향후 개선 방향

  • 캐싱 전략 도입으로 반복적인 UserBook 조회 최적화 검토
  • 배치 조회와 단일 조회 로직 통합 가능성 검토

Summary by CodeRabbit

  • 신규 기능

    • ISBN-10을 ISBN-13으로 변환하는 유틸리티와 저자명 추출 유틸리티가 추가되었습니다.
    • 외부 알라딘 API와 연동하여 도서 검색 및 상세 조회를 제공하는 서비스가 도입되었습니다.
    • 도서 표지 크기 관련 enum이 추가되었습니다.
  • 기능 개선

    • 도서 등록, 검색, 상세 요청 및 응답 DTO에 입력 검증 및 Swagger 문서화가 강화되었습니다.
    • 도서 상세 및 검색 결과에 사용자별 도서 상태가 반영됩니다.
    • 도서 상세 조회 시 ISBN-13 기반으로 통일되고, 일부 필드가 정제·추가되었습니다.
    • 도서 검색 및 상세 조회 요청 시 사용자 ID가 포함되어 처리됩니다.
    • 도서 검색 결과 병합 로직이 별도 메서드로 분리되어 가독성과 유지보수가 개선되었습니다.
    • 도서 관련 예외 처리 및 오류 코드가 세분화되어 명확해졌습니다.
    • 사용자 도서 존재 여부 확인 방식이 반환값에서 존재 유무(boolean)로 변경되고, 관련 메서드 호출 순서가 조정되었습니다.
    • 내부 API 요청 및 응답 데이터 모델이 리팩토링되어 불필요한 필드가 제거되고 명확해졌습니다.
    • Swagger UI 경로가 /swagger-ui로 변경되었습니다.
    • 일부 클래스 및 메서드명이 변경되고, 불필요한 컴패니언 오브젝트 메서드가 제거되었습니다.
  • 버그 수정

    • 사용자 도서 존재 여부 확인 방식이 반환값에서 존재 유무(boolean)로 변경되었습니다.
  • 기타

    • 오류 코드가 세분화되고, 일부 예외 클래스 명칭이 변경되었습니다.
    • 내부 구현에서 사용하지 않는 파일 및 클래스가 삭제되었습니다.

…ice 인터페이스를 sealed로 변경, 불필요한 상수 제거
…archRequest의 메서드 이름 변경 및 파라미터 정리
… 이름 변경 및 유효성 검증 메시지 수정
…ndByIdAndUserId 메서드를 existsByIdAndUserId로 변경하여 존재 여부 확인 로직 개선
…메서드 위치 변경 및 대소문자 수정
…이터 클래스의 필드 정리 및 ISBN 처리 로직 개선
@coderabbitai
Copy link

coderabbitai bot commented Aug 3, 2025

Warning

Rate limit exceeded

@hoonyworld has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 6 minutes and 28 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 804e487 and 033d224.

📒 Files selected for processing (9)
  • apis/src/main/kotlin/org/yapp/apis/auth/service/TokenService.kt (2 hunks)
  • apis/src/main/kotlin/org/yapp/apis/auth/service/UserAuthService.kt (3 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt (2 hunks)
  • apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt (2 hunks)
  • apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt (1 hunks)
📝 Walkthrough

Walkthrough

이번 변경사항은 도서 상세 조회 API의 응답에 status(사용자 도서 상태)와 totalPage(총 페이지 수)를 함께 제공하도록 전반적으로 리팩토링한 작업입니다. 이 과정에서 관련 DTO, 서비스, 유틸리티, 예외, 외부 API 연동 구조가 전면적으로 개편되었습니다.

Changes

Cohort / File(s) Change Summary
Book Detail/Status 리팩토링
apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt, apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt, apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt, apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt, domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt, domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt, infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt, infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt
도서 상세 조회 시 사용자 도서 상태(status)와 총 페이지(totalPage)를 함께 반환하도록 DTO, 유스케이스, 서비스, 레포지토리 계층 전반 리팩토링. 관련 메서드 시그니처 및 반환 타입, 내부 로직 변경.
Aladin 외부 API 연동 구조 변경
apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt, apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt, apis/src/main/kotlin/org/yapp/apis/book/exception/BookErrorCode.kt, apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookException.kt, infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt, infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt, infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt, infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt, infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt, infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt
Aladin API 연동 구조를 리팩토링하여 서비스 계층에서 직접 외부 API 호출 및 예외 처리, 응답 DTO 변환을 담당하도록 변경. 기존 helper/impl 클래스 및 response base 클래스 제거, 새로운 response/exception 구조 도입.
Request/Response/Validation/Schema 개선
apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt, apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt, apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt, apis/src/main/kotlin/org/yapp/apis/book/dto/request/UpsertUserBookRequest.kt, apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt, apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt
요청/응답 DTO에 Swagger/OpenAPI 어노테이션 및 유효성 검증 어노테이션 추가, 필드 제한 및 설명 강화. 일부 필드 구조 및 팩토리 메서드 변경.
유틸리티/패턴/변환기 추가 및 정비
apis/src/main/kotlin/org/yapp/apis/util/AuthorExtractor.kt, apis/src/main/kotlin/org/yapp/apis/util/IsbnConverter.kt, global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt
저자명 추출, ISBN 변환 등 신규 유틸리티 객체 및 함수 추가. 기존 정규식 유틸리티는 ISBN 전용으로 단순화.
예외 및 에러코드 구조 개선
domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookErrorCode.kt, domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt
사용자 도서 관련 에러코드 및 예외 클래스 신설, 도메인 계층에서 일관성 있게 사용하도록 개선.
불필요/구조 변경으로 인한 파일 삭제
apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt, apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt, apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt, infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinResponseBase.kt
리팩토링으로 인해 더 이상 사용하지 않는 상수, 헬퍼, 구현체, 응답 베이스 클래스 삭제.
기타 사소한 변경
apis/src/main/resources/application.yml, domain/src/main/kotlin/org/yapp/domain/book/Book.kt
Swagger UI 경로 변경, import 및 주석 정리 등 기능과 무관한 단순 변경.

Sequence Diagram(s)

sequenceDiagram
actor User
participant BookController
participant BookUseCase
participant UserBookService
participant BookQueryService
participant AladinBookQueryService
participant ExternalAladinAPI

User->>BookController: GET /books/{isbn}
BookController->>BookUseCase: getBookDetail(request, userId)
BookUseCase->>BookQueryService: getBookDetail(request)
BookQueryService->>AladinBookQueryService: getBookDetail(request)
AladinBookQueryService->>ExternalAladinAPI: lookupBook(request)
ExternalAladinAPI-->>AladinBookQueryService: AladinBookDetailResponse
AladinBookQueryService-->>BookQueryService: BookDetailResponse (with totalPage)
BookQueryService-->>BookUseCase: BookDetailResponse
BookUseCase->>UserBookService: findUserBookStatusByIsbn(userId, isbn)
UserBookService-->>BookUseCase: BookStatus
BookUseCase-->>BookController: BookDetailResponse (with status, totalPage)
BookController-->>User: 응답 반환
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Assessment against linked issues

Objective Addressed Explanation
도서 상세 조회 시 status와 totalPage를 같이 제공하도록 리팩토링 (#71)

Assessment against linked issues: Out-of-scope changes

(해당 이슈의 목적과 직접적으로 관련 없는 변경사항은 발견되지 않았습니다.)

Possibly related PRs

Suggested labels

🔨 refactor

Suggested reviewers

  • minwoo1999
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch BOOK-207-refactor/#71

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c0ef87 and 62eabda.

📒 Files selected for processing (35)
  • apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt (0 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt (2 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/request/UpsertUserBookRequest.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt (3 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/exception/BookErrorCode.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookException.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt (0 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt (3 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt (0 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt (2 hunks)
  • apis/src/main/kotlin/org/yapp/apis/util/AuthorExtractor.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/util/IsbnConverter.kt (1 hunks)
  • apis/src/main/resources/application.yml (1 hunks)
  • domain/src/main/kotlin/org/yapp/domain/book/Book.kt (1 hunks)
  • domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (3 hunks)
  • domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt (1 hunks)
  • domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookErrorCode.kt (1 hunks)
  • domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt (1 hunks)
  • global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt (2 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt (3 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt (4 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinResponseBase.kt (0 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt (1 hunks)
💤 Files with no reviewable changes (4)
  • apis/src/main/kotlin/org/yapp/apis/book/helper/AladinApiHelper.kt
  • apis/src/main/kotlin/org/yapp/apis/book/constant/BookQueryServiceQualifier.kt
  • apis/src/main/kotlin/org/yapp/apis/book/service/impl/AladinBookQueryService.kt
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinResponseBase.kt
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hoonyworld
PR: YAPP-Github/Reed-Server#39
File: apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt:45-45
Timestamp: 2025-07-17T15:09:36.267Z
Learning: Response DTO 클래스에서는 불변성을 지키기 위해 프로퍼티를 val로 선언하고, 업데이트가 필요한 경우 copy() 메서드를 사용해야 합니다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation
🔇 Additional comments (60)
apis/src/main/resources/application.yml (1)

28-28: LGTM! 표준 Swagger UI 경로로 업데이트되었습니다.

.html 확장자를 제거한 /swagger-ui 경로는 최신 SpringDoc 규칙에 맞는 적절한 변경입니다.

apis/src/main/kotlin/org/yapp/apis/book/exception/UserBookException.kt (1)

5-8: 예외 계층 구조 개선이 잘 되었습니다.

UserBookNotFoundException에서 UserBookException으로 이름 변경은 API 계층에서 더 일반적인 예외 타입을 제공하고, 도메인 계층의 구체적인 예외와 분리하는 좋은 설계입니다.

apis/src/main/kotlin/org/yapp/apis/book/service/BookQueryService.kt (1)

8-11: sealed interface 사용이 우수한 설계 선택입니다.

sealed interface로 변경하여 구현체를 같은 모듈 내로 제한함으로써 타입 안전성과 제어된 상속을 제공합니다. 이는 exhaustive type checking과 모듈화된 설계를 지원합니다.

infra/src/main/kotlin/org/yapp/infra/userbook/repository/JpaUserBookRepository.kt (1)

10-10: 존재 여부 확인을 위한 성능 최적화가 잘 되었습니다.

findByIdAndUserId에서 existsByIdAndUserId로 변경하여 엔티티 조회 대신 존재 여부만 확인하도록 최적화되었습니다. 이는 메모리 사용량을 줄이고 검증 시나리오에서 쿼리 성능을 향상시킵니다.

apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt (1)

48-48: 사용자 컨텍스트 전달이 올바르게 구현되었습니다.

인증된 사용자의 UUID를 use case 레이어로 전달하여 사용자별 도서 상태 조회가 가능하도록 하는 변경사항이 적절하게 구현되었습니다.

domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookNotFoundException.kt (1)

6-9: 도메인별 예외 처리 구조가 적절합니다.

CommonException을 확장하여 일관된 예외 처리 패턴을 따르고 있으며, UserBookErrorCode를 통한 구조화된 에러 처리가 잘 구현되었습니다.

infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinApi.kt (2)

5-6: 패키지 구조 개선이 적절합니다.

DTO 클래스들을 request 패키지로 이동시켜 더 명확한 패키지 구조를 만든 것이 좋습니다.


25-26: 코드 중복 제거로 가독성이 향상되었습니다.

request.toMap()에서 이미 필요한 파라미터를 처리하도록 개선하여 수동으로 파라미터를 추가하는 중복 코드가 제거되었습니다. DRY 원칙을 잘 따른 리팩토링입니다.

domain/src/main/kotlin/org/yapp/domain/userbook/UserBookRepository.kt (1)

13-13: 존재 여부 확인을 위한 메서드 시그니처 개선이 우수합니다.

엔티티 전체를 반환하던 findByIdAndUserId를 존재 여부만 확인하는 existsByIdAndUserId로 변경하여 성능을 개선하고 메서드의 의도를 더 명확하게 표현했습니다. 불필요한 엔티티 로딩을 방지하는 좋은 리팩토링입니다.

apis/src/main/kotlin/org/yapp/apis/util/AuthorExtractor.kt (1)

8-20: 저자명 추출 로직이 한국어 텍스트 처리에 적합합니다.

(지은이) 마커를 기준으로 저자명을 추출하는 로직이 적절하게 구현되었습니다. null/blank 입력 처리와 중첩된 괄호 처리도 고려되어 있습니다.

infra/src/main/kotlin/org/yapp/infra/userbook/repository/impl/UserBookRepositoryImpl.kt (1)

28-30: 성능 최적화가 잘 적용되었습니다!

단순 존재 여부 확인을 위해 전체 엔티티를 조회하고 도메인 객체로 변환하는 대신 existsByIdAndUserId를 사용하여 메모리 사용량과 성능을 개선했습니다. 데이터베이스 레벨의 EXISTS 쿼리를 활용하는 것이 더 효율적입니다.

infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt (3)

12-14: 불필요한 별칭 제거로 코드가 개선되었습니다!

client 별칭을 제거하고 restClient를 직접 사용하는 것이 더 명확하고 간결합니다.


27-27: LGTM!

restClient를 직접 사용하여 코드가 더 읽기 쉬워졌습니다.

Also applies to: 43-43


50-60: 확장 함수 리팩토링 및 private 적용은 좋습니다.
다만 58번 라인에서 쿼리 파라미터 "output""Output"으로 변경한 부분이 Aladin API에서 대소문자를 구분하지 않는지 API 문서 또는 실제 호출 테스트를 통해 확인해 주세요.

  • 파일: infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt
  • 라인: 58
apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt (3)

8-11: API 문서화가 잘 추가되었습니다!

클래스 레벨 @Schema 어노테이션으로 요청의 목적을 명확히 설명했습니다.


14-21: ISBN 필드 문서화가 상세하고 적절합니다!

13자리 ISBN 요구사항과 예시값을 포함한 상세한 스키마 정의가 API 사용자에게 도움이 될 것입니다. minLengthmaxLength 제약조건도 적절합니다.


23-31: 도서 상태 필드 검증과 문서화가 완벽합니다!

@NotNull 검증 추가와 함께 허용 가능한 enum 값들을 명시한 스키마가 API 명세를 크게 개선했습니다.

apis/src/main/kotlin/org/yapp/apis/util/IsbnConverter.kt (1)

12-21: 입력 검증 로직이 견고합니다!

ISBN-10 형식의 입력값을 적절히 정규화하고 길이와 패턴을 검증하는 로직이 잘 구현되었습니다. null과 빈 문자열 처리도 안전합니다.

domain/src/main/kotlin/org/yapp/domain/userbook/UserBookDomainService.kt (3)

5-6: 도메인 예외 처리를 위한 적절한 import 추가

새로운 예외 클래스들을 import하여 명시적인 예외 처리를 준비했습니다.


62-66: 사용자 도서 상태 조회 기능이 잘 구현되었습니다!

ISBN으로 사용자 도서를 조회하고, 없을 경우 도메인 특화 예외를 던지는 로직이 PR의 핵심 목표인 "도서 상세 응답에 사용자 도서 상태 포함" 기능을 지원합니다. 예외 처리도 명시적이고 적절합니다.


79-81: 성능 최적화된 존재 여부 확인 메서드

전체 엔티티 조회 대신 Boolean 반환으로 변경하여 성능을 개선했습니다. 메서드명도 existsByUserBookIdAndUserId로 명확하게 변경되어 의도가 잘 드러납니다.

apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt (5)

3-6: 새로운 import 추가가 적절합니다.

validation과 OpenAPI documentation을 위한 필요한 import들이 추가되었습니다.


8-11: 클래스 레벨 Schema 문서화가 잘 되었습니다.

API 문서에서 명확한 설명을 제공하는 좋은 개선사항입니다.


13-19: query 필드의 validation과 문서화가 적절합니다.

@NotBlank validation과 상세한 Schema 정보가 잘 추가되었습니다.


37-46: maxResults 필드의 범위 제한이 적절합니다.

@Min(1)@Max(100) 제약 조건이 합리적이며, Schema 문서와 일치합니다.


48-55: start 필드의 최소값 제한이 적절합니다.

페이징을 위한 시작 인덱스가 1 이상이어야 한다는 제약이 합리적입니다.

domain/src/main/kotlin/org/yapp/domain/userbook/exception/UserBookErrorCode.kt (1)

1-16: 에러 코드 enum 클래스가 잘 구현되었습니다.

BaseErrorCode 인터페이스를 구현하여 일관된 에러 처리 패턴을 따르고 있으며, 확장 가능한 구조로 설계되었습니다. 에러 코드 네이밍("USER_BOOK_001")과 한글 메시지도 적절합니다.

apis/src/main/kotlin/org/yapp/apis/book/service/UserBookService.kt (3)

11-11: Exception import 변경이 적절합니다.

UserBookNotFoundException에서 UserBookException으로의 변경이 일관된 예외 처리 전략을 반영합니다.


35-42: validateUserBookExists 메서드 리팩토링이 개선되었습니다.

존재 여부만 확인하는 boolean 기반 검증으로 변경되어 메서드의 목적이 더 명확해졌습니다. 파라미터 순서도 repository 메서드와 일치하도록 개선되었습니다.


52-55: 새로운 findUserBookStatusByIsbn 메서드가 잘 구현되었습니다.

ISBN을 통해 사용자 책 상태를 조회하는 기능이 간결하고 명확하게 구현되었습니다. domain service 호출을 통해 적절한 레이어 분리도 유지되고 있습니다.

infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt (4)

1-1: 패키지 구조 개선이 적절합니다.

dto에서 request로의 패키지 변경이 코드 구조를 더 명확하게 만듭니다.


11-11: 코드 스타일 개선이 좋습니다.

categoryId의 명시적 nullable 처리와 trailing comma 추가가 코드 가독성을 향상시킵니다.


27-27: 팩토리 메서드명 변경이 일관성을 개선합니다.

create에서 of로의 변경이 다른 DTO들과의 네이밍 일관성을 향상시킵니다.


43-44: cover 값 표준화가 합리적입니다.

cover 파라미터를 "Big"으로 하드코딩하여 모든 알라딘 API 요청에서 일관된 커버 이미지 크기를 사용하도록 표준화한 것이 좋은 결정입니다.

apis/src/main/kotlin/org/yapp/apis/book/dto/request/UpsertUserBookRequest.kt (6)

3-6: 필요한 import들이 적절히 추가되었습니다.

validation과 OpenAPI 문서화를 위한 필수 import들이 모두 포함되었습니다.


11-14: 클래스 레벨 문서화가 명확합니다.

API의 목적과 사용 용도가 명확하게 설명되어 있어 개발자 경험을 향상시킵니다.


16-23: UUID 필드들의 validation과 문서화가 우수합니다.

userId, bookId, bookIsbn 필드에 대한 validation과 Schema 정의가 일관되고 상세합니다. UUID format 지정도 적절합니다.

Also applies to: 25-32, 34-42


44-52: 텍스트 필드들의 크기 제한이 합리적입니다.

제목(500자), 저자/출판사(200자)의 크기 제한이 실제 사용 환경에 적합하며, 한글 에러 메시지가 사용자 친화적입니다.

Also applies to: 54-62, 64-72


74-83: URL 필드의 validation이 적절합니다.

2048자 제한과 URI format 지정이 URL 필드에 적합하며, 예시 URL도 실제적입니다.


85-93: BookStatus enum의 문서화가 완벽합니다.

허용 가능한 값들이 명시되어 있고 enumAsRef 설정으로 OpenAPI 스펙이 정확하게 생성될 것입니다.

global-utils/src/main/kotlin/org/yapp/globalutils/util/RegexUtils.kt (1)

4-5: ISBN 정규식 패턴이 올바르게 정의되었습니다.

ISBN-10과 ISBN-13 형식에 맞는 정확한 정규식 패턴입니다. ISBN-10은 9자리 숫자 + 체크 디지트(0-9 또는 X), ISBN-13은 표준 도서 접두사(978/979) + 10자리 숫자로 국제 ISBN 표준을 준수합니다.

apis/src/main/kotlin/org/yapp/apis/book/exception/BookErrorCode.kt (1)

13-14: 알라딘 API 에러 코드가 적절히 세분화되었습니다.

검색과 상세 조회 실패를 구분하는 것은 디버깅과 에러 처리에 도움이 됩니다. 에러 코드와 메시지가 일관성 있고 명확합니다.

apis/src/main/kotlin/org/yapp/apis/book/service/AladinBookQueryService.kt (2)

23-43: 검색 메서드가 잘 구현되었습니다.

Result 타입을 활용한 에러 처리와 성공/실패 케이스에 대한 상세한 로깅이 훌륭합니다. 도메인 요청을 알라딘 API 요청으로 변환하는 로직도 명확합니다.


45-58: 상세 조회 메서드의 구현이 우수합니다.

검색 메서드와 일관된 패턴을 사용하여 코드의 일관성을 유지했습니다. 에러 처리와 로깅이 적절하게 구현되어 있습니다.

infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt (2)

3-7: 요청 클래스 구조가 효과적으로 단순화되었습니다.

cover 속성 추가와 itemIdType의 기본값 설정이 적절하며, ISBN13 표준화 방향에 부합합니다.


17-22: 팩토리 메서드가 잘 단순화되었습니다.

itemId만 받고 나머지는 고정값으로 설정하는 것이 API 사용을 단순화하고 일관성을 보장합니다. ISBN13과 "Big" 커버 사이즈로 고정하는 것이 합리적입니다.

apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt (3)

10-23: 스키마 어노테이션과 ISBN 검증이 잘 정의되었습니다.

API 문서화를 위한 상세한 스키마 정보와 13자리 ISBN 요구사항이 명확하게 표현되었습니다.


55-63: 출간연도 검증 범위가 적절합니다.

1000년부터 2100년까지의 범위는 실용적이고 합리적인 제한입니다.


90-93: ISBN13 전용 사용으로의 변경이 일관성 있습니다.

bookDetail.isbn13만 사용하도록 변경한 것이 PR의 ISBN13 표준화 방향과 일치합니다. 예외 처리도 적절합니다.

infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinDetailResponse.kt (1)

1-53: 외부 API 응답 모델이 잘 구성되어 있습니다!

Aladin API 응답을 위한 데이터 모델이 적절히 설계되었습니다:

  • Jackson 어노테이션으로 JSON 매핑이 명확히 정의됨
  • internal 생성자로 캡슐화가 잘 되어있음
  • nullable 타입이 적절히 사용되어 외부 API의 불확실성을 잘 처리함
infra/src/main/kotlin/org/yapp/infra/external/aladin/response/AladinSearchResponse.kt (1)

1-42: 검색 응답 모델이 일관성 있게 구현되었습니다!

상세 응답 모델과 동일한 패턴으로 잘 구현되었습니다. 외부 API 통합에 적합한 구조입니다.

apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt (1)

20-22: 불변성 원칙이 잘 적용되었습니다!

이전 학습 내용에 따라 copy() 메서드를 사용하여 불변성을 유지하는 방식이 올바르게 구현되었습니다.

apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt (1)

23-25: 불변성 원칙이 잘 적용되었습니다!

copy() 메서드를 활용한 상태 업데이트가 적절히 구현되었습니다.

apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt (7)

32-42: searchBooks 메서드 리팩토링이 잘 구현되었습니다.

userId 매개변수를 추가하고 사용자별 책 상태를 병합하는 로직이 깔끔하게 분리되어 있습니다. 새로운 헬퍼 메서드를 통해 코드의 가독성과 재사용성이 향상되었습니다.


44-56: getBookDetail 메서드 개선사항이 우수합니다.

사용자 ID를 받아서 실제 사용자의 책 상태를 조회하여 반환하는 로직이 잘 구현되었습니다. ISBN13가 없는 경우 기본값으로 BEFORE_REGISTRATION을 반환하는 처리도 적절합니다.


59-75: upsertBookToMyLibrary 메서드 시그니처 개선.

메서드 시그니처가 더 명확해졌고, BookDetailRequest.from() 팩토리 메서드 사용으로 코드가 더 깔끔해졌습니다.


89-108: mergeWithUserBookStatus 헬퍼 메서드 구현이 효율적입니다.

빈 목록에 대한 조기 반환 처리와 ISBN13 기반의 상태 매핑 로직이 잘 구현되었습니다. null 체크를 통해 안전하게 상태를 업데이트하거나 원본 객체를 반환하는 로직도 적절합니다.


110-121: getUserBookStatusMap 헬퍼 메서드의 성능 최적화.

배치 조회를 통해 N+1 쿼리 문제를 방지하고, associate 함수를 사용하여 효율적으로 Map을 생성하는 방식이 우수합니다.


89-121: 헬퍼 메서드들의 역할 분리가 명확합니다.

mergeWithUserBookStatusgetUserBookStatusMap 두 개의 private 메서드로 책임을 명확히 분리한 것이 좋습니다. 단일 책임 원칙(SRP)을 잘 준수하고 있으며, 테스트하기도 용이한 구조입니다.


26-26: Qualifier 어노테이션 문자열 리터럴 사용 검증 필요

현재 BookQueryServiceQualifier 객체가 삭제되면서, @Qualifier("aladinBookQueryService")로 상수 대신 문자열 리터럴이 사용되고 있습니다.
아래 사항을 반드시 확인해주세요:

• 파일: apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt
라인 26 – @Qualifier("aladinBookQueryService")

@Qualifier("aladinBookQueryService")
private val bookQueryService: BookQueryService,

• DI 설정(예: @Bean 메서드 또는 @Component 어노테이션)에서 "aladinBookQueryService"라는 빈 이름이 실제로 정의되어 있는지
• 상수 기반 Qualifier에서 문자열 리터럴로 변경된 것이 의도된 사항인지

위 사항들이 충족되지 않으면 빈 주입 오류가 발생할 수 있으니, 빈 정의 여부와 변경 의도를 검토해 주세요.

optResult = this.optResult
)
}
fun validIsbn(): String = isbn!!
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

안전하지 않은 non-null assertion 사용

validIsbn() 메서드에서 !! 연산자를 사용하는 것은 NPE를 발생시킬 위험이 있습니다.

더 안전한 방식으로 구현하는 것을 권장합니다:

-    fun validIsbn(): String = isbn!!
+    fun validIsbn(): String = isbn ?: throw IllegalStateException("ISBN이 제공되지 않았습니다")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fun validIsbn(): String = isbn!!
fun validIsbn(): String = isbn ?: throw IllegalStateException("ISBN이 제공되지 않았습니다")
🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt at
line 28, the validIsbn() method uses the non-null assertion operator (!!) on
isbn, which can cause a NullPointerException if isbn is null. To fix this,
modify the method to safely handle null values by either returning a nullable
String, providing a default value, or throwing a controlled exception with a
clear message instead of using !!.

Comment on lines +31 to 37
fun from(
isbn: String?,
): BookDetailRequest {
return BookDetailRequest(
itemId = isbn,
itemIdType = "ISBN",
optResult = optResult
isbn = isbn,
)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Factory 메서드의 입력 검증 부재

from 메서드가 nullable isbn을 받지만 검증 없이 그대로 전달합니다. 이는 잘못된 입력으로 인한 런타임 오류를 야기할 수 있습니다.

입력 검증을 추가하는 것을 권장합니다:

 companion object {
     fun from(
         isbn: String?,
     ): BookDetailRequest {
+        requireNotNull(isbn) { "ISBN은 필수입니다" }
+        require(isbn.matches(Regex(RegexUtils.ISBN13_PATTERN))) { "유효한 13자리 ISBN 형식이 아닙니다" }
         return BookDetailRequest(
             isbn = isbn,
         )
     }
 }
🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt
around lines 31 to 37, the from factory method accepts a nullable isbn parameter
but does not validate it before passing it to the constructor, which can cause
runtime errors. Add input validation to check if isbn is null or empty and throw
an appropriate exception or handle the error before creating the
BookDetailRequest instance.

Comment on lines +37 to +39
pubDate = item.pubDate ?: "",
description = item.description ?: "",
mallType = item.mallType,
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

누락된 데이터에 대한 기본값 처리 재검토

빈 문자열을 기본값으로 사용하면 실제로 데이터가 없는 경우를 구분하기 어려울 수 있습니다.

nullable 타입을 유지하여 데이터 누락을 명시적으로 표현하는 것을 고려해보세요:

-    val pubDate: String,
-    val description: String,
+    val pubDate: String?,
+    val description: String?,
     // ...
-                pubDate = item.pubDate ?: "",
-                description = item.description ?: "",
+                pubDate = item.pubDate,
+                description = item.description,
-                publisher = item.publisher ?: "",
+                publisher = item.publisher,

Also applies to: 43-43

🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt at
lines 37-39 and line 43, the code assigns empty strings as default values for
missing data fields like pubDate and description. Instead of defaulting to empty
strings, modify these fields to be nullable types and assign null when data is
missing. This change explicitly represents missing data and avoids ambiguity
between empty and absent values.

@move-hoon move-hoon changed the title Book 207 refactor/#71 feat: BookDetailResponse에 사용자 도서 상태 및 페이지 정보 추가 Aug 3, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (5)
apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt (1)

76-76: 복잡한 ISBN 변환 로직 개선 제안

현재 한 줄에 너무 많은 로직이 포함되어 있어 가독성이 떨어집니다.

다음과 같이 개선할 수 있습니다:

-                    isbn13 = isbn13 ?: IsbnConverter.toIsbn13(isbn) ?: throw IllegalArgumentException("Either isbn13 or isbn must be provided"),
+                    isbn13 = when {
+                        !isbn13.isNullOrBlank() -> isbn13
+                        !isbn.isNullOrBlank() -> IsbnConverter.toIsbn13(isbn) 
+                            ?: throw IllegalArgumentException("ISBN을 ISBN13으로 변환할 수 없습니다")
+                        else -> throw IllegalArgumentException("ISBN13 또는 ISBN 중 하나는 반드시 제공되어야 합니다")
+                    },
apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt (3)

37-38: 누락된 데이터에 대한 기본값 처리 재검토

빈 문자열을 기본값으로 사용하면 실제로 데이터가 없는 경우를 구분하기 어려울 수 있습니다.

nullable 타입을 유지하여 데이터 누락을 명시적으로 표현하는 것을 고려해보세요:

-    val pubDate: String,
-    val description: String,
+    val pubDate: String?,
+    val description: String?,
     // ...
-                pubDate = item.pubDate ?: "",
-                description = item.description ?: "",
+                pubDate = item.pubDate,
+                description = item.description,
-                publisher = item.publisher ?: "",
+                publisher = item.publisher,

Also applies to: 43-43


40-40: ISBN 변환 로직의 일관성

BookSearchResponse와 동일한 ISBN 변환 로직이 사용되고 있습니다. 가독성 개선을 위해 동일한 리팩토링을 적용하는 것이 좋겠습니다.


44-44: 매직 넘버(4032) 상수 추출 필요

기본 페이지 수로 사용되는 4032가 코드에 하드코딩되어 있어, 그 의미를 파악하기 어렵습니다. 상수로 분리하고 주석을 추가해 의도를 명확히 하는 것이 좋습니다.

+    companion object {
+        // 알라딘 API에서 페이지 정보(itemPage)가 없을 때 사용하는 기본값
+        private const val DEFAULT_TOTAL_PAGE = 4032
+    }
 
     fun from(response: AladinBookDetailResponse, userBookStatus: BookStatus = BookStatus.BEFORE_REGISTRATION): BookDetailResponse {
         // …
-        totalPage = item.subInfo.itemPage ?: 4032,
+        totalPage = item.subInfo.itemPage ?: DEFAULT_TOTAL_PAGE,
apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt (1)

3-3: 불필요한 빈 줄 제거를 고려해보세요.

코드 스타일 일관성을 위해 불필요한 빈 줄들을 제거하는 것을 권장합니다.

Also applies to: 9-9, 21-21

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea24d9d and 91b35e8.

📒 Files selected for processing (8)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt (3 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt (3 hunks)
  • apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt (2 hunks)
  • global-utils/src/main/kotlin/org/yapp/globalutils/book/BookCoverSize.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hoonyworld
PR: YAPP-Github/Reed-Server#39
File: apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt:45-45
Timestamp: 2025-07-17T15:09:36.267Z
Learning: Response DTO 클래스에서는 불변성을 지키기 위해 프로퍼티를 val로 선언하고, 업데이트가 필요한 경우 copy() 메서드를 사용해야 합니다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation
🔇 Additional comments (18)
global-utils/src/main/kotlin/org/yapp/globalutils/book/BookCoverSize.kt (1)

3-10: 잘 설계된 enum 클래스입니다.

API 값을 중앙화하고 타입 안전성을 제공하는 좋은 설계입니다. 외부 API 요청에서 일관된 커버 사이즈 처리를 가능하게 합니다.

infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt (2)

1-3: 패키지 구조 개선과 enum 사용이 적절합니다.

dto에서 request 패키지로의 이동과 BookCoverSize enum 사용이 코드 구조를 개선합니다.


29-48: 팩토리 메서드 호출 검증 완료

  • AladinBookSearchRequest.create 호출이 전혀 없으며
  • AladinBookSearchRequest.of(...) 호출만 존재합니다.

모든 호출부가 새로운 시그니처에 맞춰 업데이트된 것이 확인되었습니다. 그대로 머지 진행해주세요.

apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBookRegisterRequest.kt (2)

8-11: Swagger 문서화 개선이 좋습니다.

API 문서화를 위한 상세한 스키마 어노테이션 추가가 적절합니다.


23-35: BookStatus 유효성 검증 구현이 좋습니다.

enum 값에 대한 상세한 스키마 정의와 안전한 접근자 메서드 제공이 적절합니다.

infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt (2)

1-4: 일관된 패키지 구조 개선입니다.

다른 request 클래스와 일관된 패키지 이동과 BookCoverSize enum 사용이 좋습니다.


5-8: optResult → cover 변경사항 검증 완료

  • infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookSearchRequest.kt
    • cover?.let { params["Cover"] = it } 로 파라미터 반영
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/request/AladinBookLookupRequest.kt
    • cover?.let { params["Cover"] = it } 로 파라미터 반영
  • infra/src/main/kotlin/org/yapp/infra/external/aladin/AladinRestClient.kt
    • 기존 OptResult 처리 로직이 “Cover”를 일반 queryParam 처리로 수용하므로 별도 보완 불필요

위 검증 결과, optResult 제거 후 cover 속성 추가가 모든 요청 경로에 정상 반영되어 기존 기능에 영향이 없습니다.

apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt (4)

10-13: 상세한 API 문서화가 훌륭합니다.

포괄적인 Swagger 스키마 어노테이션으로 API 문서화가 크게 개선되었습니다.


90-103: ISBN13 중심의 리팩토링이 PR 목표와 일치합니다.

bookDetail.isbn13만 사용하고 coverImageUrl 속성 매핑 변경이 전체 리팩토링 방향과 일관됩니다.


55-63: 출간연도 유효성 검증(@Min/@Max) 동작 확인 요청

현재 publicationYear: Int? 필드에만 @Min/@Max를 적용하면 null 값은 검증 대상에서 제외되어 통과됩니다.

  • 해당 필드가 선택값(optional)인 경우 null 허용이 비즈니스 요구사항에 부합하는지 재확인해주세요.
  • 필수 입력값이어야 한다면 @NotNull을 추가하거나 기본값을 지정하고, Bean Validation이 의도대로 동작하는지 검증하는 테스트 코드를 작성해주세요.

16-23: ISBN 유효성 검증 제약 재검토 요청

현재 BookCreateRequest.kt에서 minLength=13, maxLength=13 제약이 설정되어 있어 10자리 ISBN이 배제됩니다.
시스템 전반에서 ISBN-10 지원 여부 및 다른 DTO 클래스와의 일관성을 검토해 주세요.

  • 파일: apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookCreateRequest.kt (16–23줄)
  • 문제: @Schema 어노테이션의 minLength=13, maxLength=13 제약이 ISBN-10을 허용하지 않음
apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt (2)

21-23: copy() 메서드를 활용한 불변성 유지가 잘 되었습니다!

DTO의 불변성을 지키면서 books 리스트를 업데이트하는 구현이 적절합니다.


38-48: 외부 API 응답 매핑이 깔끔하게 구현되었습니다!

AuthorExtractor를 사용한 저자 정보 추출과 BookSummary.of 팩토리 메서드를 활용한 매핑이 적절합니다.

apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt (1)

23-25: 사용자 도서 상태 업데이트 메서드가 잘 구현되었습니다!

불변성을 유지하면서 상태를 업데이트하는 적절한 구현입니다.

apis/src/main/kotlin/org/yapp/apis/book/usecase/BookUseCase.kt (4)

32-42: 사용자 도서 상태 병합 로직의 모듈화가 잘 되었습니다!

별도의 헬퍼 메서드로 분리하여 가독성과 재사용성이 향상되었습니다.


44-58: 사용자별 도서 상태 조회 로직이 명확하게 구현되었습니다!

ISBN13이 없거나 사용자 도서 정보가 없는 경우를 적절히 처리하고 있습니다.


91-110: 효율적인 상태 병합 로직입니다!

빈 리스트에 대한 조기 반환과 불변성을 유지한 상태 업데이트가 적절합니다.


112-123: ISBN을 키로 하는 상태 맵 생성이 효율적입니다!

associate 함수를 활용한 깔끔한 구현입니다.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt (1)

20-44: 널 가능 프로퍼티 + !! 해제는 런타임 NPE 위험
Bean Validation이 반드시 실행된다는 보장이 없으면 valid*()에서 !!로 즉시 NPE가 발생할 수 있습니다. DTO 자체를 불변·안전하게 유지하려면 프로퍼티를 nullable이 아닌 val로 두고 기본값을 제거하는 편이 안전합니다.

-    val pageNumber: Int? = null,
+    val pageNumber: Int,

-    val quote: String? = null,
+    val quote: String,

-    val review: String? = null,
+    val review: String,

이렇게 하면 valid*() 보조 메서드도 필요 없어집니다.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 91b35e8 and 19cbea0.

📒 Files selected for processing (7)
  • apis/src/main/kotlin/org/yapp/apis/readingrecord/dto/request/CreateReadingRecordRequest.kt (1 hunks)
  • apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt (0 hunks)
  • apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt (2 hunks)
  • domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt (2 hunks)
  • domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordErrorCode.kt (1 hunks)
  • domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordNotFoundException.kt (1 hunks)
  • infra/src/main/kotlin/org/yapp/infra/readingrecord/repository/impl/ReadingRecordRepositoryImpl.kt (1 hunks)
💤 Files with no reviewable changes (1)
  • apis/src/main/kotlin/org/yapp/apis/readingrecord/service/ReadingRecordService.kt
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: hoonyworld
PR: YAPP-Github/Reed-Server#39
File: apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt:45-45
Timestamp: 2025-07-17T15:09:36.267Z
Learning: Response DTO 클래스에서는 불변성을 지키기 위해 프로퍼티를 val로 선언하고, 업데이트가 필요한 경우 copy() 메서드를 사용해야 합니다.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-validation
🔇 Additional comments (6)
domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordNotFoundException.kt (1)

1-8: 도메인 계층으로의 예외 클래스 이동 승인

예외 클래스를 API 계층(org.yapp.apis.readingrecord.exception)에서 도메인 계층(org.yapp.domain.readingrecord.exception)으로 이동한 것은 도메인 주도 설계 원칙에 부합하는 좋은 아키텍처 개선입니다. 구조화된 에러 코드(ReadingRecordErrorCode)를 사용하여 명확한 예외 처리를 제공합니다.

apis/src/main/kotlin/org/yapp/apis/readingrecord/usecase/ReadingRecordUseCase.kt (2)

57-57: 매개변수 순서 일관성 확인

Line 29와 동일하게 매개변수 순서가 변경되었습니다. 두 호출 지점 모두 일관되게 업데이트되어 좋습니다.


29-29: validateUserBookExists 매개변수 순서 일관성 확인 완료

서비스 메서드 시그니처 fun validateUserBookExists(userBookId: UUID, userId: UUID)와 호출부의 매개변수 순서가 일치함을 확인했습니다. 다른 호출 지점도 모두 (userBookId, userId) 순서로 일관되게 반영되어 있으므로 변경 사항을 승인합니다.

domain/src/main/kotlin/org/yapp/domain/readingrecord/ReadingRecordDomainService.kt (2)

5-6: 도메인 예외 처리 개선을 위한 임포트 추가

새로운 에러 코드와 예외 클래스 임포트가 추가되어 구조화된 예외 처리를 지원합니다.


64-69: 예외 처리 확인 완료: GlobalExceptionHandler에서 CommonException(ReadingRecordNotFoundException) 처리
findReadingRecordById가 던지는 ReadingRecordNotFoundExceptionCommonException의 하위 클래스이며, 전역 예외 처리기(GlobalExceptionHandler)의
@ExceptionHandler(CommonException::class)에서 이미 처리되고 있습니다. 추가적인 호출 지점별 예외 처리 코드는 필요하지 않습니다.

domain/src/main/kotlin/org/yapp/domain/readingrecord/exception/ReadingRecordErrorCode.kt (1)

1-16: 구조화된 에러 코드 enum 구현 승인

ReadingRecordErrorCode enum이 잘 설계되었습니다:

  • BaseErrorCode 인터페이스를 올바르게 구현
  • 적절한 HTTP 상태 코드(404) 사용
  • 명확한 한국어 에러 메시지 제공
  • 향후 에러 코드 확장에 용이한 구조
  • 프로젝트의 다른 에러 코드 enum들과 일관된 패턴

도메인 계층에서의 명시적 에러 처리를 위한 좋은 기반을 제공합니다.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Aug 4, 2025

Quality Gate Failed Quality Gate failed

Failed conditions
2.6% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@move-hoon move-hoon merged commit 89989ab into develop Aug 4, 2025
2 of 3 checks passed
@move-hoon move-hoon deleted the BOOK-207-refactor/#71 branch August 12, 2025 06:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BOOK-207/refactor] 도서 상세 조회 시 status와 totalPage를 같이 제공하도록 리팩토링

2 participants