Skip to content

Commit c0d258b

Browse files
authored
refactor: 모든 ISBN 사용 로직을 ISBN13을 사용하도록 변경 (#80)
* [BOOK-235] refactor: 모든 ISBN 사용 로직을 ISBN13을 사용하도록 변경 * [BOOK-235] fix: infra - 칼럼 이름 필드명이랑 통일 * [BOOK-235] refactor: apis, domain - 코드레빗 리뷰 반영 * [BOOK-235] chore: apis - UserBooksByIsbn13sRequest DTO의 설명을 ISBN13으로 수정 * [BOOK-235] refactor: global-utils - ISBN 유효성 검사 메서드를 ISBN10과 ISBN13으로 분리 * [BOOK-235] refactor: domain - ISBN 유효성 검사 메서드를 ISBN13으로 변경 * [BOOK-235] refactor: apis - ISBN 유효성 검사 로직을 별도의 메서드로 분리하여 가독성 향상
1 parent 80300e9 commit c0d258b

File tree

30 files changed

+180
-169
lines changed

30 files changed

+180
-169
lines changed

apis/src/main/kotlin/org/yapp/apis/book/controller/BookControllerApi.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ interface BookControllerApi {
101101
),
102102
ApiResponse(
103103
responseCode = "404",
104-
description = "존재하지 않는 책 (ISBN 오류)",
104+
description = "존재하지 않는 책 (ISBN13 오류)",
105105
content = [Content(schema = Schema(implementation = ErrorResponse::class))]
106106
)
107107
]

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

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
package org.yapp.apis.book.dto.request
22

33
import io.swagger.v3.oas.annotations.media.Schema
4-
import jakarta.validation.constraints.Max
5-
import jakarta.validation.constraints.Min
6-
import jakarta.validation.constraints.NotBlank
7-
import jakarta.validation.constraints.Size
4+
import jakarta.validation.constraints.*
85
import org.yapp.apis.book.dto.response.BookDetailResponse
6+
import org.yapp.globalutils.util.RegexUtils
97

108
@Schema(
119
title = "책 생성 요청",
1210
description = "시스템에 새로운 책 정보를 생성하는 요청 (주로 내부 API에서 사용)"
1311
)
1412
data class BookCreateRequest private constructor(
15-
@field:NotBlank(message = "ISBN은 필수입니다.")
13+
@field:NotBlank(message = "ISBN13은 필수입니다.")
14+
@field:Pattern(
15+
regexp = RegexUtils.ISBN13_PATTERN,
16+
message = "유효한 13자리 ISBN13 형식이 아닙니다."
17+
)
1618
@Schema(
17-
description = "책의 13자리 ISBN 코드",
19+
description = "책의 13자리 ISBN13 코드",
1820
example = "9788932473901",
1921
required = true,
2022
minLength = 13,
2123
maxLength = 13
2224
)
23-
val isbn: String? = null,
25+
val isbn13: String? = null,
2426

2527
@field:NotBlank(message = "제목은 필수입니다.")
2628
@field:Size(max = 500, message = "제목은 500자 이내여야 합니다.")
@@ -81,26 +83,25 @@ data class BookCreateRequest private constructor(
8183
)
8284
val description: String? = null
8385
) {
84-
fun validIsbn(): String = isbn!!
86+
fun validIsbn13(): String = isbn13!!
8587
fun validTitle(): String = title!!
8688
fun validAuthor(): String = author!!
8789
fun validPublisher(): String = publisher!!
8890
fun validCoverImageUrl(): String = coverImageUrl!!
8991

9092
companion object {
91-
92-
fun from(bookDetail: BookDetailResponse): BookCreateRequest {
93-
val finalIsbn = bookDetail.isbn13
94-
?: throw IllegalArgumentException("ISBN이 존재하지 않습니다.")
93+
fun from(bookDetailResponse: BookDetailResponse): BookCreateRequest {
94+
val finalIsbn13 = bookDetailResponse.isbn13
95+
?: throw IllegalArgumentException("ISBN13이 존재하지 않습니다.")
9596

9697
return BookCreateRequest(
97-
isbn = finalIsbn,
98-
title = bookDetail.title,
99-
author = bookDetail.author,
100-
publisher = bookDetail.publisher,
101-
publicationYear = parsePublicationYear(bookDetail.pubDate),
102-
coverImageUrl = bookDetail.coverImageUrl,
103-
description = bookDetail.description,
98+
isbn13 = finalIsbn13,
99+
title = bookDetailResponse.title,
100+
author = bookDetailResponse.author,
101+
publisher = bookDetailResponse.publisher,
102+
publicationYear = parsePublicationYear(bookDetailResponse.pubDate),
103+
coverImageUrl = bookDetailResponse.coverImageUrl,
104+
description = bookDetailResponse.description,
104105
)
105106
}
106107

apis/src/main/kotlin/org/yapp/apis/book/dto/request/BookDetailRequest.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,31 @@ import org.yapp.globalutils.util.RegexUtils
77

88
@Schema(
99
title = "책 상세 정보 요청",
10-
description = "특정 ISBN을 통한 책 상세 정보 조회 요청"
10+
description = "특정 ISBN13을 통한 책 상세 정보 조회 요청"
1111
)
1212
data class BookDetailRequest private constructor(
13-
@field:NotBlank(message = "ISBN 비어 있을 수 없습니다.")
13+
@field:NotBlank(message = "ISBN13 비어 있을 수 없습니다.")
1414
@field:Pattern(
1515
regexp = RegexUtils.ISBN13_PATTERN,
16-
message = "유효한 13자리 ISBN 형식이 아닙니다."
16+
message = "유효한 13자리 ISBN13 형식이 아닙니다."
1717
)
1818
@Schema(
1919
description = "조회할 책의 13자리 ISBN 코드",
2020
example = "9788932473901",
2121
required = true,
22-
pattern = "\\d{13}",
2322
minLength = 13,
2423
maxLength = 13
2524
)
26-
val isbn: String? = null,
25+
val isbn13: String? = null,
2726
) {
28-
fun validIsbn(): String = isbn!!
27+
fun validIsbn13(): String = isbn13!!
2928

3029
companion object {
3130
fun from(
32-
isbn: String?,
31+
isbn13: String,
3332
): BookDetailRequest {
3433
return BookDetailRequest(
35-
isbn = isbn,
34+
isbn13 = isbn13,
3635
)
3736
}
3837
}

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package org.yapp.apis.book.dto.request
33
import io.swagger.v3.oas.annotations.media.Schema
44
import jakarta.validation.constraints.NotBlank
55
import jakarta.validation.constraints.NotNull
6+
import jakarta.validation.constraints.Pattern
67
import jakarta.validation.constraints.Size
78
import org.yapp.apis.book.dto.response.BookCreateResponse
89
import org.yapp.domain.userbook.BookStatus
10+
import org.yapp.globalutils.util.RegexUtils
911
import java.util.UUID
1012

1113
@Schema(
@@ -31,15 +33,19 @@ data class UpsertUserBookRequest private constructor(
3133
)
3234
val bookId: UUID? = null,
3335

34-
@field:NotBlank(message = "책 ISBN은 필수입니다.")
36+
@field:NotBlank(message = "책 ISBN13은 필수입니다.")
37+
@field:Pattern(
38+
regexp = RegexUtils.ISBN13_PATTERN,
39+
message = "유효한 13자리 ISBN13 형식이 아닙니다."
40+
)
3541
@Schema(
36-
description = "책의 13자리 ISBN 코드",
42+
description = "책의 13자리 ISBN13 코드",
3743
example = "9788932473901",
3844
required = true,
3945
minLength = 13,
4046
maxLength = 13
4147
)
42-
val bookIsbn: String? = null,
48+
val isbn13: String? = null,
4349

4450
@field:NotBlank(message = "책 제목은 필수입니다.")
4551
@field:Size(max = 500, message = "책 제목은 500자 이내여야 합니다.")
@@ -94,7 +100,7 @@ data class UpsertUserBookRequest private constructor(
94100
) {
95101
fun validUserId(): UUID = userId!!
96102
fun validBookId(): UUID = bookId!!
97-
fun validBookIsbn(): String = bookIsbn!!
103+
fun validBookIsbn13(): String = isbn13!!
98104
fun validBookTitle(): String = bookTitle!!
99105
fun validBookAuthor(): String = bookAuthor!!
100106
fun validBookPublisher(): String = bookPublisher!!
@@ -110,7 +116,7 @@ data class UpsertUserBookRequest private constructor(
110116
return UpsertUserBookRequest(
111117
userId = userId,
112118
bookId = bookCreateResponse.bookId,
113-
bookIsbn = bookCreateResponse.isbn,
119+
isbn13 = bookCreateResponse.isbn13,
114120
bookTitle = bookCreateResponse.title,
115121
bookAuthor = bookCreateResponse.author,
116122
bookPublisher = bookCreateResponse.publisher,

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,28 @@ package org.yapp.apis.book.dto.request
33
import io.swagger.v3.oas.annotations.media.Schema
44
import jakarta.validation.constraints.NotBlank
55
import jakarta.validation.constraints.NotNull
6+
import jakarta.validation.constraints.Pattern
67
import org.yapp.domain.userbook.BookStatus
8+
import org.yapp.globalutils.util.RegexUtils
79

810
@Schema(
911
title = "사용자 도서 등록 요청",
1012
description = "사용자의 서재에 도서를 등록하거나 상태를 변경하는 요청"
1113
)
1214
data class UserBookRegisterRequest private constructor(
13-
@field:NotBlank(message = "ISBN은 필수입니다.")
15+
@field:NotBlank(message = "ISBN13은 비어 있을 수 없습니다.")
16+
@field:Pattern(
17+
regexp = RegexUtils.ISBN13_PATTERN,
18+
message = "유효한 13자리 ISBN13 형식이 아닙니다."
19+
)
1420
@Schema(
15-
description = "등록할 책의 13자리 ISBN 코드",
21+
description = "등록할 책의 13자리 ISBN13 코드",
1622
example = "9788932473901",
1723
required = true,
1824
minLength = 13,
1925
maxLength = 13
2026
)
21-
val bookIsbn: String? = null,
27+
val isbn13: String? = null,
2228

2329
@field:NotNull(message = "도서 상태는 필수입니다.")
2430
@Schema(
@@ -30,6 +36,6 @@ data class UserBookRegisterRequest private constructor(
3036
)
3137
val bookStatus: BookStatus? = null
3238
) {
33-
fun validBookIsbn(): String = bookIsbn!!
39+
fun validIsbn13(): String = isbn13!!
3440
fun validBookStatus(): BookStatus = bookStatus!!
3541
}

apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBooksByIsbnsRequest.kt renamed to apis/src/main/kotlin/org/yapp/apis/book/dto/request/UserBooksByIsbn13sRequest.kt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import jakarta.validation.constraints.NotNull
66
import java.util.UUID
77

88
@Schema(
9-
name = "UserBooksByIsbnsRequest",
10-
description = "Request DTO for finding user books by user ID and a list of ISBNs"
9+
name = "UserBooksByIsbn13sRequest",
10+
description = "Request DTO for finding user books by user ID and a list of ISBN13s"
1111
)
12-
data class UserBooksByIsbnsRequest private constructor(
12+
data class UserBooksByIsbn13sRequest private constructor(
1313
@Schema(
1414
description = "사용자 ID",
1515
example = "1"
@@ -21,16 +21,16 @@ data class UserBooksByIsbnsRequest private constructor(
2121
description = "도서 ISBN 목록",
2222
example = "[\"9788966262429\", \"9791190412351\"]"
2323
)
24-
@field:NotEmpty(message = "isbns는 비어있을 수 없습니다.")
25-
val isbns: List<String>? = null
24+
@field:NotEmpty(message = "isbn13 리스트는 비어있을 수 없습니다.")
25+
val isbn13s: List<String>? = null
2626

2727
) {
2828
fun validUserId(): UUID = userId!!
29-
fun validIsbns(): List<String> = isbns!!
29+
fun validIsbn13s(): List<String> = isbn13s!!
3030

3131
companion object {
32-
fun of(userId: UUID, isbns: List<String>): UserBooksByIsbnsRequest {
33-
return UserBooksByIsbnsRequest(userId = userId, isbns = isbns)
32+
fun of(userId: UUID, isbn13s: List<String>): UserBooksByIsbn13sRequest {
33+
return UserBooksByIsbn13sRequest(userId = userId, isbn13s = isbn13s)
3434
}
3535
}
3636
}

apis/src/main/kotlin/org/yapp/apis/book/dto/response/BookCreateResponse.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import java.util.UUID
66

77
data class BookCreateResponse private constructor(
88
val bookId: UUID,
9-
val isbn: String,
9+
val isbn13: String,
1010
val title: String,
1111
val author: String,
1212
val publisher: String,
@@ -16,7 +16,7 @@ data class BookCreateResponse private constructor(
1616
fun from(bookVO: BookInfoVO): BookCreateResponse {
1717
return BookCreateResponse(
1818
bookId = bookVO.id.value,
19-
isbn = bookVO.isbn.value,
19+
isbn13 = bookVO.isbn13.value,
2020
title = bookVO.title,
2121
author = bookVO.author,
2222
publisher = bookVO.publisher,

apis/src/main/kotlin/org/yapp/apis/book/dto/response/UserBookResponse.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import java.util.UUID
99
data class UserBookResponse private constructor(
1010
val userBookId: UUID,
1111
val userId: UUID,
12-
val bookIsbn: String,
12+
val isbn13: String,
1313
val bookTitle: String,
1414
val bookAuthor: String,
1515
val status: BookStatus,
@@ -26,7 +26,7 @@ data class UserBookResponse private constructor(
2626
return UserBookResponse(
2727
userBookId = userBook.id.value,
2828
userId = userBook.userId.value,
29-
bookIsbn = userBook.bookIsbn.value,
29+
isbn13 = userBook.bookIsbn13.value,
3030
bookTitle = userBook.title,
3131
bookAuthor = BookDataValidator.removeParenthesesFromAuthor(userBook.author),
3232
status = userBook.status,

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

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import org.yapp.infra.external.aladin.AladinApi
1616
import org.yapp.infra.external.aladin.request.AladinBookLookupRequest
1717
import org.yapp.infra.external.aladin.request.AladinBookSearchRequest
1818
import org.yapp.infra.external.aladin.response.AladinBookDetailResponse
19+
import org.yapp.infra.external.aladin.response.AladinSearchItem
1920
import org.yapp.infra.external.aladin.response.AladinSearchResponse
2021

2122
@Service
@@ -46,12 +47,7 @@ class AladinBookQueryService(
4647
}
4748

4849
val filteredItems = response.item.filter { item ->
49-
val isbn13 = item.isbn13?.takeIf { it.isNotBlank() }
50-
?: item.isbn?.let { IsbnConverter.toIsbn13(it) }
51-
52-
isbn13?.let {
53-
IsbnValidator.isValidIsbn(it) && !it.startsWith("K", ignoreCase = true)
54-
} ?: false
50+
getValidAndFilteredIsbn13(item) != null
5551
}
5652

5753
val filteredResponse = AladinSearchResponse(
@@ -73,15 +69,28 @@ class AladinBookQueryService(
7369

7470
override fun getBookDetail(@Valid request: BookDetailRequest): BookDetailResponse {
7571
log.info("Service - Converting BookDetailRequest to AladinBookLookupRequest and calling Aladin API for book detail lookup.")
76-
val aladinLookupRequest = AladinBookLookupRequest.from(request.validIsbn())
72+
val aladinLookupRequest = AladinBookLookupRequest.from(request.validIsbn13())
7773
val response: AladinBookDetailResponse = aladinApi.lookupBook(aladinLookupRequest)
7874
.onSuccess { response ->
79-
log.info("Aladin lookup successful for itemId: '${aladinLookupRequest.itemId}', title: ${response.item?.firstOrNull()?.title}")
75+
log.info("Aladin lookup successful for itemId: '${aladinLookupRequest.itemId}', title: ${response.item.firstOrNull()?.title}")
8076
}
8177
.getOrElse { exception ->
8278
log.error("Failed to call Aladin lookup API for request: '$request'", exception)
8379
throw BookException(BookErrorCode.ALADIN_API_LOOKUP_FAILED, exception.message)
8480
}
8581
return BookDetailResponse.from(response)
8682
}
83+
84+
85+
private fun getValidAndFilteredIsbn13(item: AladinSearchItem): String? {
86+
val primaryIsbn13 = item.isbn13
87+
?.takeIf { it.isNotBlank() && IsbnValidator.isValidIsbn13(it) }
88+
89+
val convertedIsbn13 = item.isbn
90+
?.takeIf { it.isNotBlank() && IsbnValidator.isValidIsbn10(it) }
91+
?.let { validIsbn10 -> IsbnConverter.toIsbn13(validIsbn10) }
92+
?.takeIf { IsbnValidator.isValidIsbn13(it) }
93+
94+
return primaryIsbn13 ?: convertedIsbn13
95+
}
8796
}

apis/src/main/kotlin/org/yapp/apis/book/service/BookManagementService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class BookManagementService(
1414
) {
1515
fun findOrCreateBook(@Valid request: BookCreateRequest): BookCreateResponse {
1616
val bookInfoVO = bookDomainService.findOrCreate(
17-
request.validIsbn(),
17+
request.validIsbn13(),
1818
request.validTitle(),
1919
request.validAuthor(),
2020
request.validPublisher(),

0 commit comments

Comments
 (0)