[자바 2차과제] KakaoAPI를 활용해 책을 검색하는 JAVA 애플리케이션을 만들어보자

·

7 min read

과제를 받았다.

일단 https://developers.kakao.com/ 여기서 "내 애플리케이션"에서 app을 생성한 뒤, API KEY를 받아두었다.

아래는 과제 내용이다.

※ 순수 Java로만 구현하기 위해 진행한 과제이다. lombok,JPA도 안썼다!

- DB : mySQL, JDBC(!)


[과제 내용]

책 검색 및 데이터베이스 Java 애플리케이션 개발

이 프로그램은 Kakao Book Open API를 활용하여 책을 검색하고, 검색한 책 데이터를 데이터베이스에 저장하는 기능을 제공합니다.

1.문제 설명

단계 1: Kakao API 키 획득 <- 위에서 발급한 API KEY를 의미한다.

단계 2: 책 검색 API 사용

단계 3: Java 애플리케이션 구현

  1. Kakao Book Open API와 상호작용하는 Java 애플리케이션을 설계합니다.

  2. 입력으로 책 제목을 제공하고 API에서 받아온 책 데이터를 JSON 형식으로 파싱합니다.

  3. 검색한 책에 관한 정보를 도서 제목, 가격, 출판사, 저자, 할인 가격 및 ISBN과 같이 적절한 정보로 출력합니다.

  4. 책 데이터는 기본 10개를 출력합니다.

단계 4: 데이터베이스 저장

  1. 검색 결과를 출력한 후, 사용자에게 데이터베이스에 저장할지 여부를 선택하도록 안내합니다.

  2. 사용자가 저장을 선택한 경우 (Y), 관련 책 정보를 데이터베이스에 저장하는 로직을 구현합니다.

  3. 데이터베이스에 저장된 책 목록을 도서 제목을 기준으로 오름차순 정렬하여 불러오는 로직을 구현합니다.

2.입력 및 출력 예시

[입력 화면]

도서를 검색할 제목을 입력하세요: 자바의 정석

[출력 결과]

데이터베이스에 저장하시겠습니까? Y/N

저장성공

[TABLE LIST]


생각보다 어렵지 않고 재밌어보였고, 구현하면서도 재밌게 했던 것 같다ㅋㅋ 생각보다 체질에 맞을지도,,

암튼 각설하고, 어떻게 구현했는지 코드를 보자.

[클래스 다이어그램]

클래스는 총 4개다.

- Main : 유저의 Input/Output을 관리하고 애플리케이션 전체 흐름을 관장하는 클래스이다.

- KakaoAPI : 카카오 API와 http 연결을 통해 응답을 받아오고 api_key를 관리하는 클래스이다.

- BookDAO : mysqlDB와 연결 및 CRUD를 관리하는 클래스이다.

- Book : 책 정보 및 속성을 관리하는 클래스이다.

[시퀀스 다이어그램]

시퀀스 다이어그램을 보면,

1. main 클래스의 main메서드에서 searchBookList 메서드를 실행한다.

  • searchBookList를 실행하면 사용자에게 검색할 keyword를 입력받는다.

  • 만약 키워드가 빈 값이면 "키워드가 빈 값입니다"를 출력하고 종료시킨다.

  • 키워드가 있다면 아래 2번을 실행한다.

2. 키워드가 있다면 kakaoAPI클래스의 findBookByKeyword 메서드를 실행한다.

  • kakaoAPI에 요청할 URL을 생성한다.
String url = search_book_URL + "?query=" + URLEncoder.encode(keyword, "UTF-8");
  • requestApi 메서드를 실행하여 kakaoAPI와 HTTP 통신을 연결한다.

  • reqeustApi는 CloseablehttpClient를 활영하여 http request 메시지의 헤더 정보를 설정하고 요청을보낸다.

  • 이후 httpResponse를 통해 위 url에 부합하는 정보를 얻어온다.

  • 얻어온 정보를 버퍼에 담고, json으로 반환한다.

3. 반환한 json을에 담긴 책정보 중 필요한 key만 골라서 Book 객체에 담아준다.

  • title,price,publisher,sale_price,authors,isbn

  • book정보는 총 10권까지 받아오므로, 각 Book객체는 List타입의 BookList에 모아준다.

  • 이후 받아온 book 정보를 콘솔창에 출력한다.

Npostman으로 쿼리에 "자바의 정석"을 넣고 get요청해 받은 책 정보.json

4. Main 클래스 main메서드에서 saveBookList메서드를 실행하여 DB에 저장한다.

  • 유저가 "데이터베이스에 저장하시겠습니까?Y/N" 에 대한 응답을 Y로 입력했다면, 저장로직을 시작한다.

  • 저장로직이 담긴 메서드 실행 : saveBooksList(ArrayList<Book> booklist)

  • BookDAO 객체를 생성하고 매개변수로 받은 booklist를 for문을 통해 책 1권씩 DB에 insert해준다.

  • 위 insert는 BookDAO의 insertData 메서드를 실행시켜 진행한다.

5. BookDAO 클래스의 insertData 메서드를 실행하면 mysql DB에 데이터가 저장된다.

public int insertData(Book book) {
    String SQL = "insert into task2(title,price,publisher,authors,discountPrice,ISBN) values(?,?,?,?,?,?)";
    getConnection();
    int cnt = 1;
    try {
        preparedStatement=connection.prepareStatement(SQL);
        preparedStatement.setString(1,book.getTitle());
        preparedStatement.setInt(2,book.getPrice());
        preparedStatement.setString(3,book.getPublisher());
        preparedStatement.setObject(4,book.getAuthors().get(0));
        preparedStatement.setInt(5,book.getDiscountPrice());
        preparedStatement.setString(6,book.getISBN());
        cnt = preparedStatement.executeUpdate();

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        dbclose();
    }
    return cnt;
}

6. BookDAO 클래스의 findAllBookList 메서드를 실행시켜 DB에 저장된 책을 오름차순으로 출력한다.

애플리케이션 입출력 화면


[Review]

뭐 요구사항을 만족하는데 큰 문제는 없지만,,몇가지 해결되지 않은 의문이 있다.

1. 순수 자바로 구현할 때 API_KEY는 어떻게 보호할 수 있을까?

2. kakaoAPI에서 json으로 정보를 받아오면, 파싱하는 과정이 번거로운 경우가 있다.
방금도 KakaoAPI.java 내 findBookListByKeyword 메서드를 통해 카카오API에서 jsonobject를 먼저 받아온 후, key값이 'documents'인 JSONArray만 추출하는 과정이 있었다. 이처럼 JSON 구조를 한꺼풀씩 벗겨나가는 게 상당히 번거로운데, Gson 라이브러리를 이용하면 이런 불편함을 해소할 수 있는지 ?

3. authours 타입만 JSONArray로 감싸져 있어서 파싱하는데 불편함을 겪었는데, String으로 형변환해서 간편하게 사용할 수 있는 방법이 있을지??

이와 같은 고민을 좀...하는 중이다. 더 좋게 해결할 수 있는 방법이 있을지..?

[전체 코드]

Main.java

public class Main {
    private static final Scanner scanner = new Scanner(System.in);
    private static final KakaoAPI kakaoAPI = new KakaoAPI();

    public static void main(String[] args) throws Exception {
        ArrayList<Book> booklist = searchBookList();
        saveBooksList(booklist);
        scanner.close();
    }
    private static ArrayList<Book> searchBookList() throws IOException {
        System.out.print("키워드를 입력하세요: ");
        String keyword = scanner.nextLine();
        ArrayList<Book> booklist = null;

        if (!keyword.isEmpty()) {
            System.out.println("도서 제목 " + " | " + "가격 " + " | " + "출판사 " + " | " + "작가 " + " | " + "할인 가격 " + " | " + "ISBN");
            booklist = kakaoAPI.findBookListByKeyword(keyword);
        } else {
            System.out.println("키워드가 빈 값 입니다.");
        }
        return booklist;
    }

    private static void saveBooksList(ArrayList<Book> booklist) throws Exception {
        if (booklist.size() != 0) {
            System.out.println("데이터베이스에 저장하시겠습니까? Y/N");
            String answerSaveDB = scanner.next();
            if (answerSaveDB.equals("Y")) {
                System.out.println("저장 시작");
                BookDAO dao = new BookDAO();
                for (int i = 0; i < booklist.size(); i++) {
                    int cnt = dao.insertData(booklist.get(i));
                    if (cnt > 0) {
                        System.out.println("저장성공 " + (i + 1));
                    } else {
                        System.out.println("저장실패");
                    }
                }
                System.out.println("[TABLE LIST]");
                System.out.println("도서 제목 " + " | " + "가격 " + " | " + "출판사 " + " | " + "작가 " + " | " + "할인 가격 " + " | " + "ISBN");
                dao.findAllBookList();
            } else {
                System.out.println("저장하지 않고 종료");
            }
        } else {
            System.out.println("조회 서비스를 종료합니다.");
        }
    }


}

KakaoAPI.java

public class KakaoAPI {

    private static String API_KEY = "aaaaaaaaaaaaaaaaaaaa";
    private static String search_book_URL = "https://dapi.kakao.com/v3/search/book";

    public ArrayList<Book> findBookListByKeyword(String keyword) throws IOException {
        String url = search_book_URL + "?query=" + URLEncoder.encode(keyword, "UTF-8");
        JSONObject BookInformationJson = requestApi(url);
        JSONArray documents = BookInformationJson.getJSONArray("documents");

        ArrayList<Book> booklist = new ArrayList<>();
        if (documents.length() != 0) {
            for (int i = 0; i < documents.length(); i++) {
                JSONObject bookObject = documents.getJSONObject(i);
                Book book = new Book((String) bookObject.get("title"), (Integer) bookObject.get("price"), (String) bookObject.get("publisher"), (Integer) bookObject.get("sale_price"), (JSONArray) bookObject.get("authors"), (String) bookObject.get("isbn"));
                booklist.add(book);
                System.out.println(book);
            }
        } else {
            System.out.println("찾으려는 도서가 없습니다.");
        }
        return booklist;
    }

    private JSONObject requestApi(String apiUrl) {
        String BookInformationJson = "";
        try {
            CloseableHttpClient httpClient = HttpClientBuilder.create().build();
            HttpGet getRequest = new HttpGet(apiUrl);
            getRequest.setHeader("Authorization","KakaoAK "+API_KEY);
            HttpResponse getResponse = httpClient.execute(getRequest);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(getResponse.getEntity().getContent(),"UTF-8"));
            BookInformationJson = bufferedReader.readLine();
            httpClient.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new JSONObject(BookInformationJson);


    }
}

BookDAO.java

public class BookDAO {

    private Connection connection;
    private Statement statement;
    private PreparedStatement preparedStatement;
    private ResultSet resultSet;

    public void getConnection() {
        String url = "jdbc:mysql://localhost:3306/aaaaaaa";
        String username = "aaaaaa";
        String password = "aaaaaa";
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection(url, username, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public int insertData(Book book) {
        String SQL = "insert into task2(title,price,publisher,authors,discountPrice,ISBN) values(?,?,?,?,?,?)";
        getConnection();
        int cnt = 1;
        try {
            preparedStatement=connection.prepareStatement(SQL);
            preparedStatement.setString(1,book.getTitle());
            preparedStatement.setInt(2,book.getPrice());
            preparedStatement.setString(3,book.getPublisher());
            preparedStatement.setObject(4,book.getAuthors().get(0));
            preparedStatement.setInt(5,book.getDiscountPrice());
            preparedStatement.setString(6,book.getISBN());
            cnt = preparedStatement.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            dbclose();
        }
        return cnt;
    }

    public void findAllBookList() throws Exception {
        String SQL = "select * from task2 order by title";
        getConnection();
        int cnt = 1;
        try {
            statement = connection.createStatement();
            resultSet = statement.executeQuery(SQL);
            while (resultSet.next()) {
                String title = resultSet.getString("title");
                int price = resultSet.getInt("price");
                String publisher = resultSet.getString("publisher");
                int discountPrice = resultSet.getInt("discountPrice");
                String authors = resultSet.getString("authors");
                String ISBN = resultSet.getString("ISBN");
                System.out.println(title + " | " + price + " | " + discountPrice + " | " + authors + " | " + ISBN);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            dbclose();
        }
    }
    public void dbclose() {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (preparedStatement != null) {
                preparedStatement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Book.java

public class Book {

    String title;
    Integer price;
    String publisher;
    JSONArray authors;
    Integer discountPrice;
    String ISBN;
    public Book(String title, Integer price, String publisher, Integer discountPrice, JSONArray authors, String ISBN) throws UnsupportedEncodingException {
        this.title = title;
        this.price = price;
        this.publisher = publisher;
        this.authors = authors;
        this.discountPrice = discountPrice;
        this.ISBN = ISBN;
    }

    @Override
    public String toString() {
        return this.title + " | " + this.price + " | " + this.publisher + " | " + this.authors.get(0) + " | " + this.discountPrice + " | " + this.ISBN;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

    public JSONArray getAuthors() {
        return authors;
    }

    public void setAuthors(JSONArray authors) {
        this.authors = authors;
    }

    public Integer getDiscountPrice() {
        return discountPrice;
    }

    public void setDiscountPrice(Integer discountPrice) {
        this.discountPrice = discountPrice;
    }

    public String getISBN() {
        return ISBN;
    }

    public void setISBN(String ISBN) {
        this.ISBN = ISBN;
    }
}

감사합니다.