Core 프로젝트에서 도서 API를 사용하기 전에 미리 사용하는 방법을 익혀보려고 간단한 도서 검색 web page를 만들어보기로 했다.
우선 API 사용을 위해 네이버 개발자 페이지에서 애플리케이션 등록을 해주자.
🧷 NAVER Developers - 애플리케이션 등록 [link]
등록 후, API 사용 Key를 받았으니 우선 Postman을 사용해서 데이터가 잘 받아와지는지 확인해보자.
🧷 NAVER 검색>도서 API 사용 매뉴얼 을 참고하여 진행해보자.
HTTP 메서드는 GET + 위에 나와있는 요청 URL을 넣고(필자는 JSON으로 반환받을 것이다),
참고 사항에 나와있는대로 Header
영역에 클라이언트 아이디와 클라이언트 시크릿을 추가한다.
그리고 Params
영역에 위에 나와있는 파라미터를 토대로 원하는 검색 결과를 설정한다. (일단 query(검색어) 만 사용해 검색해보자)
요청을 보내니 Body에 검색한 도서에 대한 정보가 성공적(200)으로 돌아오는 것을 확인할 수 있다.
IDE : intelliJ
Framework : Spring Boot 3.3.2
Java version : JAVA 17
build.gradle > dependencies
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-mustache'
//lombok add!
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
본격적으로 Spring Boot에서 API를 사용하는 방법을 실습해보자!
간단하게, 검색어 입력받고 그에 대한 책 목록을 받아와 출력하는 web을 만들어 볼 것이다.
주요하게 만들어야 할 건 딱 세가지 이다.
일단 controller의 대략적인 틀을 만들자. 검색어를 받고 출력하는 것 모두 Get 방식으로 진행했다. (처음에 Post와 Get을 나눴다가 굳이? 라는 생각이 들었따. 정말 굳이 였따 ㅎ)
@Controller
public class BookController {
@GetMapping("/book/search")
public String search() {
return "search";
}
@GetMapping("/book/result")
public String result() {
result "result";
}
VO 작성 시, 위의 응답 데이터를 참고하여 작성해야 한다.
<빨간 박스>는 ResultVO.java
에, <파란 박스>는 BookVO.java
에 작성하자.
ResultVO.java
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Setter
@Getter
public class ResultVO {
private String lastBuildDate;
private int total;
private int start;
private int display;
private List<BookVO> items;
}
BookVO.java
@ToString
@AllArgsConstructor
@NoArgsConstructor
@Setter
@Getter
public class BookVO {
private String title;
private String link;
private String image;
private String author;
private String discount;
private String publisher;
private String isbn;
private String description;
private String pubdate;
}
mustache 사용 방법
- File > Settings > Plugins > Handlebars/Mustache 설치
- build.gradle > dependencies 에
implementation 'org.springframework.boot:spring-boot-starter-mustache'
추가
search.mustache
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>book search</title>
</head>
<body>
<h1>도서 검색</h1>
<form action="/book/result" method="get">
<input type="text" placeholder="검색어를 입력하세요" name="bookname" required>
<button type="submit">확인</button>
</form>
</body>
</html>
result.mustache
<!DOCTYPE html>
<html lang="en">
<head>
<title>book list</title>
</head>
<body>
<h2>검색한 도서 목록 : "{{text}}"에 대한 검색어</h2>
<table border="1">
<tr bgcolor="pink">
<td>isbn</td>
<td>도서 이미지</td>
<td>도서제목</td>
<td>저자/출판사</td>
<td>소개</td>
<td>출판일</td>
</tr>
{{#books}}
<tr>
<td>{{isbn}}</td>
<td><img src="{{image}}" alt="{{title}}" width="60%" height="60%"></td>
<td><a href="{{link}}">{{title}}</a></td>
<td>{{author}}/{{publisher}}</td>
<td>{{description}}</td>
<td>{{pubdate}}</td>
</tr>
{{/books}}
</table>
</body>
</html>
검색어 가져오기
public String result(@RequestParam("bookname") String text, Model model) {
log.info("Received text: {}", text);
model.addAttribute("text", text);
@RequestParam("bookname")
: Http 요청 파라미터에서 "bookname"의 값을 가져와 text에 주입한다.클라이언트 아이디/시크릿 값 입력
String clientId = "클라이언트 아이디 값";
String clientSecret = "클라이언트 시크릿 값";
Spring apiURI
URI uri = UriComponentsBuilder
.fromUriString("https://openapi.naver.com")
.path("/v1/search/book.json")
.queryParam("query", text)
.queryParam("display", 10)
.queryParam("start", 1)
.queryParam("sort", "sim")
.encode()
.build()
.toUri();
Spring 요청 제공 클래스
RequestEntity<Void> req = RequestEntity.get(uri)
.header("X-Naver-Client-Id", clientId)
.header("X-Naver-Client-Secret", clientSecret)
.build();
Spring 제공 RestTemplate
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> resp = restTemplate.exchange(req, String.class); //응답 본문 String 타입으로 변환
JSON 파싱 (JSON 문자열을 객체로 만듦, 문서화)
ObjectMapper om = new ObjectMapper();
ResultVO resultVO = null;
try{
resultVO = om.readValue(resp.getBody(), ResultVO.class);
} catch (JsonMappingException e){
e.printStackTrace();
} catch (JsonProcessingException e){
e.printStackTrace();
}
result.mustache 에 값 넘겨주기
List<BookVO> books = resultVO.getItems();
model.addAttribute("books", books);
📌 전체 코드
@GetMapping("/book/result")
public String result(@RequestParam("bookname") String text, Model model) {
log.info("Received text: {}", text);
model.addAttribute("text", text);
//네이버 검색 API 요청 - 클라이언트 아이디/시크릿 값 입력
String clientId = "클라이언트 아이디 값";
String clientSecret = "클라이언트 시크릿 값";
//String apiURL
URI uri = UriComponentsBuilder
.fromUriString("https://openapi.naver.com")
.path("/v1/search/book.json")
.queryParam("query", text)
.queryParam("display", 10)
.queryParam("start", 1)
.queryParam("sort", "sim")
.encode()
.build()
.toUri();
//Spring 요청 제공 클래스
RequestEntity<Void> req = RequestEntity.get(uri)
.header("X-Naver-Client-Id", clientId)
.header("X-Naver-Client-Secret", clientSecret)
.build();
//Spring 제공 RestTemplate
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> resp = restTemplate.exchange(req, String.class); //응답 본문 String 타입으로 변환
//JSON 파싱 (JSON 문자열을 객체로 만듦, 문서화)
ObjectMapper om = new ObjectMapper();
ResultVO resultVO = null;
try{
resultVO = om.readValue(resp.getBody(), ResultVO.class);
} catch (JsonMappingException e){
e.printStackTrace();
} catch (JsonProcessingException e){
e.printStackTrace();
}
//result.mustahce 에 값 넘겨주기
List<BookVO> books = resultVO.getItems();
model.addAttribute("books", books);
return "result";
}
이거 때문에 몇 시간을 삽질했는지 모른다.
개발을 할 때 로그를 보니 result() 메서드가 두 번 호출돼서 API 요청이 두 번 되는 문제가 있었다.
처음에는 내가 Post 요청으로 redirect를 해서 문제가 발생한 건가 엄청난 삽질을 했는데...
알고보니 해결은 간단했다.
html 내에 link 태그 안에 불러오는 주소를 비워두든가 경로를 잘못 설정할 경우 이 문제가 발생한다는 것이다.
<td><img src="#" alt=""></td>
<td><a href="#">도서제목</a></td>
그러니 #
이나 공백으로 비워두지 말고 걍 없애버려라... 흑흑
<RestTemplate
과 RequestEntity
는 Spring Framework에서 제공하는 클래스로, 웹 서비스와의 HTTP 통신을 돕는 역할을 한다, 이 클래스들을 사용하면 웹 서비스를 호출하거나 응답을 처리하는 작업을 간결하게 할 수 있음>
RestTemplate
RequestEntity
👉 즉, RequestEntity 를 사용해 요청 정보를 정의하고, RestTemplate 의 exchange 메서드를 통해 실제 요청을 보낸다. 그 후에 서버로부터의 응답은 ResponseEntity 객체에 담게 된다.
ObjectMapper를 이용하면 JSON을 Java 객체로 변환할 수 있고, 반대로 Java 객체를 JSON 객체로 serialization(직렬화) 할 수 있다.
writeValue()
: Java 객체를 JSON으로 serialization 하기 위해서는 ObjectMapper의 writeValue() 메서드를 이용
readValue()
: JSON 파일을 Java 객체로 deserialization 하기 위해서는 ObjectMapper의 readValue() 메서드를 이용
참고 사이트
https://this-circle-jeong.tistory.com/167 - 네이버 도서 API 활용