티스토리 뷰
📝 개요
Kakao Book Open API를 활용하여 도서를 검색하고, 검색한 도서 데이터를 데이터베이스에 저장하는 기능을 제공하는 어플리케이션이다.
기존에 개발했는 Kakao Map Open API를 이용한 지도 검색에 이어 두번째로 오픈 API를 사용하는 것이기 때문에 기능을 쉽게 구현할 수 있었다.
[JAVA] 특정 위치 주변의 💊 약국 💊 검색하는 어플리케이션 제작
📌 애플리케이션 명 : 특정 위치(키워드) 주변의 지정된 반경 내에서 💊 약국 💊 검색하는 Java 어플리케이션 📌 사용 언어 : JAVA 📌 빌드 : maven 📌 활용 API : 카카오 API - 키워드로 장소 검색하
nebulaisme.tistory.com
그래서 기능을 구현하는 것보다는 어떤 기능을 사용하고, 클래스를 어떤식으로 분리할지 고민을 하였다.
1) DB에 접속하고 CRUD를 처리하는 클래스를 dao 패키지로 분리하여 관리하였다.
무슨 기능이 있는지 확실하게 알 수 있도록 추상 클래스로 정의하여 기능을 명시하였고,
하위 클래스에서 기능을 구체화하도록 설계하였다.
또한 이 애플리케이션에서 DB 관련 기능은 한가지이므로, dao 추상 클래스에서 final로 선언한 팩토리 메소드를 정의하여 시나리오를 작성하는 코드를 구현하였다.
2) JSON을 파싱하기 위해서 GSON 라이브러리를 사용하여 보다 간편하고 쉽게 파싱하였다.
Kakao API 와 HTTP 통신 connect를 담당하는 클래스와 Json 파싱 클래스를 분리하였다.
3) 입출력을 진행하는 곳은 특별히 패키지를 분리하지 않았다.
간단한 기능이기 때문에 과하게 구체화된 클래스 분리는 오히려 헷갈릴 것 같았다. 모든 입출력은 Main 클래스에서 진행된다.
📝 기능 소개
📕 책 제목을 검색하고, 검색 결과를 데이터베이스에 저장하는 어플리케이션 📕
- 이 Java 어플리케이션은 입력에 기반하여 책을 검색할 수 있도록 한다.
- 찾고 싶은 책을 검색하여 카카오 API를 이용해 결과를 불러오고 DB에 저장하는 기능을 한다.
- Kakao OPEN API 중 Daum 검색하기 - 책을 검색하기 기능 API를 활용하였다.
📝 클래스 구조
Main : 입출력을 수행하는 메인 클래스, 처음 실행 장소
model 패키지
- BookDTO : 책 한권의 정보를 담는 객체
- BookListDTO : BookDTO의 모음(리스트)의 정보를 담는 객체
dao 패키지
- BookDAO : BookDTO를 가지고 DB에 접근하기 위해 작업하는 클래스로, 구조만 정의되어 있는 추상 클래스
- BookDAOImpl : BookDAO를 상속받아 구체화한 클래스, 실제 DB 접근 및 CRUD 진행
api 패키지
- JsonToBookListAPI : json 형식으로 받은 문자열을 GSON을 이용하여 BookListDTO 형태로 변환하는 클래스.
- KakaoAPIConnect : 카카오 오픈 API를 이용하여 JSON을 요청받고 BookListDTO를 main 클래스에 전달하는 클래스.
resources - config
- application.properties : API 키 정보 보관.
- db.properties : DB 접속 정보 보관.
📝 DAO의 분리
- 데이터의 CRUD를 처리하기 위해 따로 DAO 패키지를 만들어 클래스를 선언하였다.
- CRUD는 자바 기본 라이브러리인 JDBC를 활용했다.
- 추상 메소드를 선언하여 기능이 무엇이 있는지 명시하였고, 하위 클래스에서 기능을 구현하였다.
- 우리가 할 작업은 검색한 책 리스트를 DB에 저장하고 불러와야하는 작업까지 해야하므로, 상위클래스인 DAO에서 DB 커넥션->테이블 초기화->데이터의 삽입->데이터 검색->DB 디스커넥션 까지 메소드들을 한번에 실행시킬 수 있는 템플릿 메소드를 구현하였다.
✏️ 템플릿 메소드?
추상 클래스에서 final로 정의한 메소드로, 전체의 흐름(시나리오)를 정의한 메소드이다. final로 선언하여 하위클래스에선 재정의할 수 없게 만들며, 디자인 패턴의 일종으로 프레임워크에서 많이 사용하는 기법이다. 구체적인 각 메소드는 하위클래스에 위임한다.
코드 보기 - BookDAO, BookDAOImpl
- 책 리스트를 전부 저장해야하므로, insert 문을 모아놨다가 한번에 실행시키는 addBatch()를 활용했다.
public abstract class BookDAO {
public abstract void getConnection();
public abstract boolean insertBookList(BookListDTO bookListDTO);
public abstract BookListDTO getBookListOrderByTitle();
public abstract boolean deleteTotalBooks();
public abstract void closeConnection();
//템플릿 메소드 (시나리오 메소드)
public final BookListDTO excuteBookDAO(BookListDTO bookListDTO) {
/*
1) DB 연결
2) DB에 있는 책 리스트 전부 삭제
3) 책 리스트 삽입
4) 삽입한 책 리스트들을 제목을 기준으로 오름차순 정렬하여 새로운 책 리스트로 만들어 반환.
5) DB 연결 해제
6) 새로운 책 리스트 반환.
*/
getConnection();
deleteTotalBooks();
insertBookList(bookListDTO);
BookListDTO newBookListDTO = getBookListOrderByTitle();
closeConnection();
return newBookListDTO;
}
}
public class BookDAOImpl extends BookDAO {
private Connection con;
private PreparedStatement ps;
private ResultSet rs;
private String url,username,password;
private static final String CLASS_NAME = "com.mysql.cj.jdbc.Driver";
private static final String PROPERTIES = "config/db.properties";
public BookDAOImpl() {
readDBConfig();
}
private void readDBConfig() {
//DB에 관련된 정보는 따로 파일에 저장한 후, 파일을 불러와 처리.
Properties properties = new Properties();
try(
Reader reader = Resources.getResourceAsReader(PROPERTIES)
) {
properties.load(reader);
url = properties.getProperty("db.url");
username = properties.getProperty("db.username");
password = properties.getProperty("db.password");
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void getConnection() {
try {
Class.forName(CLASS_NAME);
con = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean insertBookList(BookListDTO bookListDTO) {
// 책 리스트 삽입.
String sql = "INSERT INTO " +
"TB_BOOK (title, publisher, authors, price, sale_price, isbn) " +
"VALUES (?, ?, ?, ?, ?, ?)";
try {
int cnt = 0;
ps = con.prepareStatement(sql);
for (int i = 0; i < bookListDTO.getSize(); i++) {
BookDTO book = bookListDTO.getBook(i);
ps.setString(1, book.getTitle());
ps.setString(2, book.getPublisher());
ps.setString(3, book.authorsToString(book.getAuthors()));
ps.setInt(4, book.getPrice());
ps.setInt(5, book.getSale_price());
ps.setString(6, book.getIsbn());
ps.addBatch();
//각 반복문마다 매번 executeUpdate 문을 실행시키면 오버헤드가 날 수있기 때문에 addBatch 처리해줌.
cnt++;
}
ps.executeBatch();
ps.close();
if (cnt > 0) return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public BookListDTO getBookListOrderByTitle() {
// 책 제목을 기준으로 오름차순 정렬하여 책 리스트 불러오기.
String sql = "SELECT * FROM TB_BOOK ORDER BY TITLE";
BookListDTO bookListDTO = new BookListDTO();
try {
ps = con.prepareStatement(sql);
rs = ps.executeQuery(sql);
while (rs.next()) {
BookDTO bookDTO = new BookDTO();
bookDTO.setTitle(rs.getString("title"));
bookDTO.setAuthors(rs.getString("authors").split(", "));
bookDTO.setPublisher(rs.getString("publisher"));
bookDTO.setPrice(rs.getInt("price"));
bookDTO.setIsbn(rs.getString("isbn"));
bookDTO.setSale_price(rs.getInt("sale_price"));
bookListDTO.addBook(bookDTO);
}
rs.close();
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
return bookListDTO;
}
@Override
public boolean deleteTotalBooks() {
// 테이블 안에 있는 모든 책 목록 삭제.
String sql = "DELETE FROM TB_BOOK";
try {
ps = con.prepareStatement(sql);
int cnt = ps.executeUpdate(sql);
if (cnt > 0) return true;
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@Override
public void closeConnection() {
try {
if (con != null) con.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
📝 JSON 파싱 - GSON
- GSON을 활용하여 JSON을 보다 쉽게 파싱했다.
✏️ GSON?
gson은 json구조를 띄는 직렬화된 데이터를 JAVA의 객체로 역직렬화, 직렬화 해주는 자바 라이브러리이다.
즉, JSON Object -> JAVA Object 또는 그 반대의 행위를 돕는 기능을 한다.
- 카카오 API 책에서 가져온 Json 형식은 다음과 같다.
- documents 라는 배열 안에 도서 객체가 담겨있는 구조이다.
documents [
{authors:"", contents:"", title:"", price:""},
{authors:"", contents:"", title:"", price:""},
{authors:"", contents:"", title:"", price:""},
{authors:"", contents:"", title:"", price:""},
...
]
- 그렇기 때문에 GSON을 사용하려면 documents을 담는 리스트 DTO도 필요하다.
documents는 거대한 Book List로 볼 수 있으므로, BookListDTO를 선언하여 @SerializeName("documents") 통해 리스트 변수 안에 값을 담아 파싱했다.
- 코드
public class JsonToBookListAPI {
public BookListDTO parseJsonToBookList(String jsonBody) {
//GSON 을 통해 JSON -> BookListDTO 객체로 반환.
Gson gson = new Gson();
BookListDTO bookListDTO = gson.fromJson(jsonBody, BookListDTO.class);
return bookListDTO;
}
}
BookListDTO
@Data
@AllArgsConstructor
public class BookListDTO {
@SerializedName("documents") // kakao bookList 는 documents에 담겨 오므로 따로 처리를 해줌.
List<BookDTO> bookList;
public BookListDTO() {
this.bookList = new ArrayList<>();
}
public int getSize() {
return bookList.size();
}
public BookDTO getBook(int index) {
return bookList.get(index);
}
public void addBook(BookDTO book) {
bookList.add(book);
}
public void removeBook(BookDTO book) {
bookList.remove(book);
}
}
각각의 Book의 객체
💡 여기서 kakao에서 도서의 정보를 json으로 제공할 때 작가는 String[] 로 담겨서 온다. 하지만 우리의 작업에서는 작가들을 DB에 저장해야하므로, String[] 형태보다는 String 형태가 필요했다. 그래서 Json으로 파싱할 때에는 String[] 로 받아오되, BookDTO에서 배열을 String 형태로 바꿔주는 메소드를 선언하여 활용하였다.
예시) ["홍길동", "나길동"] -> "홍길동, 나길동"
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookDTO {
// 도서 제목, 가격, 출판사, 저자, 할인 가격 및 ISBN
private String title, publisher, isbn;
private String[] authors;
private int price, sale_price;
public String toString() {
String str = "";
str += String.format("%-30s", title)
+ " | " + authorsToString(authors)
+ " | " + publisher
+ " | " + price
+ " | " + sale_price
+ " | " + isbn;
return str;
}
public String authorsToString(String[] authors) {
// String[] -> String 으로 만들어 DB에 저장하거나 출력하는 용도.
// ["홍길동","나길동"] -> 홍길동, 나길동 식으로 DB 저장 및 콘솔에 출력되기 위함.
String str_author = "";
for (int i = 0; i < authors.length-1; i++) {
str_author += authors[i] + ", ";
}
if (authors.length > 0) str_author += authors[authors.length-1];
return str_author;
}
}
📝 실행 결과
'패스트캠퍼스 > 과제' 카테고리의 다른 글
패스트캠퍼스X야놀자 백엔드 부트 캠프 : 미니프로젝트 회고 [ 숙박 예약 서비스 ] (0) | 2023.12.12 |
---|---|
토이프로젝트 3 : 여행 여정을 기록하고 관리하는 SNS 프로젝트 후기 (1) | 2023.11.20 |
토이 프로젝트2 : 여행 여정을 기록과 관리하는 SNS 서비스 2단계 회고 (0) | 2023.11.02 |
토이 프로젝트1 : 여행 여정을 기록과 관리하는 SNS 서비스 1단계 제작 (1) | 2023.09.14 |
특정 위치 주변의 💊 약국 💊 검색하는 어플리케이션 제작 (0) | 2023.08.24 |
- Total
- Today
- Yesterday
- 스터디후기
- 야놀자X패스트캠퍼스부트캠프
- Java
- 패스트캠퍼스강의
- 국비지원취업
- 데이터베이스
- 국비지원캠프
- 부트캠프
- 자료구조
- springboot
- TiL
- 그룹스터디워크샵
- 국비지원
- be
- 백엔드개발자
- 야놀자
- 백엔드부트캠프
- 그룹스터디
- 과정중간회고
- 백엔드
- qjzl
- #국비지원취업
- 카카오API
- 프로젝트후기
- 커리어멘토링
- 백준
- boj
- 자료구조 #스택 #큐 #덱 #선형자료구조
- 채팅기능개발
- 패스트캠퍼스
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |