Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ jobs:
- name: Create application-oauth.yml
run: |
echo "${{ secrets.APPLICATION_OAUTH_YML }}" > src/main/resources/application-oauth.yml


# 3-3. application-book.yml 생성
- name: Create application-book.yml
run: |
echo "${{ secrets.APPLICATION_BOOK_YML }}" > src/main/resources/application-book.yml

# 4. Gradle 실행 권한 부여
- name: Grant execute permission for gradlew
run: chmod +x gradlew
Expand Down
7 changes: 5 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ dependencies {
//implementation 'com.mysql:mysql-connector-j:9.1.0'

// H2
runtimeOnly 'com.h2database:h2'
//runtimeOnly 'com.h2database:h2'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
// MariaDB
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.moongeul.backend.api.book.controller;

import com.moongeul.backend.api.book.dto.BookSearchRequestDTO;
import com.moongeul.backend.api.book.dto.BookSearchResponseDTO;
import com.moongeul.backend.api.book.service.BookService;
import com.moongeul.backend.common.response.ApiResponse;
import com.moongeul.backend.common.response.SuccessStatus;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Book", description = "Book(도서) 관련 API 입니다.")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/book")
@Validated
public class BookController {

private final BookService bookService;

@Operation(
summary = "도서 검색 API",
description = "네이버 도서 API를 활용하여 책 제목으로 도서를 검색합니다. 검색 결과는 DB에 저장되며, 이미 저장된 도서는 최신 정보로 업데이트됩니다. (페이지와 사이즈는 1부터 시작합니다)"
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "도서 검색 성공"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청 파라미터"),
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "500", description = "서버 오류 발생")
})
@GetMapping("/search")
public ResponseEntity<ApiResponse<BookSearchResponseDTO>> searchBooks(
@RequestParam @NotBlank(message = "검색어는 필수입니다.") String query,
@RequestParam(required = false, defaultValue = "1") @Min(value = 1, message = "페이지는 1 이상이어야 합니다.") Integer page,
@RequestParam(required = false, defaultValue = "10") @Min(value = 1, message = "한 페이지당 개수는 1 이상이어야 합니다.") Integer size) {

BookSearchRequestDTO bookSearchRequestDTO = BookSearchRequestDTO.builder()
.query(query)
.page(page)
.size(size)
.build();

BookSearchResponseDTO bookSearchResponseDTO = bookService.searchBooks(bookSearchRequestDTO);
return ApiResponse.success(SuccessStatus.SEARCH_BOOK_SUCCESS, bookSearchResponseDTO);
}
}

23 changes: 23 additions & 0 deletions src/main/java/com/moongeul/backend/api/book/dto/BookDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.moongeul.backend.api.book.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BookDTO {
private String isbn; // ISBN
private String title; // 책 제목
private String author; // 저자
private String bookImage; // 표지 이미지
private String publisher; // 출판사
private String description; // 책 소개
private String pubdate; // 출판연도
private Double ratingAverage; // 별점 평균
private Integer ratingCount; // 별점 개수
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.moongeul.backend.api.book.dto;

import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BookSearchRequestDTO {
@NotBlank(message = "검색어는 필수입니다.")
private String query; // 검색어

@Min(value = 1, message = "페이지는 1 이상이어야 합니다.")
private Integer page = 1; // 페이지 번호 (기본값 1)

@Min(value = 1, message = "한 페이지당 개수는 1 이상이어야 합니다.")
private Integer size = 10; // 한 페이지당 개수 (기본값 10, 최대 100)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.moongeul.backend.api.book.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class BookSearchResponseDTO {
private Integer total; // 전체 검색 결과 수
private Integer page; // 현재 페이지
private Integer size; // 페이지당 개수
private Integer totalPages; // 전체 페이지 수
private Boolean isLast; // 마지막 페이지 여부
private List<BookDTO> books; // 책 목록
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.moongeul.backend.api.book.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NaverBookItemDTO {
@JsonProperty("title")
private String title; // 책 제목

@JsonProperty("link")
private String link; // 네이버 도서 정보 URL

@JsonProperty("image")
private String image; // 표지 이미지 URL

@JsonProperty("author")
private String author; // 저자

@JsonProperty("discount")
private String discount; // 할인가격

@JsonProperty("publisher")
private String publisher; // 출판사

@JsonProperty("pubdate")
private String pubdate; // 출판연도

@JsonProperty("isbn")
private String isbn; // ISBN

@JsonProperty("description")
private String description; // 책 소개
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.moongeul.backend.api.book.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class NaverBookSearchResponseDTO {
@JsonProperty("lastBuildDate")
private String lastBuildDate;

@JsonProperty("total")
private Integer total;

@JsonProperty("start")
private Integer start;

@JsonProperty("display")
private Integer display;

@JsonProperty("items")
private List<NaverBookItemDTO> items;
}

43 changes: 43 additions & 0 deletions src/main/java/com/moongeul/backend/api/book/entity/Book.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.moongeul.backend.api.book.entity;

import com.moongeul.backend.common.entity.BaseTimeEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "book")
public class Book extends BaseTimeEntity {

@Id
private String isbn; // ISBN (PK)

private String title; // 책 제목
private String author; // 저자
private String bookImage; // 표지
private String publisher; // 출판사

@Column(columnDefinition = "TEXT")
private String description; // 책 소개
private String pubdate; // 출판연도
private Double ratingAverage; // 별점 평균
private Integer ratingCount; // 별점 개수

// 책 정보 업데이트
public void update(String title, String author, String bookImage, String publisher,
String description, String pubdate) {
this.title = title;
this.author = author;
this.bookImage = bookImage;
this.publisher = publisher;
this.description = description;
this.pubdate = pubdate;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.moongeul.backend.api.book.repository;

import com.moongeul.backend.api.book.entity.Book;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;
import java.util.Optional;

public interface BookRepository extends JpaRepository<Book, String> {
Optional<Book> findByIsbn(String isbn);

List<Book> findByIsbnIn(List<String> isbns);
}

Loading