-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 도서 등록 및 도서 목록 조회 #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 19 commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
0c66457
[BOOK-96] feat: infra - UserBookEntity 구현 (#29)
minwoo1999 e36fcf9
[BOOK-96] feat: infra - UserBookRepository 구현 (#29)
minwoo1999 374356d
[BOOK-96] refactor: infra - UserBookEntity 수정 (#29)
minwoo1999 7be19e0
[BOOK-96] feat: domain - book,userbook domainservice 분리 (#29)
minwoo1999 8ac5039
[BOOK-96] feat: domain - UserBook Domain 모델 구현 (#29)
minwoo1999 d0722ff
[BOOK-96] refactor: domain - Book Domain 모델 수정 (#29)
minwoo1999 a0882aa
[BOOK-96] feat: apis - 도서등록,내서재 Usecase 구현 (#29)
minwoo1999 7241fdb
[BOOK-96] feat: apis - Book Exception 정의 (#29)
minwoo1999 e63d655
[BOOK-96] feat: apis - UserBook Dto 구현 (#29)
minwoo1999 cbced98
[BOOK-96] refactor: apis - BookDetail DTO 수정 (#29)
minwoo1999 5cc8b78
[BOOK-96] feat: apis - Controller 도서등록, 내서재 구현 (#29)
minwoo1999 352a221
[BOOK-96] chore: infra,domain,apis import * 제거
minwoo1999 d635d9f
[BOOK-96] refator: infra - 상태 변경이 필요한 필드를 var + protected set (#29)
minwoo1999 1077b85
[BOOK-96] feat: domain - userBook 도메인계층 기능개발 (#29)
minwoo1999 ea6f796
[BOOK-96] refactor: apis - service계층 분리 (#29)
minwoo1999 e744548
[BOOK-96] feat: apis - dto 정의 (#29)
minwoo1999 8772208
[BOOK-96] refactor: apis - usecase service 분리 (#29)
minwoo1999 d70529f
[BOOK-96] refactor: apis - AuthenticationPrincipal를 통해 userId 주입 …
minwoo1999 31b643c
[BOOK-96] refactor: apis - api swagger 명세서 response 수정
minwoo1999 3be48ab
[BOOK-96] refactor: apis,domain,infra - user validate 메소드 별도로 분리
minwoo1999 753d309
[BOOK-96] chore: apis - 불필요한 코드 제거 (#29)
minwoo1999 2931e91
[BOOK-96] refactor: apis,domain - 패키지구조 변경 (#29)
minwoo1999 e66845a
[BOOK-96] refactor: apis- 패키지구조 변경 (#29)
minwoo1999 91d69cf
[BOOK-96] refactor: apis- 외부 API 인터페이스 추상화 (#29)
minwoo1999 3911657
[BOOK-96] refactor: apis- book도메인 객체 domainservice 내부에서 생성 (#29)
minwoo1999 c1cfc11
[BOOK-96] refactor: apis- BookQueryServiceQualifier 상수처리 (#29)
minwoo1999 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,31 +3,32 @@ package org.yapp.apis.book.controller | |
| import io.swagger.v3.oas.annotations.Operation | ||
| import io.swagger.v3.oas.annotations.Parameter | ||
| import io.swagger.v3.oas.annotations.media.Content | ||
| import io.swagger.v3.oas.annotations.media.ExampleObject | ||
| import io.swagger.v3.oas.annotations.media.Schema | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponse | ||
| import io.swagger.v3.oas.annotations.responses.ApiResponses | ||
| import io.swagger.v3.oas.annotations.tags.Tag | ||
| import jakarta.validation.Valid | ||
| import org.springframework.http.ResponseEntity | ||
| import org.springframework.security.core.annotation.AuthenticationPrincipal | ||
| import org.springframework.web.bind.annotation.GetMapping | ||
| import org.springframework.web.bind.annotation.PutMapping | ||
| import org.springframework.web.bind.annotation.RequestBody | ||
| import org.springframework.web.bind.annotation.RequestMapping | ||
| import org.yapp.apis.book.dto.request.BookDetailRequest | ||
| import org.yapp.apis.book.dto.request.BookSearchRequest | ||
| import org.yapp.apis.book.dto.request.UserBookRegisterRequest | ||
| import org.yapp.apis.book.dto.response.BookDetailResponse | ||
| import org.yapp.apis.book.dto.response.BookSearchResponse | ||
| import org.yapp.apis.book.dto.response.UserBookResponse | ||
| import org.yapp.globalutils.exception.ErrorResponse | ||
| import java.util.UUID | ||
|
|
||
|
|
||
| /** | ||
| * API interface for book controller. | ||
| */ | ||
| @Tag(name = "Books", description = "도서 정보를 조회하는 API") | ||
| @RequestMapping("/api/v1/books") | ||
| interface BookControllerApi { | ||
|
|
||
| @Operation( | ||
| summary = "도서 검색", | ||
| description = "키워드를 사용하여 알라딘 도서 정보를 검색합니다." | ||
| ) | ||
| @Operation(summary = "도서 검색", description = "키워드를 사용하여 알라딘 도서 정보를 검색합니다.") | ||
| @ApiResponses( | ||
| value = [ | ||
| ApiResponse( | ||
|
|
@@ -37,41 +38,15 @@ interface BookControllerApi { | |
| ), | ||
| ApiResponse( | ||
| responseCode = "400", | ||
| description = "잘못된 요청 파라미터" | ||
| description = "잘못된 요청 파라미터", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ) | ||
| ] | ||
| ) | ||
| @GetMapping("/search") | ||
| fun searchBooks( | ||
| @Valid | ||
| @Parameter( | ||
| description = "도서 검색 요청 객체. 다음 쿼리 파라미터를 포함합니다:<br>" + | ||
| "- `query` (필수): 검색어 <br>" + | ||
| "- `queryType` (선택): 검색어 타입 (예: Title, Author). 기본값은 All <br>" + | ||
| "- `maxResults` (선택): 한 페이지당 결과 개수 (1-50). 기본값 10 <br>" + | ||
| "- `start` (선택): 결과 시작 페이지. 기본값 1 <br>" + | ||
| "- `sort` (선택): 정렬 방식 (예: PublishTime, SalesPoint). 기본값 Accuracy <br>" + | ||
| "- `categoryId` (선택): 카테고리 ID", | ||
| examples = [ | ||
| ExampleObject(name = "기본 검색", value = "http://localhost:8080/api/v1/books/search?query=코틀린"), | ||
| ExampleObject( | ||
| name = "상세 검색", | ||
| value = "http://localhost:8080/api/v1/books/search?query=클린코드&queryType=Title&maxResults=10&sort=PublishTime" | ||
| ), | ||
| ExampleObject( | ||
| name = "카테고리 검색", | ||
| value = "http://localhost:8080/api/v1/books/search?query=Spring&categoryId=170&start=2&maxResults=5" | ||
| ) | ||
| ] | ||
| ) | ||
| request: BookSearchRequest | ||
| ): ResponseEntity<BookSearchResponse> | ||
|
|
||
| fun searchBooks(@Valid @Parameter(description = "도서 검색 요청 객체") request: BookSearchRequest): ResponseEntity<BookSearchResponse> | ||
|
|
||
| @Operation( | ||
| summary = "도서 상세 조회", | ||
| description = "특정 도서의 상세 정보를 조회합니다. `itemId`는 쿼리 파라미터로 전달됩니다." | ||
| ) | ||
| @Operation(summary = "도서 상세 조회", description = "특정 도서의 상세 정보를 조회합니다.") | ||
| @ApiResponses( | ||
| value = [ | ||
| ApiResponse( | ||
|
|
@@ -81,33 +56,75 @@ interface BookControllerApi { | |
| ), | ||
| ApiResponse( | ||
| responseCode = "400", | ||
| description = "잘못된 요청 파라미터 (예: 유효하지 않은 itemId 또는 itemIdType)" | ||
| description = "잘못된 요청 파라미터", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ), | ||
| ApiResponse( | ||
| responseCode = "404", | ||
| description = "해당하는 itemId를 가진 도서를 찾을 수 없습니다." | ||
| description = "해당하는 itemId를 가진 도서를 찾을 수 없습니다.", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ) | ||
| ] | ||
| ) | ||
| @GetMapping("/detail") | ||
| fun getBookDetail( | ||
| @Valid | ||
| @Parameter( | ||
| description = "도서 상세 조회 요청 객체. 다음 쿼리 파라미터를 포함합니다:<br>" + | ||
| "- `itemId` (필수): 조회할 도서의 고유 ID (ISBN, ISBN13, 알라딘 ItemId 등)<br>" + | ||
| "- `itemIdType` (선택): `itemId`의 타입 (ISBN, ISBN13, ItemId). 기본값은 ISBN입니다.<br>" + | ||
| "- `optResult` (선택): 조회할 부가 정보 목록 (쉼표로 구분). 예시: `BookInfo,Toc,PreviewImg`", | ||
| examples = [ | ||
| ExampleObject( | ||
| name = "ISBN으로 상세 조회", | ||
| value = "http://localhost:8080/api/v1/books/detail?itemId=9791162241684&itemIdType=ISBN13" | ||
| ), | ||
| ExampleObject( | ||
| name = "ISBN 및 부가 정보 포함", | ||
| value = "http://localhost:8080/api/v1/books/detail?itemId=8994492040&itemIdType=ISBN&optResult=BookInfo,Toc" | ||
| ) | ||
| ] | ||
| ) | ||
| request: BookDetailRequest | ||
| ): ResponseEntity<BookDetailResponse> | ||
| fun getBookDetail(@Valid @Parameter(description = "도서 상세 조회 요청 객체") request: BookDetailRequest): ResponseEntity<BookDetailResponse> | ||
|
|
||
| @Operation(summary = "서재에 책 등록 또는 상태 업데이트 (Upsert)", description = "사용자의 서재에 책을 등록하거나, 이미 등록된 책의 상태를 업데이트합니다.") | ||
| @ApiResponses( | ||
| value = [ | ||
| ApiResponse( | ||
| responseCode = "201", | ||
| description = "책이 서재에 성공적으로 등록되었습니다.", | ||
| content = [Content(schema = Schema(implementation = UserBookResponse::class))] | ||
| ), | ||
| ApiResponse( | ||
| responseCode = "200", | ||
| description = "책 상태가 성공적으로 업데이트되었습니다.", | ||
| content = [Content(schema = Schema(implementation = UserBookResponse::class))] | ||
| ), | ||
| ApiResponse( | ||
| responseCode = "400", | ||
| description = "잘못된 요청 파라미터", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ), | ||
| ApiResponse( | ||
| responseCode = "404", | ||
| description = "존재하지 않는 책 (ISBN 오류)", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ) | ||
| ] | ||
| ) | ||
| @PutMapping("/upsert") | ||
| fun upsertBookToMyLibrary( | ||
| @AuthenticationPrincipal userId: UUID, | ||
| @Valid @RequestBody request: UserBookRegisterRequest | ||
| ): ResponseEntity<UserBookResponse> | ||
|
|
||
| @Operation(summary = "사용자 서재 조회", description = "현재 사용자의 서재에 등록된 모든 책을 조회합니다.") | ||
| @ApiResponses( | ||
| value = [ | ||
| ApiResponse( | ||
| responseCode = "200", | ||
| description = "서재 조회 성공", | ||
| content = [Content(schema = Schema(implementation = UserBookResponse::class))] | ||
| ), | ||
| ApiResponse( | ||
| responseCode = "400", | ||
| description = "Invalid request or credentials", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ), | ||
| ApiResponse( | ||
| responseCode = "409", | ||
| description = "Email already in use with a different account", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ), | ||
| ApiResponse( | ||
| responseCode = "404", | ||
| description = "사용자를 찾을 수 없습니다.", | ||
| content = [Content(schema = Schema(implementation = ErrorResponse::class))] | ||
| ) | ||
| ] | ||
| ) | ||
| @GetMapping("/my-library") | ||
| fun getUserLibraryBooks(@AuthenticationPrincipal userId: UUID): ResponseEntity<List<UserBookResponse>> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ApiResponses 설명이 현재 API와 맞지 않습니다. 라인 113, 118-119의 description들이 다른 API (인증 관련)에서 복사된 것으로 보입니다. 사용자 서재 조회 API에 맞게 수정해주세요. - responseCode = "400",
- description = "Invalid request or credentials",
+ responseCode = "400",
+ description = "잘못된 요청",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
),
ApiResponse(
- responseCode = "409",
- description = "Email already in use with a different account",
- content = [Content(schema = Schema(implementation = ErrorResponse::class))]
- ),
- ApiResponse(
responseCode = "404",
description = "사용자를 찾을 수 없습니다.",
content = [Content(schema = Schema(implementation = ErrorResponse::class))]또한 409 상태 코드는 이 API에서 발생하지 않을 것 같으니 제거하는 것이 좋겠습니다.
🤖 Prompt for AI Agents |
||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick (assertive)
인증 처리와 HTTP 메서드 선택이 개선되었습니다.
과거 리뷰에서 제안된 사항들이 잘 반영되었습니다:
@PutMapping사용으로 멱등성 보장@AuthenticationPrincipal사용으로 인증 처리 개선ErrorResponse스키마 사용다만
/upsert경로명은 여전히 RESTful하지 않습니다./my-library또는/library/books와 같은 리소스 중심의 경로명을 고려해보세요.🤖 Prompt for AI Agents