-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[BOOK-14]-네이버 Open API 연동 및 도서 검색 API 구현 (#10)
* [BOOK-14]-chore: 네이버 오픈 API 관련 설정 * [BOOK-14]-feat: 도서 엔티티 생성 * [BOOK-14]-feat: Open API 이용한 도서 검색 API * [BOOK-14]-feat: 유저 도서 간 다대다 매핑 * [BOOK-14]-refactor: API 호출 권한 검증 시 토큰 인증 필터 추가
- Loading branch information
Showing
13 changed files
with
339 additions
and
6 deletions.
There are no files selected for viewing
This file contains 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 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
74 changes: 74 additions & 0 deletions
74
src/main/java/goorm/unit/booklog/domain/book/application/BookService.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package goorm.unit.booklog.domain.book.application; | ||
|
||
import java.net.URI; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import org.json.JSONArray; | ||
import org.json.JSONObject; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.data.domain.PageRequest; | ||
import org.springframework.http.RequestEntity; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.web.client.RestTemplate; | ||
import org.springframework.web.util.UriComponentsBuilder; | ||
|
||
import goorm.unit.booklog.common.response.PageableResponse; | ||
import goorm.unit.booklog.domain.book.domain.BookRepository; | ||
import goorm.unit.booklog.domain.book.presentation.response.BookPageResponse; | ||
import goorm.unit.booklog.domain.book.presentation.response.BookResponse; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class BookService { | ||
private final BookRepository bookRepository; | ||
|
||
@Value("${naver.api.clientId}") | ||
private String clientId; | ||
|
||
@Value("${naver.api.clientSecret}") | ||
private String clientSecret; | ||
|
||
public BookPageResponse searchBooks(int page, int size, String keyword) { | ||
URI uri = UriComponentsBuilder | ||
.fromUriString("https://openapi.naver.com") | ||
.path("/v1/search/book.json") | ||
.queryParam("query", keyword) | ||
.queryParam("display", size) | ||
.queryParam("start", page) | ||
.queryParam("sort", "sim") | ||
.encode() | ||
.build() | ||
.toUri(); | ||
|
||
RequestEntity<Void> req = RequestEntity | ||
.get(uri) | ||
.header("X-Naver-Client-Id", clientId) | ||
.header("X-Naver-Client-Secret", clientSecret) | ||
.build(); | ||
|
||
RestTemplate restTemplate = new RestTemplate(); | ||
String response = restTemplate.exchange(req, String.class).getBody(); | ||
|
||
JSONObject jsonResponse = new JSONObject(response); | ||
JSONArray items = jsonResponse.getJSONArray("items"); | ||
|
||
List<BookResponse> bookResponses = new ArrayList<>(); | ||
for (int i = 0; i < items.length(); i++) { | ||
JSONObject item = items.getJSONObject(i); | ||
BookResponse bookResponse = BookResponse.of( | ||
(long)(i + 1), | ||
item.getString("title"), | ||
item.getString("author"), | ||
item.getString("description"), | ||
item.getString("link") | ||
); | ||
bookResponses.add(bookResponse); | ||
} | ||
|
||
int total = jsonResponse.getInt("total"); | ||
return BookPageResponse.of(bookResponses, PageableResponse.of(PageRequest.of(page, size), (long)total)); | ||
|
||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/main/java/goorm/unit/booklog/domain/book/domain/Book.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package goorm.unit.booklog.domain.book.domain; | ||
|
||
import static jakarta.persistence.GenerationType.IDENTITY; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import goorm.unit.booklog.common.domain.BaseTimeEntity; | ||
import goorm.unit.booklog.domain.file.domain.File; | ||
import goorm.unit.booklog.domain.user.domain.User; | ||
import jakarta.persistence.Column; | ||
import jakarta.persistence.Entity; | ||
import jakarta.persistence.GeneratedValue; | ||
import jakarta.persistence.Id; | ||
import jakarta.persistence.JoinColumn; | ||
import jakarta.persistence.ManyToMany; | ||
import jakarta.persistence.OneToOne; | ||
import lombok.AccessLevel; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@Entity | ||
@Builder | ||
@AllArgsConstructor | ||
@NoArgsConstructor(access= AccessLevel.PROTECTED) | ||
public class Book extends BaseTimeEntity { | ||
|
||
@Id | ||
@GeneratedValue(strategy = IDENTITY) | ||
private Long id; | ||
|
||
@Column(nullable = false) | ||
private String title; | ||
|
||
@Column(nullable = false) | ||
private String author; | ||
|
||
@Column(nullable = false, length = 1000) | ||
private String description; | ||
|
||
@OneToOne | ||
@JoinColumn(name = "file_id") | ||
private File file; | ||
|
||
@ManyToMany(mappedBy = "books") | ||
private List<User> users = new ArrayList<>(); | ||
|
||
public static Book create(Long id, String title, String author, String description, File file) { | ||
return Book.builder() | ||
.id(id) | ||
.title(title) | ||
.author(author) | ||
.description(description) | ||
.file(file) | ||
.build(); | ||
} | ||
} |
4 changes: 4 additions & 0 deletions
4
src/main/java/goorm/unit/booklog/domain/book/domain/BookRepository.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package goorm.unit.booklog.domain.book.domain; | ||
|
||
public interface BookRepository { | ||
} |
13 changes: 13 additions & 0 deletions
13
src/main/java/goorm/unit/booklog/domain/book/infrastructure/BookRepositoryImpl.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package goorm.unit.booklog.domain.book.infrastructure; | ||
|
||
import org.springframework.stereotype.Repository; | ||
|
||
import goorm.unit.booklog.domain.book.domain.BookRepository; | ||
import lombok.RequiredArgsConstructor; | ||
|
||
@Repository | ||
@RequiredArgsConstructor | ||
public class BookRepositoryImpl implements BookRepository { | ||
private final JpaBookRepository jpaBookRepository; | ||
|
||
} |
8 changes: 8 additions & 0 deletions
8
src/main/java/goorm/unit/booklog/domain/book/infrastructure/JpaBookRepository.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package goorm.unit.booklog.domain.book.infrastructure; | ||
|
||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import goorm.unit.booklog.domain.book.domain.Book; | ||
|
||
public interface JpaBookRepository extends JpaRepository<Book, String> { | ||
} |
45 changes: 45 additions & 0 deletions
45
src/main/java/goorm/unit/booklog/domain/book/presentation/BookController.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package goorm.unit.booklog.domain.book.presentation; | ||
|
||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RequestParam; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import goorm.unit.booklog.domain.book.application.BookService; | ||
import goorm.unit.booklog.domain.book.presentation.response.BookPageResponse; | ||
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.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 lombok.RequiredArgsConstructor; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/v1/books") | ||
@Tag(name = "Book", description = "도서 관련 api / 담당자 : 이한음") | ||
public class BookController { | ||
private final BookService bookService; | ||
|
||
@Operation(summary = "도서 검색", description = "네이버 도서 검색 Open API에서 키워드 기반으로 도서를 검색 합니다.") | ||
@ApiResponses({ | ||
@ApiResponse( | ||
responseCode = "200", | ||
description = "도서 검색 성공", | ||
content = @Content(schema = @Schema(implementation = BookPageResponse.class)) | ||
|
||
) | ||
}) | ||
@GetMapping("/search") | ||
public ResponseEntity<BookPageResponse> searchBooks( | ||
@Parameter(description = "페이지 인덱스", example = "1", required = true) @RequestParam(value = "page", defaultValue = "1") int page, | ||
@Parameter(description = "응답 개수", example = "10", required = true) @RequestParam(value = "size", defaultValue = "10") int size, | ||
@Parameter(description = "키워드", example = "스프링", required = true) @RequestParam(value = "keyword") String keyword | ||
) { | ||
BookPageResponse response = bookService.searchBooks(page, size, keyword); | ||
return ResponseEntity.ok(response); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
src/main/java/goorm/unit/booklog/domain/book/presentation/response/BookPageResponse.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package goorm.unit.booklog.domain.book.presentation.response; | ||
|
||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; | ||
|
||
import java.util.List; | ||
|
||
import goorm.unit.booklog.common.response.PageableResponse; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.Builder; | ||
|
||
@Builder | ||
public record BookPageResponse<T>( | ||
@Schema( | ||
description = "도서 목록", | ||
example = "[{\"id\": 1, \"title\": \"스프링 부트와 AWS로 혼자 구현하는 웹 서비스\", \"author\": \"이한음\", \"description\": \"스프링 부트와 AWS로 혼자 구현하는 웹 서비스\", \"file\": {\"id\": 1, \"logicalName\": \"example.jpg\", \"physicalPath\": \"https://example-bucket.ncp.com/files/example.jpg\"}}]", | ||
requiredMode = REQUIRED | ||
) | ||
List<BookResponse> contents, | ||
|
||
@Schema(description = "페이징 정보", requiredMode = REQUIRED) | ||
PageableResponse<T> pageable | ||
) { | ||
public static <T> BookPageResponse<T> of(List<BookResponse> books, PageableResponse<T> pageableResponse) { | ||
return BookPageResponse.<T>builder() | ||
.contents(books) | ||
.pageable(pageableResponse) | ||
.build(); | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
src/main/java/goorm/unit/booklog/domain/book/presentation/response/BookResponse.java
This file contains 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 |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package goorm.unit.booklog.domain.book.presentation.response; | ||
|
||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED; | ||
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED; | ||
|
||
import goorm.unit.booklog.domain.book.domain.Book; | ||
import goorm.unit.booklog.domain.file.presentation.response.FileResponse; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import lombok.Builder; | ||
|
||
@Builder | ||
public record BookResponse( | ||
@Schema(description = "도서 ID", example = "1", requiredMode = REQUIRED) | ||
Long id, | ||
|
||
@Schema(description = "도서 제목", example = "스프링 부트와 AWS로 혼자 구현하는 웹 서비스", requiredMode = REQUIRED) | ||
String title, | ||
|
||
@Schema(description = "도서 저자", example = "이한음", requiredMode = NOT_REQUIRED) | ||
String author, | ||
|
||
@Schema(description = "도서 설명", example = "스프링 부트와 AWS로 혼자 구현하는 웹 서비스", requiredMode = NOT_REQUIRED) | ||
String description, | ||
|
||
@Schema( | ||
description = "파일 정보", | ||
example = "{\"id\": 1, \"logicalName\": \"example.jpg\", \"physicalPath\": \"https://example-bucket.ncp.com/files/example.jpg\"}", | ||
requiredMode = NOT_REQUIRED) | ||
FileResponse file | ||
) { | ||
public static BookResponse from(Book book) { | ||
return BookResponse.builder() | ||
.id(book.getId()) | ||
.title(book.getTitle()) | ||
.author(book.getAuthor()) | ||
.description(book.getDescription()) | ||
.file(FileResponse.from(book.getFile())) | ||
.build(); | ||
} | ||
|
||
public static BookResponse of (Long id, String title, String author, String description, String physicalPath) { | ||
return BookResponse.builder() | ||
.id(id) | ||
.title(title) | ||
.author(author) | ||
.description(description) | ||
.file(FileResponse.of(title, physicalPath)) | ||
.build(); | ||
} | ||
} |
This file contains 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
Oops, something went wrong.