Skip to content

feat(#4): 도서 목록·검색 API 추가 - #7

Merged
fnzl54 merged 1 commit into
mainfrom
feat/#4
Aug 7, 2026
Merged

feat(#4): 도서 목록·검색 API 추가#7
fnzl54 merged 1 commit into
mainfrom
feat/#4

Conversation

@fnzl54

@fnzl54 fnzl54 commented Aug 7, 2026

Copy link
Copy Markdown
Member

연관 이슈

작업 사항

  • GET /books/search?q=&page=&pageSize=
    • q 가 없거나 공백이면 전체 조회, 값이 있으면 제목·저자 부분 일치 검색
  • 포트/어댑터
    • MVP 개발 이후 검색 엔진 교체가 설정 한 줄로 끝나도록 유스케이스,도메인을 엔진에서 격리
    • search.engine 값만 바꿔 RDB / OpenSearch를 번갈아 확인할 수 있어 MVP 전후 테스트 가능한 구조로 설계

테스트

  • 로컬 서버 테스트 완료

주의 사항 및 참고사항

@fnzl54 fnzl54 self-assigned this Aug 7, 2026
import org.library.core.presentation.Pagination
import org.springframework.stereotype.Service

@Service

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[MEDIUM] SearchBooksService에 읽기 전용 트랜잭션 경계가 없습니다

Problem: 이 서비스는 순수 조회(검색/목록) 유스케이스인데 @Transactional이 전혀 선언되어 있지 않습니다. 같은 모듈의 동일 성격 유스케이스인 GetBookService는 클래스 레벨에 @Transactional(readOnly = true)를 명시하고 있어, 이 PR만 그 관례에서 벗어납니다.

Evidence: GetBookService

@Service
@Transactional(readOnly = true)
에서 @Transactional(readOnly = true)를 선언합니다. 트랜잭션 경계·readOnly 여부는 엔티티/영속성 컨벤션에서 명시적으로 점검 대상입니다. 지금은 Spring Data JPA의 기본 트랜잭션에 암묵적으로 의존하고 있어, 조회 로직이 향후 지연 로딩 연관을 다루게 되면 트랜잭션 경계가 서비스가 아닌 리포지토리/어댑터에 암묵적으로 걸리는 구조가 됩니다.

Fix direction: GetBookService와 동일하게 클래스 레벨에 @Transactional(readOnly = true)를 추가해 주세요.

@Service
@Transactional(readOnly = true)
class SearchBooksService(
    private val bookSearchPort: BookSearchPort,
) {
    ...
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

RdbBookSearchAdapter@Transactional 반영하였습니다.

SearchBooksServiceBookSearchPort 만 의존해 DB의 존재를 모르는 유스케이스라 @Transactional 을 붙이면 JPA 트랜잭션 선언이 유스케이스로 올라와 포트를 둔 목적과 어긋난다고 판단

import org.library.core.presentation.Pagination

@Schema(description = "도서 목록·검색 응답")
data class BookSearchResponse(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[LOW] BookSearchResponse가 단일 유스케이스 전용인데 모듈 dto/에 위치합니다

Problem: BookSearchResponseSearchBooksService 한 곳에서만 사용되는데, 여러 유스케이스가 공유하는 응답을 모아두는 book/dto/ 패키지에 최상위 클래스로 추출되어 있습니다. 프로젝트 컨벤션상 Request/Response는 단일 유스케이스 전용이면 서비스 내부 nested data class여야 하고, 2개 이상 유스케이스가 공유할 때만 dto/로 추출합니다.

Evidence: 같은 패키지의 BookResponseCreateBookService, GetBookService, UpdateBookService 세 곳에서 공유되기 때문에 dto/에 위치하는 것이 맞지만, BookSearchResponseSearchBooksService 전용입니다.

Fix direction: SearchBooksService 내부의 nested data class로 옮겨주세요.

@Service
class SearchBooksService(
    private val bookSearchPort: BookSearchPort,
) {
    fun execute(query: String?, params: PageRequestParams): Response { ... }

    @Schema(description = "도서 목록·검색 응답")
    data class Response(
        val books: List<BookDocument>,
        val pagination: Pagination,
    )
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

수정 완료

@fnzl54
fnzl54 merged commit 6b46bba into main Aug 7, 2026
@fnzl54
fnzl54 deleted the feat/#4 branch August 7, 2026 07:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant