패스트캠퍼스 X 야놀자: 백엔드 개발 부트캠프

패스트캠퍼스X야놀자: 백엔드 개발 부트캠프 - DB를 활용한 책 검색 어플리케이션

이-프 2023. 9. 1. 10:05

과제 설명

목적

카카오 Daum 검색 REST API 중 책 검색하기 API를 활용하여 책 제목을 기반으로 책의 전반적인 정보들을 검색하는 java 애플리케이션을 개발합니다.

구현내용

  • 카카오 API 키 획득
    • 생성한 어플리케이션의 REST API 메모

  • 로컬 REST API 사용
    • Daum 검색 중 책 검색하기 API 문서를 참고하여 개발한다.
    • 요청과 응답 구조에 대한 예제 코드를 확인하여 개발한다.

  • JAVA 어플리케이션 구현
    • 책 제목 입력
    • 제목을 기반으로 책의 기본 정보들 JSON 반환
    • 검색 결과(JSON)에서 가격, 출판사, 저자, 할인가격 및 ISBN 결과를 추출
    • 상위 10개 결과 표시

  • 데이터 베이스 저장
    • 검색 결과를 DB에 저장할지 선택
    • 저장 선택 (Y)
      • 관련 책 정보를 DB에 저장
      • DB 데이터를 도서 제목을 기준으로 오름차순 정렬하여 호출

🔑
GET 검색하기
  • 헤더 : 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

DB 저장 (Y) 후 출력화면

DB 테이블 화면

DB 저장 (N) 후 출력화면

개인 공부 정리

과제 1을 진행하면서 멘토님께서 코드리뷰를 해주셨다.

해당 리뷰를 확인하며 과제 2를 보완했고, 그 과정을 정리하겠다.

  1. 메소드 분리
    request를 생성하는 과정을 별도 메소드로 빼는 것이 조금 더 깔끔할 것 같다.

    이전 과제에서는 메소드 분리를 많이 하지 않고, 대부분 하나의 java 파일에 몰아서 코드를 작성했다. 프로그램 자체는 잘 수행되지만, 결과적으로 공유하기 어려운 코드라 생각이 들었다. 프로그래밍도 하나의 언어로 타인이 이해하기 쉬운 코드를 짜는 것도 하나의 능력이라 생각하기에 이번 과제에서는 메소드 분리를 시도했다.

    변경 전
    변경 후 (메소드로 분리)
  1. 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가지가 있다.

    1. try-catch를 통한 예외처리 | Checked Exception
      • try : 실행할 코드
      • catch : 예외가 발생했을 시 작동할 코드

        에러 메시지를 직접 작성(공개)하면 보안관련 문제가 생길 수 있으므로 로깅시스템을 이용해서 따로 에러를 모아둬야한다.

        └ (자세하게) e.printStackTrace() > e.getMessage() > e.toString() (간단하게)

      • finally : 예외에 관계없이 작동할 코드
    1. throw로 예외 발생 | UnChecked Exception

      개발자들이 비즈니스 로직에서 처리하는 예외 방법

      • RuntimeException(UnCheckedException) : 런타임시 예외처리를 위해 쓰는 방식
      • exception을 던질 때, 예외 내용도 던져주지 않고 exception을 따로 커스터마이징해서 만들고, 그 안에 메시지를 넣어서 던져주는 방법 (= ExceptionHandler)
    1. throws 키워드를 통한 예외처리 위임

      해당 메서드를 호출한 쪽(부모)에게 예외 처리를 위임하는 방법

  1. static 변수
    클래스 변수 및 static 변수는 jvm 내 공유 메모리에 할당되기 때문에 동시성 이슈가 발생할 수 있다.

    Java 에서 Static이란 메모리 중 Data영역에 한번 할당되어 프로그램이 종료될 때 해제되는 것을 의미한다. Data 영역에 할당되면 모든 객체가 해당 데이터를 공유한다는 장점을 갖지만, Heap 영역과 달리 Garbage Collector을 통해 관리 받을 수 없으므로 시스템 퍼포먼스에 악영향을 끼칠 수 있다.

    그러므로 가급적 사용을 자제하고 지역변수로 사용하는 것이 좋다.

    변경 전
    변경 후

    해당 피드백을 반영하여 이전 과제에 static으로 선언했던 변수들을 모두 지역변수로 바꾸어 과제를 제출했다.

  1. DB 연동 방법

    과제를 위해 JDBC를 활용하여 DB와 연동하는 과정을 진행했다.

    JDBC란?

    자바 언어로 데이터베이스 프로그래밍을 하기 위한 라이브러리

    • DBMS에 종속되지 않는 관련 API를 제공
    • JDK → JDBC API를 제공
    • JDBC 프로그래밍 : JDBC 드라이버 필요 (DBMS 회사에서 제공하는 라이브러리 압축파일)

      ex ) MySQL용 JDBC 드라이버 사용

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

회고 및 고찰

  1. 프로젝트 구조에 대한 고민

    이번 프로젝트를 하면서 과제 내용보다, 프로젝트 구조에 대한 고민을 많이 했다. 이전 과제와 내용은 비슷했고 db연동 자체는 수업을 통해 여러번 실습을 하니 익숙했다. 그렇기에 가장 큰 고민은 “어떻게 하면 짜임새있는 코드를 짤 수 있을까?”였다.

    프로젝트의 규모가 컸다면 Spring을 통해 프로젝트를 할때와 같이 Controller, Service, Http, DTO 등으로 패키지를 분리하여 코드를 작성했을 것 같다. 하지만, 이번 과제는 그 규모가 크지 않다고 생각했고 너무 패키지를 많이 나누면 오히려 확인이 어려울 것 같았다.

    ** 이런 고민들은 디자인 패턴(설계이론)등을 공부하면 해결 될 수 있을 것 같아 앞으로 틈틈히 이를 공부해야겠다 !!

    ** 이번 기회를 통해 패키지 분리 기준에 대해서 공부하여 앞으로의 프로젝트에 적용해야겠음을 깨닫게 됐다.


    [ 가장 일반적인 프로젝트의 구성 ]

    [com.example]

    • api
    • controller
    • domain
    • exception
    • repository
    • service

    [resources]

    • statics
    • templates
    • application.properties

    [ Scaling을 고려한 프로젝트의 구성 ]

    참고 github

    [components]

    • delivery
      • controller
      • dto
      • repository
    • order
      • controller
      • dto
      • repository
    • payment
      • controller
      • dto
      • repository
    • user
      • controller
      • dto
      • repository

    └ rluvaton이 작성한 프로젝트 설계에 관한 방법

    • 중소 규모 이상의 애플리케이션에서는 monolith를 지양하자
    • 자급자족할 수 있는 비지니스 컴포넌트로 나누어 프로젝트를 구성하여 컴포넌트 사이에 경계를 두면, 의존성을 방지하여 마이크로서비스로 전환할 수 있는 가능성이 생긴다.

    ⇒ 프로젝트 패키지만 보고도 이것이 어느 시스템인지 알 수 있도록 하자


Uploaded by N2T