
과제 설명
목적
카카오 Daum 검색 REST API 중 책 검색하기 API를 활용하여 책 제목을 기반으로 책의 전반적인 정보들을 검색하는 java 애플리케이션을 개발합니다.
구현내용
- 카카오 API 키 획득
- 카카오 개발자에 로그인하여 어플리케이션 생성
- 생성한 어플리케이션의 REST API 메모
- 로컬 REST API 사용
- Daum 검색 중 책 검색하기 API 문서를 참고하여 개발한다.
- 요청과 응답 구조에 대한 예제 코드를 확인하여 개발한다.
- JAVA 어플리케이션 구현
- 책 제목 입력
- 제목을 기반으로 책의 기본 정보들 JSON 반환
- 검색 결과(JSON)에서 가격, 출판사, 저자, 할인가격 및 ISBN 결과를 추출
- 상위 10개 결과 표시
- 데이터 베이스 저장
- 검색 결과를 DB에 저장할지 선택
- 저장 선택 (Y)
- 관련 책 정보를 DB에 저장
- DB 데이터를 도서 제목을 기준으로 오름차순 정렬하여 호출
GET
책 검색하기 - URL : https://dapi.kakao.com/v3/search/book (JSON 객체)
└ Static으로 URL값을 저장해둔다.
- 헤더 : Authorization: KakaoAK ${REST_API_KEY}
└ src/main/resources/application.properties에 REST_API값 숨겨둔다.
REST_API_KEY와 같은 개인키는 github와 같은 공유공간에 업로드하면 안되므로, .gitignore에 적어 비공개로 해야한다. (과제를 위해 예외적으로 공개했다.)
public static void getAPIKey() throws IOException { Properties pro = new Properties(); FileInputStream fis = new FileInputStream(PROPERTIES); pro.load(new BufferedInputStream(fis)); REST_API_KEY = pro.getProperty("REST_API"); }
- Request
public static HttpUriRequest buildRequest(String info){ return RequestBuilder.get() .setUri(GET_BOOKINFO) .setHeader("Authorization", REST_API_KEY) .addParameter("query",info) .addParameter("size",String.valueOf(10)) .build(); }
책 제목을 입력하여 쿼리로 request를 보낸다.
- Response
[ Response 반환 함수 ]
public static StringBuffer getResponse(CloseableHttpClient client, HttpUriRequest request) { StringBuffer responseSB = null; try{ CloseableHttpResponse response = client.execute(request); BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); String input; responseSB= new StringBuffer(); while ((input = br.readLine()) != null) { responseSB.append(input); } br.close(); client.close(); } catch (IOException e){ System.out.println("IOException 발생"); } return responseSB; }
[ Response 파싱 함수 ]
public static List<Book> parsingResponse(StringBuffer responseSB){ List<Book> bookList = new ArrayList<>(); try { JSONObject resJsonObj = new JSONObject(responseSB.toString()); JSONArray resJsonArr = resJsonObj.getJSONArray("documents"); for(int i = 0; i<10; i++){ JSONObject subJsonObj = resJsonArr.getJSONObject(i); String title = subJsonObj.getString("title"); int price = subJsonObj.getInt("price"); String publisher = subJsonObj.getString("publisher"); JSONArray authorsJson = subJsonObj.getJSONArray("authors"); String authors = authorsJson.toString(); int sale_price = subJsonObj.getInt("sale_price"); String isbn = subJsonObj.getString("isbn"); Book book = new Book(title,price,publisher,authors,sale_price,isbn); bookList.add(book); } } catch (Exception e) { e.printStackTrace(); } return bookList; }
JSON으로 받은 여러 응답값 중 가격, 출판사, 저자, 할인가격 및 ISBN를 파싱하여 변수에 저장한다.
입출력화면




개인 공부 정리
과제 1을 진행하면서 멘토님께서 코드리뷰를 해주셨다.
해당 리뷰를 확인하며 과제 2를 보완했고, 그 과정을 정리하겠다.
- 메소드 분리
request를 생성하는 과정을 별도 메소드로 빼는 것이 조금 더 깔끔할 것 같다.
이전 과제에서는 메소드 분리를 많이 하지 않고, 대부분 하나의 java 파일에 몰아서 코드를 작성했다. 프로그램 자체는 잘 수행되지만, 결과적으로 공유하기 어려운 코드라 생각이 들었다. 프로그래밍도 하나의 언어로 타인이 이해하기 쉬운 코드를 짜는 것도 하나의 능력이라 생각하기에 이번 과제에서는 메소드 분리를 시도했다.
- try with resources 구문
자원해제하는 과정을 try with resources 구문을 활용하면 조금 더 깔끔할 것 같다.
이전 과제에서는 모든 exception들을 throws로 보내고 try-catch-finally 문장을 사용하지 않았다. 사실 그 차이를 잘 인지하고 있지 못해서 어떤게 더 효율적인 코드인지 몰랐던 것 같다.
❓try with resources 구문이란 ?try(…)에 선언된 객체들에 대해서 try가 종료될 때 자동으로 자원을 해제해주는 기능
- Java7부터는 코드 실행의 위치가 try(…)를 벗어나면 close()메소드를 자동으로 호출하기에 finally에 close()를 명시적으로 호출해줄 필요가 없다.
(단, 모든 객체가 close()를 호출해주지는 않고, AutoCloseable을 구현한 객체만 close()가 호출되므로, 필요하다면
implements AutoCloseable
을 해주면 된다.)
❓try catch와 throw의 차이예외처리 기법에는 총 3가지가 있다.
- try-catch를 통한 예외처리 | Checked Exception
- try : 실행할 코드
- catch : 예외가 발생했을 시 작동할 코드
에러 메시지를 직접 작성(공개)하면 보안관련 문제가 생길 수 있으므로 로깅시스템을 이용해서 따로 에러를 모아둬야한다.
└ (자세하게) e.printStackTrace() > e.getMessage() > e.toString() (간단하게)
- finally : 예외에 관계없이 작동할 코드
- throw로 예외 발생 | UnChecked Exception
개발자들이 비즈니스 로직에서 처리하는 예외 방법
- RuntimeException(UnCheckedException) : 런타임시 예외처리를 위해 쓰는 방식
- exception을 던질 때, 예외 내용도 던져주지 않고 exception을 따로 커스터마이징해서 만들고, 그 안에 메시지를 넣어서 던져주는 방법 (= ExceptionHandler)
- throws 키워드를 통한 예외처리 위임
해당 메서드를 호출한 쪽(부모)에게 예외 처리를 위임하는 방법
- Java7부터는 코드 실행의 위치가 try(…)를 벗어나면 close()메소드를 자동으로 호출하기에 finally에 close()를 명시적으로 호출해줄 필요가 없다.
- static 변수
클래스 변수 및 static 변수는 jvm 내 공유 메모리에 할당되기 때문에 동시성 이슈가 발생할 수 있다.
Java 에서 Static이란 메모리 중 Data영역에 한번 할당되어 프로그램이 종료될 때 해제되는 것을 의미한다. Data 영역에 할당되면 모든 객체가 해당 데이터를 공유한다는 장점을 갖지만, Heap 영역과 달리 Garbage Collector을 통해 관리 받을 수 없으므로 시스템 퍼포먼스에 악영향을 끼칠 수 있다.
그러므로 가급적 사용을 자제하고 지역변수로 사용하는 것이 좋다.
해당 피드백을 반영하여 이전 과제에 static으로 선언했던 변수들을 모두 지역변수로 바꾸어 과제를 제출했다.
- DB 연동 방법
과제를 위해 JDBC를 활용하여 DB와 연동하는 과정을 진행했다.
❓
JDBC를 이용한 DB 연동 과정
** 전체 코드는 GITHUB를 확인하도록 한다. **

(1) JDBC 드라이버 Load
Class.forName(CLASSNAME); // com.mysql.cj.jdbc.Driver
(2) Connection 객체 생성
conn = DriverManager.getConnection(CONNECTION_URL,USERNAME,PASSWORD);
//CONNECTION_URL = "jdbc:mysql://localhost:601/assignment2";
(3)(4) Statement 객체 생성 + Query 수행
private PreparedStatement ps;
String InsertSQL = "insert into booktable(title, price, publisher, authors, sale_price, isbn ) values(?,?,?,?,?,?)";
String SelectSQL = "select * from booktable order by title";
ps = conn.prepareStatement(' ' SQL);
(4-1) Insert Query Data Binding
for(Book book : bookList){
ps.setString(1, book.getTitle());
ps.setInt(2, book.getPrice());
ps.setString(3, book.getPublisher());
ps.setString(4, book.getAuthors());
ps.setInt(5, book.getSale_price());
ps.setString(6, book.getIsbn());
ps.executeUpdate();
}
(4-2) Select Query Result Set
while(rs.next()){
String title = rs.getString("title");
int price = rs.getInt("price");
String publisher = rs.getString("publisher");
String authors = rs.getString("authors");
int sale_price = rs.getInt("sale_price");
String isbn = rs.getString("isbn");
Book book = new Book(title,price,publisher,authors,sale_price,isbn);
bookListDB.add(book);
}
회고 및 고찰
- 프로젝트 구조에 대한 고민
이번 프로젝트를 하면서 과제 내용보다, 프로젝트 구조에 대한 고민을 많이 했다. 이전 과제와 내용은 비슷했고 db연동 자체는 수업을 통해 여러번 실습을 하니 익숙했다. 그렇기에 가장 큰 고민은 “어떻게 하면 짜임새있는 코드를 짤 수 있을까?”였다.
프로젝트의 규모가 컸다면 Spring을 통해 프로젝트를 할때와 같이 Controller, Service, Http, DTO 등으로 패키지를 분리하여 코드를 작성했을 것 같다. 하지만, 이번 과제는 그 규모가 크지 않다고 생각했고 너무 패키지를 많이 나누면 오히려 확인이 어려울 것 같았다.
** 이런 고민들은 디자인 패턴(설계이론)등을 공부하면 해결 될 수 있을 것 같아 앞으로 틈틈히 이를 공부해야겠다 !!
** 이번 기회를 통해 패키지 분리 기준에 대해서 공부하여 앞으로의 프로젝트에 적용해야겠음을 깨닫게 됐다.
[ 가장 일반적인 프로젝트의 구성 ]
[com.example]
- api
- controller
- domain
- exception
- repository
- service
[resources]
- statics
- templates
- application.properties
[ Scaling을 고려한 프로젝트의 구성 ]
[components]
- delivery
- controller
- dto
- repository
- order
- controller
- dto
- repository
- payment
- controller
- dto
- repository
- user
- controller
- dto
- repository
└ rluvaton이 작성한 프로젝트 설계에 관한 방법
- 중소 규모 이상의 애플리케이션에서는 monolith를 지양하자
- 자급자족할 수 있는 비지니스 컴포넌트로 나누어 프로젝트를 구성하여 컴포넌트 사이에 경계를 두면, 의존성을 방지하여 마이크로서비스로 전환할 수 있는 가능성이 생긴다.
⇒ 프로젝트 패키지만 보고도 이것이 어느 시스템인지 알 수 있도록 하자
Uploaded by N2T