Development/Java

KakaoAPI를 활용해 책을 검색하는 JAVA 애플리케이션을 만들어보자

Klay_J 2023. 8. 31. 12:28

과제를 받았다. (으어어ㅓㅓㅇ어어ㅓ)

 

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

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

키를 발급받는 자세한 방법은 아래 포스팅을 참고하면 된다.

https://kadosholy.tistory.com/25

 

카카오 API 키 발급받기 (kakao API Key)

카카오 API Key 발급받기 (kakao API Key) 카카오에서 제공하는 API를 이용하려면 먼저 카카오에서 발급하는 API Key를 발급받아야 합니다. (카카오 API Key 발급시에는 카카오 계정이 있어야 함) 여기서는

kadosholy.tistory.com

아래는 과제 내용이다.

 

※ 순수 Java로만 구현하기 위해 진행한 과제이다. lombok,JPA도 안쓰고 진행해서 코드가 많이 더러울 수 있..다

- DB : mySQL, JDBC(?!)

 

 


[과제 내용]

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

1.문제 설명

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

단계 2: 책 검색 API 사용

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

단계 4: 데이터베이스 저장 (with. JDBC)

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으로&nbsp; 쿼리에 "자바의 정석"을 넣고 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으로 형변환해서 간편하게 사용할 수 있는 방법이 있을지??

4. 나는 과연 객체지향적으로 코드를 짰는가??? 객체지향적으로 코드를 짠다는 건 무엇이지??(책과 실전의 괴리감을 느끼는중..)

 

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

 

[전체 코드]

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;
    }
}

감사합니다.