Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
517ae17
[BOOK-79] feat: RestClient 알라딘 API적용 (#18)
minwoo1999 Jul 3, 2025
f048cf5
[BOOK-79] fix: IllegalArgumentException 500 공통에러로 떨어지는 문제 해결 (#15)
minwoo1999 Jul 3, 2025
cdb4ba8
[BOOK-79] feat: apis - 알라딘 도서검색, 도서상세검색 Controlller (#15)
minwoo1999 Jul 3, 2025
c8782d9
[BOOK-79] feat: apis - 알라딘 도서검색, 도서상세검색 DTO (#15)
minwoo1999 Jul 3, 2025
d3b0c44
[BOOK-79] feat: apis - 알라딘 도서검색, 도서상세검색 외부 API Helper (#15)
minwoo1999 Jul 3, 2025
d91523a
[BOOK-79] feat: apis - 알라딘 도서검색, 도서상세검색 UseCase (#15)
minwoo1999 Jul 3, 2025
3148203
[BOOK-79] feat: apis - 알라딘 도서검색, 도서상세검색 Service (#15)
minwoo1999 Jul 3, 2025
a9ffd4f
[BOOK-79] feat: apis - 알라딘 도서검색, 도서상세검색 외부 API Response 정의 (#15)
minwoo1999 Jul 3, 2025
629053b
[BOOK-79] feat: gateway - 도서관련 security permitAll (#15)
minwoo1999 Jul 3, 2025
6c4f147
[BOOK-79] feat: domain - domain model (#15)
minwoo1999 Jul 3, 2025
b5856e1
[BOOK-79] feat: infra - domain entity 설계 (#15)
minwoo1999 Jul 3, 2025
2d22b33
[BOOK-79] refactor: global-util - HttpRequestMethodNotSupportedExcep…
minwoo1999 Jul 3, 2025
35632d8
Merge branch 'develop' into BOOK-79-feature/#18
minwoo1999 Jul 4, 2025
11121ff
[BOOK-79] chore: infra external.yml파일 알라딘 api key 세팅 (#15)
minwoo1999 Jul 4, 2025
f98a2d9
[BOOK-79] refactor: infra user impl 분리 (#15)
minwoo1999 Jul 4, 2025
e2fdf05
[BOOK-79] refactor: infra - 알라딘 책 가격 부동소수점 오류를 위한 BigDecimal (#15)
minwoo1999 Jul 4, 2025
62fe27a
[BOOK-79] refactor: infra - 알라딘 외부 API용 DTO분리 (#15)
minwoo1999 Jul 4, 2025
c354dcb
[BOOK-79] refactor: apis - external yml group 추가 (#15)
minwoo1999 Jul 4, 2025
edacec3
[BOOK-79] refactor: apis - 요청 DTO값 분리 및 알라딘 외부용 API 분리 (#15)
minwoo1999 Jul 4, 2025
887d2a9
[BOOK-79] refactor: admin - external yml group 추가 (#15)
minwoo1999 Jul 4, 2025
3ff9bc2
[BOOK-79] fix: apis - inner dto class 이름변경 (#15)
minwoo1999 Jul 4, 2025
b76cae6
[BOOK-79] feat: infra - BookRepository 기능개발 (#15)
minwoo1999 Jul 4, 2025
6313699
[BOOK-79] feat: domain - BookRepository 기능개발 (#15)
minwoo1999 Jul 4, 2025
433c2f1
[BOOK-79] refactor: infra,apis RestClient 각 외부 APi별 분리 (#15)
minwoo1999 Jul 5, 2025
9bbebe6
[BOOK-79] chore: infra,apis 필요없는 코드 삭제 (#15)
minwoo1999 Jul 5, 2025
26b7da8
[BOOK-79] refactor: apis,global-utils validation 강화 (#15)
minwoo1999 Jul 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions admin/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ spring:
- persistence
- jwt
- redis
- external
prod:
- persistence
- jwt
- redis
- external
test:
- persistence
- jwt
Expand Down
4 changes: 2 additions & 2 deletions apis/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ dependencies {
implementation(project(Dependencies.Projects.GATEWAY))

implementation(Dependencies.Spring.BOOT_STARTER_WEB)
testImplementation(Dependencies.Spring.BOOT_STARTER_TEST)
implementation(Dependencies.Spring.BOOT_STARTER_DATA_JPA)
implementation(Dependencies.Spring.BOOT_STARTER_SECURITY)
implementation(Dependencies.Spring.BOOT_STARTER_VALIDATION)
Expand All @@ -22,11 +21,12 @@ dependencies {
implementation(Dependencies.Swagger.SPRINGDOC_OPENAPI_STARTER_WEBMVC_UI)

implementation(Dependencies.Logging.KOTLIN_LOGGING)
implementation(Dependencies.Feign.STARTER_OPENFEIGN)

testImplementation(Dependencies.Spring.BOOT_STARTER_TEST)
Copy link

@coderabbitai coderabbitai bot Jul 4, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

spring-boot-starter-test 의존성 중복

10행과 26행에 동일 의존성이 중복되어 있습니다. 하나만 남기고 제거해 주세요.

-    testImplementation(Dependencies.Spring.BOOT_STARTER_TEST)
📝 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
testImplementation(Dependencies.Spring.BOOT_STARTER_TEST)
🤖 Prompt for AI Agents
In apis/build.gradle.kts at line 26, the dependency spring-boot-starter-test is
duplicated as it also appears at line 10. Remove the duplicate declaration at
line 26 and keep only the one at line 10 to avoid redundancy.

Copy link
Member

Choose a reason for hiding this comment

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

@minwoo1999 요것도 확인부탁드려요!!

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

testImplementation(Dependencies.TestContainers.MYSQL)
testImplementation(Dependencies.TestContainers.JUNIT_JUPITER)
testImplementation(Dependencies.TestContainers.REDIS)

}

tasks {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.yapp.apis.book.controller

import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.ModelAttribute
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.yapp.apis.book.dto.request.BookDetailRequest
import org.yapp.apis.book.dto.request.BookSearchRequest
import org.yapp.apis.book.dto.response.BookDetailResponse
import org.yapp.apis.book.dto.response.BookSearchResponse
import org.yapp.apis.book.usecase.BookUseCase


@RestController
@RequestMapping("/api/v1/books")
class BookController(
private val bookUseCase: BookUseCase
) : BookControllerApi {

@GetMapping("/search")
override fun searchBooks(@Valid @ModelAttribute request: BookSearchRequest): ResponseEntity<BookSearchResponse> {
val response = bookUseCase.searchBooks(request)
return ResponseEntity.ok(response)
}

@GetMapping("/detail")
override fun getBookDetail(
@Valid @ModelAttribute request: BookDetailRequest
): ResponseEntity<BookDetailResponse> {
val response = bookUseCase.getBookDetail(request)
return ResponseEntity.ok(response)
}
}
Comment on lines +16 to +35
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

예외 처리 고려 제안

현재 controller에서는 성공 케이스만 처리하고 있습니다. UseCase나 하위 계층에서 발생할 수 있는 예외에 대한 처리를 고려해보세요.

GlobalExceptionHandler에서 공통 예외 처리를 하고 있다면 현재 구조도 충분하지만, 비즈니스 로직 특화된 예외 처리가 필요한 경우 추가 고려가 필요합니다.

🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/controller/BookController.kt around
lines 16 to 35, the controller currently only handles successful responses
without managing exceptions from the use case layer. To fix this, add try-catch
blocks around the calls to bookUseCase methods to catch and handle
business-specific exceptions, returning appropriate error responses.
Alternatively, if a GlobalExceptionHandler is used, ensure it covers all
relevant exceptions; otherwise, implement specific exception handling in the
controller to manage business logic errors properly.

Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
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.web.bind.annotation.GetMapping
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.response.BookDetailResponse
import org.yapp.apis.book.dto.response.BookSearchResponse

/**
* API interface for book controller.
*/
@Tag(name = "Books", description = "도서 정보를 조회하는 API")
@RequestMapping("/api/v1/books")
interface BookControllerApi {

@Operation(
summary = "도서 검색",
description = "키워드를 사용하여 알라딘 도서 정보를 검색합니다."
)
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "성공적인 검색",
content = [Content(schema = Schema(implementation = BookSearchResponse::class))]
),
ApiResponse(
responseCode = "400",
description = "잘못된 요청 파라미터"
)
]
)
@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>


@Operation(
summary = "도서 상세 조회",
description = "특정 도서의 상세 정보를 조회합니다. `itemId`는 쿼리 파라미터로 전달됩니다."
)
@ApiResponses(
value = [
ApiResponse(
responseCode = "200",
description = "성공적으로 도서 상세 정보를 조회했습니다.",
content = [Content(schema = Schema(implementation = BookDetailResponse::class))]
),
ApiResponse(
responseCode = "400",
description = "잘못된 요청 파라미터 (예: 유효하지 않은 itemId 또는 itemIdType)"
),
ApiResponse(
responseCode = "404",
description = "해당하는 itemId를 가진 도서를 찾을 수 없습니다."
)
]
)
@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>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.yapp.apis.book.dto.request

import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Pattern // Pattern 어노테이션 추가
import org.yapp.globalutils.util.RegexUtils
import org.yapp.infra.external.aladin.dto.AladinBookLookupRequest

data class BookDetailRequest private constructor(
@field:NotBlank(message = "아이템 ID는 필수입니다.")
@field:Pattern(
regexp = RegexUtils.NOT_BLANK_AND_NOT_NULL_STRING_PATTERN,
message = "아이템 ID는 유효한 ISBN 형식이 아닙니다."
)
val itemId: String? = null,
val itemIdType: String? = "ISBN",
val optResult: List<String>? = null
) {
fun toAladinRequest(): AladinBookLookupRequest {
return AladinBookLookupRequest.create(
itemId = this.itemId!!,
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

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

toAladinRequest() 메소드에서 this.itemId!!와 같은 non-null assertion을 사용하는 것은 위험합니다. validation이 실패하거나 예상치 못한 상황에서 런타임 예외가 발생할 수 있습니다.

-itemId = this.itemId!!,
+itemId = this.itemId ?: throw IllegalArgumentException("Item ID is required"),

또는 validation 통과를 보장하는 더 안전한 방법을 사용하는 것을 권장합니다.

🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt at
line 20, the use of non-null assertion (!!) on this.itemId is unsafe and can
cause runtime exceptions if itemId is null. To fix this, ensure itemId is
validated before this method is called or refactor the method to safely handle
null values, such as using a safe call with an appropriate fallback or throwing
a controlled exception with a clear message instead of using !!.

itemIdType = this.itemIdType ?: "ISBN",
optResult = this.optResult
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.yapp.apis.book.dto.request

import org.yapp.infra.external.aladin.dto.AladinBookSearchRequest


data class BookSearchRequest private constructor(
val query: String? = null,
val queryType: String? = null,
val searchTarget: String? = null,
val maxResults: Int? = null,
val start: Int? = null,
val sort: String? = null,
val cover: String? = null,
val categoryId: Int? = null
) {

fun validQuery(): String = query!!
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

안전한 검증 방법으로 개선 필요

validQuery() 메소드에서 query!!와 같은 non-null assertion을 사용하는 것은 위험할 수 있습니다. validation이 실패하거나 호출 순서가 잘못되면 KotlinNullPointerException이 발생할 수 있습니다.

-fun validQuery(): String = query!!
+fun validQuery(): String = query ?: throw IllegalArgumentException("Query parameter is required")

또는 validation 통과를 보장하는 안전한 방법을 사용하는 것을 권장합니다.

🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookSearchRequest.kt at
line 17, the validQuery() method uses a non-null assertion (!!) on query, which
can cause a KotlinNullPointerException if query is null. To fix this, replace
the non-null assertion with a safe validation check that ensures query is not
null before returning it, or throw a controlled exception with a clear message
if query is null, thereby avoiding unsafe calls.

fun toAladinRequest(): AladinBookSearchRequest {

return AladinBookSearchRequest.create(
query = this.validQuery(),
queryType = this.queryType,
searchTarget = this.searchTarget,
maxResults = this.maxResults,
start = this.start,
sort = this.sort,
cover = this.cover,
categoryId = this.categoryId
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.yapp.apis.book.dto.response

import org.yapp.infra.external.aladin.response.AladinBookDetailResponse
import java.math.BigDecimal

/**
* 단일 도서의 상세 정보를 나타내는 DTO.
* 외부 API 응답 및 도메인 Book 객체로부터 변환됩니다.
*/
data class BookDetailResponse private constructor(
val version: String?,
val title: String,
val link: String?,
val author: String?,
val pubDate: String?,
val description: String?,
val isbn: String?,
val isbn13: String?,
val itemId: Long?,
val priceSales: BigDecimal?,
val priceStandard: BigDecimal?,
val mallType: String?,
val stockStatus: String?,
val mileage: Int?,
val cover: String?,
val categoryId: Int?,
val categoryName: String?,
val publisher: String?
) {
companion object {
/**
* AladinBookDetailResponse와 Book 도메인 객체로부터 BookDetailResponse를 생성합니다.
*/
fun from(response: AladinBookDetailResponse): BookDetailResponse {
val bookItem = response.item?.firstOrNull()
?: throw IllegalArgumentException("No book item found in detail response.")

return BookDetailResponse(
version = response.version,
title = bookItem.title ?: "",
link = bookItem.link,
author = bookItem.author ?: "",
pubDate = bookItem.pubDate,
description = bookItem.description ?: "",
isbn = bookItem.isbn ?: bookItem.isbn13 ?: "",
Copy link

@coderabbitai coderabbitai bot Jul 3, 2025

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

ISBN 필드 우선순위 로직 개선

ISBN 필드에서 isbn 또는 isbn13 중 하나를 선택하는 로직이 있습니다. 일반적으로 ISBN13이 더 표준적이므로 우선순위를 고려해보세요.

-                isbn = bookItem.isbn ?: bookItem.isbn13 ?: "",
+                isbn = bookItem.isbn13 ?: bookItem.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
isbn = bookItem.isbn ?: bookItem.isbn13 ?: "",
isbn = bookItem.isbn13 ?: bookItem.isbn ?: "",
🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookDetailResponse.kt at
line 44, the current logic assigns the ISBN field by prioritizing 'isbn' over
'isbn13'. Since ISBN13 is generally more standard, update the assignment to
prioritize 'isbn13' first, falling back to 'isbn' if 'isbn13' is null or empty,
and default to an empty string if both are unavailable.

Copy link
Member

Choose a reason for hiding this comment

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

isbn13 좋습니다 ㅎㅎ

Copy link

Choose a reason for hiding this comment

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

@hoonyworld 감사합니다! ISBN13이 더 표준적이고 범용적이라 좋은 선택이라고 생각합니다. 👍

/|   /|  

( ˶ᵔ ᵕ ᵔ˶ )
| _/ |
| |
\ /
___/
Happy CodeRabbit!


🧠 Learnings used
Learnt from: hoonyworld
PR: YAPP-Github/26th-App-Team-1-BE#17
File: .github/workflows/ci-pr.yml:8-9
Timestamp: 2025-07-01T09:50:45.498Z
Learning: hoonyworld 사용자는 Naver 코드 포맷터를 사용하며, 이 포맷터는 YAML 배열의 대괄호 내부에 자동으로 여분의 공백을 추가하는 특성이 있음.

If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

isbn13 = bookItem.isbn13,
itemId = bookItem.itemId,
priceSales = bookItem.priceSales,
priceStandard = bookItem.priceStandard,
mallType = bookItem.mallType,
stockStatus = bookItem.stockStatus,
mileage = bookItem.mileage,
cover = bookItem.cover ?: "",
categoryId = bookItem.categoryId,
categoryName = bookItem.categoryName,
publisher = bookItem.publisher ?: "",
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.yapp.apis.book.dto.response

import org.yapp.infra.external.aladin.response.AladinSearchResponse
import org.yapp.infra.external.aladin.response.BookItem

data class BookSearchResponse private constructor(
val version: String?,
val title: String?,
val link: String?,
val pubDate: String?,
val totalResults: Int?,
val startIndex: Int?,
val itemsPerPage: Int?,
val query: String?,
val searchCategoryId: Int?,
val searchCategoryName: String?,
val books: List<BookSummary>
Comment on lines +6 to +17
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 필드가 많은 메타데이터 구조 검토

BookSearchResponse의 대부분 필드가 nullable로 정의되어 있습니다. 외부 API 응답의 불확실성을 고려한 설계로 보이지만, 일부 필드(totalResults, startIndex, itemsPerPage 등)는 검색 결과의 핵심 정보로서 non-null이 더 적절할 수 있습니다.

외부 API 응답에서 이러한 필드들이 항상 제공되는지 확인하고, 필요시 기본값을 제공하는 것을 고려해보세요.

🤖 Prompt for AI Agents
In apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookSearchResponse.kt
between lines 6 and 17, several fields like totalResults, startIndex, and
itemsPerPage are currently nullable but represent core search result metadata.
Review the external API to confirm if these fields are always present; if so,
change their types to non-nullable and provide default values if necessary to
ensure these fields always have valid data in the response object.

) {
companion object {
fun from(response: AladinSearchResponse): BookSearchResponse {
val books = response.item?.mapNotNull { BookSummary.fromAladinItem(it) } ?: emptyList()
return BookSearchResponse(
version = response.version,
title = response.title,
link = response.link,
pubDate = response.pubDate,
totalResults = response.totalResults,
startIndex = response.startIndex,
itemsPerPage = response.itemsPerPage,
query = response.query,
searchCategoryId = response.searchCategoryId,
searchCategoryName = response.searchCategoryName,
books = books
)
}
}

data class BookSummary private constructor(
val isbn: String,
val title: String,
val author: String?,
val publisher: String?,
val coverImageUrl: String?,
) {
companion object {
private val unknownTitle = "제목없음"

fun fromAladinItem(item: BookItem): BookSummary? {
val isbn = item.isbn ?: item.isbn13 ?: return null
Copy link
Member

Choose a reason for hiding this comment

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

해당 부분 위에서는 isbn13을 사용하셨는데, 해당 방식으로 처리하신 이유는 확장성 때문일까요?

Copy link
Member Author

Choose a reason for hiding this comment

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

네네 맞아요! 아무래도 ISBN값을 키값으로 잡았는데 없을 경우를 대비해서 저렇게 잡았습니다 ~

return BookSummary(
isbn = isbn,
title = item.title ?: unknownTitle,
author = item.author,
publisher = item.publisher,
coverImageUrl = item.cover
)
}
}
}
}
Loading