이번 글에서는 국립 도서관의 Open API를 활용하여 사서가 추천하는 도서를 가져오고, 웹 페이지에 표시하는 방법을 소개해 볼게요. 부족한 실력 탓에 설명이 미흡합니다... 이해해주세요 :)
API에서 반환데는 데이터를 매핑할 DTO 작성
package com.demo.dto;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@XmlRootElement(name = "item")
public class BookRecommendation {
private String drCodeName;
private String recomtitle;
private String recomauthor;
private String recomcontens;
private String regdate;
private String recomfilepath;
@XmlElement(name = "drCodeName")
public String getDrCodeName() {
return drCodeName;
}
public void setDrCodeName(String drCodeName) {
this.drCodeName = drCodeName;
}
@XmlElement(name = "recomtitle")
public String getRecomtitle() {
return recomtitle;
}
public void setRecomtitle(String recomtitle) {
this.recomtitle = recomtitle;
}
@XmlElement(name = "recomauthor")
public String getRecomauthor() {
return recomauthor;
}
public void setRecomauthor(String recomauthor) {
this.recomauthor = recomauthor;
}
@XmlElement(name = "recomcontens")
public String getRecomcontens() {
return recomcontens;
}
public void setRecomcontens(String recomcontens) {
this.recomcontens = recomcontens;
}
@XmlElement(name = "regdate")
public String getRegdate() {
return regdate;
}
public void setRegdate(String regdate) {
this.regdate = regdate;
}
@XmlElement(name = "recomfilepath")
public String getRecomfilepath() {
return recomfilepath;
}
public void setRecomfilepath(String recomfilepath) {
this.recomfilepath = recomfilepath;
}
}
- 도서 정보의 각 항목을 XML에서 매핑된 자바 개게로 반환.
package com.demo.dto;
import java.util.ArrayList;
import java.util.List;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@XmlRootElement(name = "channel")
public class BookRecommendationResponse {
private int totalCount;
private List<BookRecommendationWrapper> lists = new ArrayList<>();
@XmlElement(name = "totalCount")
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
this.totalCount = totalCount;
}
@XmlElement(name = "list")
public List<BookRecommendationWrapper> getLists() {
return lists;
}
public void setLists(List<BookRecommendationWrapper> lists) {
this.lists = lists;
}
}
package com.demo.dto;
import java.util.ArrayList;
import java.util.List;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@XmlRootElement(name = "list")
public class BookRecommendationWrapper {
private List<BookRecommendation> items = new ArrayList<>();
@XmlElement(name = "item")
public List<BookRecommendation> getItems() {
return items;
}
public void setItems(List<BookRecommendation> items) {
this.items = items;
}
}
package com.demo.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@JsonIgnoreProperties(ignoreUnknown = true) // 알 수 없는 속성을 무시
public class ItemDTO {
// 국립중앙도서관 api용
private String kwd; // 검색어
private String titleInfo; // 제목
private String authorInfo; // 저자
private String imageUrl; // 이미지 URL
private String kdcName1s;
}
Service 클래스 작성
package com.demo.service;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.demo.dto.BookRecommendation;
import com.demo.dto.BookRecommendationResponse;
import com.demo.dto.BookRecommendationWrapper;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
@Service
public class BookRecommendationService {
@Value("${library.api.key}")
private String apiKey;
private final RestTemplate restTemplate;
public BookRecommendationService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public List<BookRecommendation> getBookRecommendations(String start_date, String end_date) throws JAXBException {
String url = String.format("https://nl.go.kr/NL/search/openApi/saseoApi.do?key=%s&start_date=%s&end_date=%s",
apiKey, start_date, end_date);
String response = restTemplate.getForObject(url, String.class);
// 로그 추가
System.out.println("Response: " + response);
JAXBContext jaxbContext = JAXBContext.newInstance(BookRecommendationResponse.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
BookRecommendationResponse bookRecommendationResponse =
(BookRecommendationResponse) unmarshaller.unmarshal(new StringReader(response));
// 모든 리스트의 아이템들을 하나의 리스트로 합치기
List<BookRecommendation> recommendations = new ArrayList<>();
for (BookRecommendationWrapper listWrapper : bookRecommendationResponse.getLists()) {
recommendations.addAll(listWrapper.getItems());
}
// 추천 목록이 비어있는 경우
if (recommendations.isEmpty()) {
return Collections.singletonList(new BookRecommendation()); // 빈 객체를 반환
}
return recommendations;
}
}
Html
<script>
$(document).ready(function() {
var message = /*[[${message}]]*/ '';
var messageType = /*[[${messageType}]]*/ '';
var text = /*[[${text}]]*/ '';
if (message) {
Swal.fire({
title: '알림',
text: message,
icon: messageType || 'info', // 메시지 타입이 없는 경우 기본값으로 'info' 사용
confirmButtonText: '확인'
});
}
$('.month-btn').click(function() {
var start_date = $(this).data('start-date');
var end_date = $(this).data('end-date');
console.log('추천을 가져옵니다:', start_date, end_date); // 디버깅 로그
$.ajax({
url: '/fetch-recommendations',
method: 'GET',
data: { start_date: start_date, end_date: end_date },
success: function(data) {
$('#recommendations-list').empty(); // 이전 데이터 삭제
console.log('Response:', data); // 디버깅 로그
if (data.error) {
Swal.fire({
title: 'Error',
text: '데이터 오류',
icon: 'error',
confirmButtonText: '확인'
});
$('#recommendations-list').html('<li>추천이 없습니다.</li>'); // 추천이 없을 때 표시
} else if (data.message) {
Swal.fire({
title: '알림',
text: data.message,
icon: 'info',
confirmButtonText: '확인'
});
} else {
var recommendations = data.recommendations || []; // 기본값으로 빈 배열 설정
var totalCount = data.totalCount || 0; // totalCount 값 설정
// totalCount를 사용하여 목록 상단에 추가
var listHtml = '<h1>추천 책 : ' + totalCount + '권</h1>'; // 총 추천 책 수
if (Array.isArray(recommendations) && recommendations.length > 0) {
recommendations.forEach(function(recommendation) {
listHtml += '<li class="recommendations-item">' +
'<h2>' + recommendation.recomtitle + '</h2>' +
'<img src="' + recommendation.recomfilepath + '" alt="책 이미지">' +
'<p>' + recommendation.recomauthor.replace(';', '/') + '</p>' +
'<p>' + recommendation.recomcontens + '</p>' +
'<p>날짜: ' + recommendation.regdate + '</p>' +
'</li>';
});
$('#recommendations-list').html(listHtml);
} else {
$('#recommendations-list').html('<li>추천이 없습니다.</li>'); // 추천이 없을 때 표시
}
}
},
error: function(xhr, status, error) {
console.error('Ajax request failed:', error); // 디버깅 로그
}
});
});
});
</script>
</head>
<body>
<!-- 헤더 -->
<th:block th:insert="~{include/header}"></th:block>
<div class="main-container">
<h1>사서 도서 추천</h1>
<div th:if="${error}">
<p th:text="${error}"></p>
</div>
<div class="recommendations-btns">
<button class="month-btn" data-start-date="20240101" data-end-date="20240131">January</button>
<button class="month-btn" data-start-date="20240201" data-end-date="20240229">February</button>
<button class="month-btn" data-start-date="20240301" data-end-date="20240331">March</button>
<button class="month-btn" data-start-date="20240401" data-end-date="20240430">April</button>
<button class="month-btn" data-start-date="20240501" data-end-date="20240531">May</button>
<button class="month-btn" data-start-date="20240601" data-end-date="20240630">June</button>
<button class="month-btn" data-start-date="20240701" data-end-date="20240731">July</button>
<button class="month-btn" data-start-date="20240801" data-end-date="20240831">August</button>
<button class="month-btn" data-start-date="20240901" data-end-date="20240930">September</button>
<button class="month-btn" data-start-date="20241001" data-end-date="20241031">October</button>
<button class="month-btn" data-start-date="20241101" data-end-date="20241130">November</button>
<button class="month-btn" data-start-date="20241201" data-end-date="20241231">December</button>
</div>
<div id="recommendations-container" class="recommendations-container">
<ul id="recommendations-list" class="recommendateions-list">
<!-- 추천 목록이 여기에 동적으로 추가됩니다. -->
</ul>
</div>
</div>
- $('.month-btn').click(function() {...} JavaScript 함수 : <button class="month-btn" 월별 버튼 클릭 시 추천 도서 가져오는 함수.
- data-start-date="20240101" data-end-date="20240131" : 버튼의 날짜 값을 JavaScript에서 사용할 수 있게 data-*속성을 이용.
- var start_date = $(this).data('start-date'); , var end_date = $(this).data('end-date'); :전달받은 날짜 값을 저장.
- $.ajax({... : Ajax를 이용해서 url( url: '/fetch-recommendations' )로 요청, method형태( method: 'GET' ) HTTP GET 요청, data( data: { start_date: start_date, end_date: end_date } ) 요청 시 전송할 데이터.
Controller 작성
// 사서 추천 도서
@GetMapping("/fetch-recommendations")
@ResponseBody
public Map<String, Object> fetchRecommendations(@RequestParam String start_date, @RequestParam String end_date) {
Map<String, Object> response = new HashMap<>();
try {
List<BookRecommendation> recommendations = bookRecoService.getBookRecommendations(start_date, end_date);
// 총 개수를 response에 추가
response.put("totalCount", recommendations.size());
if (recommendations.isEmpty() || (recommendations.size() == 1 && recommendations.get(0).getRecomtitle() == null)) {
response.put("message", "아직 이달의 사서 도서 추천이 없습니다.");
} else {
response.put("recommendations", recommendations);
}
} catch (JAXBException e) {
response.put("error", "Failed to fetch book recommendations.");
}
return response;
}
- List<BookRecommendation> recommendations = bookRecoService.getBookRecommendations(start_date, end_date); : (@RequestParam String start_date, @RequestParam String end_date) 로 전달받은 날짜값으로 Service의 메서드를 사용해서 추천 도서 목록을 List형식으로 recommendations에 저장.
- response.put("totalCount", recommendations.size()); : put()메서드를 이용해서 "totalCount"에 해당하는 값 recommendations.size()을 추가 또는 변경해서 response에 추가.
( totalCount : Map 키 > 총 추천 도서의 개수 / recommendations : 추천 도서 목록을 담고있는 리스트 > List<BookRecommendation>
// success 콜백 내에서 받은 data는 recommendations 배열.
else {
var recommendations = data.recommendations || []; // 기본값으로 빈 배열 설정
var totalCount = data.totalCount || 0; // totalCount 값 설정
// totalCount를 사용하여 목록 상단에 추가
var listHtml = '<h1>추천 책 : ' + totalCount + '권</h1>'; // 총 추천 책 수
if (Array.isArray(recommendations) && recommendations.length > 0) {
recommendations.forEach(function(recommendation) {
listHtml += '<li class="recommendations-item">' +
'<h2>' + recommendation.recomtitle + '</h2>' +
'<img src="' + recommendation.recomfilepath + '" alt="책 이미지">' +
'<p>' + recommendation.recomauthor.replace(';', '/') + '</p>' +
'<p>' + recommendation.recomcontens + '</p>' +
'<p>날짜: ' + recommendation.regdate + '</p>' +
'</li>';
});
$('#recommendations-list').html(listHtml);
} else {
$('#recommendations-list').html('<li>추천이 없습니다.</li>'); // 추천이 없을 때 표시
}
}
- $('#recommendations-list').empty(); : 이전에 선택한 날짜의 추천 도서 데이터 삭제.
- recommendations.forEach(function(recommendation) { : 배열을 순회하면서 각 추천 도서의 정보를 처리.
- listHtml += ... ; : 변수에 추천 도서의 HTML을 추가.
- $('#recommendations-list').html(listHtml); : <ul id="recommendations-list" class="recommendateions-list">를 선택해서 listHtml 로 생성된 HTML을 해당 <ul>요소에 삽입.
'SpringBoot 프로젝트' 카테고리의 다른 글
Spring Boot - 국립 도서관 Open API를 활용한 도서 검색 구현 (0) | 2024.12.10 |
---|---|
Spring Boot - 관리자 페이지 (5) (0) | 2024.12.07 |
Spring Boot - 관리자 페이지 (4) (0) | 2024.12.07 |
Spring Boot - 관리자 페이지 (3) (0) | 2024.11.30 |
Spring Boot - 관리자 페이지 (2) (0) | 2024.11.25 |