글에 앞서서…


이 글은 추천 도서 시스템을 구축하는 과정과 그 안에서 발생한 문제를 해결하는 과정을 정리한 글이다. 구현 기능을 정하게 된 계기가 의사결정 과정을 먼저 알고 싶다면 밑에 게시한 해당 페이지를 읽으면 많은 도움이 될 것이다.😊

도서 추천 시스템 구축 과정😀

개발과정


  1. 도서의 상세페이지에 들어가게 된 다면 쿠키를 생성하고 쿠키의 값은 해당 도서의 isbn으로 설정한다.
@GetMapping("/search_isbn")
    public String getBookIsbn(@RequestParam() String isbn, Model model, HttpServletResponse response,HttpServletRequest request) {
        Cookie cookie = new Cookie(isbn, isbn);
        cookie.setMaxAge(3600);//쿠키의 수명을 한시간으로 지정
        response.addCookie(cookie);//새로운 쿠키를 저장
       //생략....
		}

(참고:쿠키의 생성과 삭제 등 cookie에 대한 정보를 더욱 더 알고 싶으면 작성자가 정리한 글이 있으니 하단 링크로 들어가면 도움이 될 것이다.)

Cookie를 배워보도록 하자🍪

  1. **저장된 Cookie의 정보를 이용하여 추천 도서 목록을 불러온다.

문제점:** 만약 현재 저장된 cookie가 많은데 이 들을 모두 이용해서 추천 도서 데이터 open api를 호출하면 특정 도서에만 관련된 도서 데이터들만 응답 상단에 위치해 있고 다른 도서들와 관련된 도서는 응답 상단에 노출되지 않은다.(controller 부분에서 응답된 json타입의 도서 목록 중에서 첫번째 부터 10번째 까지의 도서 데이터만 return한다.) 이렇게 어떤 특정 도서들의 관련 도서만 추천 리스트에 노출된다면 사용자에게 그렇게 달갑지 않을 것이다.

해결방법: 호출하는 cookie들의 갯수를 제한을 한다. 이렇게 하면 많은 수들의 책들의 isbn으로 open api를 호출해서 어떠한 특정 책들의 책들의 정보만 노출되는 것이 아니라. 제한된 갯수(우리는 최근 조회한 책들중에 상위 3개의 cookie들로만) open api를 호출을 해서 관련된 추천 도서들이 최대한 많이 노출되도록 하였다.

메인 page를 호출하는 controller부분

@GetMapping("/")
    public String main(Model model, HttpServletRequest request,HttpServletResponse response) {
        Cookie[] cookies = request.getCookies();//브라우저에 저장된 쿠키를 받음
        if(cookies!=null) {//지금 저장된 COOKIE가 존재하면
            StringBuilder sb = new StringBuilder();/isbn을 parsing하기 위한 StringBuilder
            if(cookies.length==1){//현재 조회한 책이 한개일 경우는 한개의 isbn으로만 추천 리스트를 보냄
                sb.append(cookies[0].getValue());
            }
            else if(cookies.length==2) {//두개일때는 두개의 isbn으로만 호출
                sb.append(cookies[0].getValue());
                sb.append(";");
                sb.append(cookies[1].getValue());
            }
            else{//그 이상이라면 상위 3개의 isbn을 parsing해서 ghcnf
                for (int i = cookies.length-1; i >=cookies.length-3; i--) {
                    if (i == cookies.length - 1)
                        sb.append(cookies[i].getValue());
                    else {
                        sb.append(cookies[i].getValue());
                        sb.append(";");
                    }
                }
            }
            List<BookResponseDto3> booksList = bookService.recommend_Book(sb.toString());
            model.addAttribute("list", booksList);//MVC
            return "main";
        }
        return "main";
    }

json 객체를 받아오고 return하는 service 부분

@Transactional
    public List<BookResponseDto3>recommend_Book(String isbn) {
        //도서나루 open api를 통해 도서 상세 정보를 불러오는 부분
        String url_address = "<https://data4library.kr/api/recommandList?authKey=>[허가받은 key]&isbn13=" + isbn + "&format=json";//호출 url
        try {
            URL url = new URL(url_address);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("Content-type", "application/json");
            BufferedReader rd;
            if (conn.getResponseCode() >= 200 && conn.getResponseCode() <= 300) {
                rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            } else {
                rd = new BufferedReader(new InputStreamReader(conn.getErrorStream()));
            }
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = rd.readLine()) != null) {
                sb.append(line);
            }
            rd.close();
            conn.disconnect();
            JSONParser parser = new JSONParser();
            JSONObject obj = (JSONObject) parser.parse(sb.toString());
            JSONObject response = (JSONObject) obj.get("response");
            JSONArray detail = (JSONArray) response.get("docs");
            List<BookResponseDto3> list= new ArrayList<>();
            for (int i = 0; i < 10; i++) {//return된 도서 데이터들중 상위 10가지의 데이터만 받아옴
                JSONObject temp = (JSONObject) detail.get(i);
                JSONObject book = (JSONObject) temp.get("book");
                BookResponseDto3 bookResponseDto = BookResponseDto3.builder()
                        .bookName((String) book.get("bookname"))//책제목
                        .authors((String) book.get("authors"))//저자
                        .publisher((String) book.get("publisher"))//출판사
                        .class_nm((String) book.get("class_nm"))//주제 분류명
                        .publicationYear((String) book.get("publication_year"))//출판년도
                        .bookImageURL((String) book.get("bookImageURL"))//책표지 URL
                        .class_no((String) book.get("class_no"))//주제분류
                        .isbn13((String) book.get("isbn13"))//13자리 ISBN
                        .build();
               list.add(bookResponseDto);//저장
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }