항해 8주차 회고 (WIL)

계리·2022년 11월 14일
0
post-custom-banner

항해 8주차 WIL List

NAVER API를 이용하여 Data Extract한 후 Data Parsing 하기

  • NEAVER OPEN API

NAVER 오픈API를 사용하려면 네이버 개발자 센터(https://developers.naver.com/main/)에서 클라이언트 아이디와 클라이언트 시크릿을 발급 받아야 한다. 네이버 오픈API를 호출할 때 HTTP 헤더에 포함해서 전송해야 인증된 사용자인지를 확인하고 API를 호출할 수 있다. 네이버 개발자 센터에서 애플리케이션을 등록하면 발급이 된다.


네이버 개발자 센터에서 나의 애플리케이션 등록

네이버 개발자 센터에 접속 하여 로그인 후
상단에 Application > 애플리케이션 등록 클릭


사용 용도에 맞춰서 각 항목을 작성

  • 애플리케이션 이름 작성
  • 사용 API를 선택
  • 환경 추가
  • 웹 서비스 URL 작성

그러면 애플리케이션 등록이 완료되고 Client ID와 Client Secret 발급 된 것을 확인할 수 있다.



Documents > 책

책 검색에 관한 정보를 얻기 위해 Documents에 접속하여 확인을 하였다.

책에 대한 검색 결과 조회를 사용할 것이기 때문에 '책 검색 결과 조회'를 클릭한다.
그러면 설명부터 차례대로 Documents를 읽으면서 내려가면 마지막에 '검색 API 책 검색 구현 예제'를 클릭한다. 클릭하면 Java, PHP, Node.js, Python, C# 언어들에 대한 예제코드가 나와있으므로 해당 코드를 클릭하면 확인할 수 있다.


import com.project.odok.dto.ResponseDto;
import lombok.RequiredArgsConstructor;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.*;
import java.net.URLEncoder;

import java.util.*;


@RequiredArgsConstructor
@Service
    public ResponseDto<?> searchResult(String keyword){

            String bookTitle = null;

            try {
                bookTitle = URLEncoder.encode(keyword, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("검색어 인코딩 실패",e);
            }

            String apiURL = "https://openapi.naver.com/v1/search/book?query=" + bookTitle + "&display=" + 5 + "&start=" + 1;


            Map<String, String> requestHeaders = new HashMap<>();
            requestHeaders.put("X-Naver-Client-Id", clientId);
            requestHeaders.put("X-Naver-Client-Secret", clientSecret);
            String responseBody = get(apiURL,requestHeaders);

            return ResponseDto.success(responseBody);

    }

    private static String get(String apiUrl, Map<String, String> requestHeaders){
        HttpURLConnection con = connect(apiUrl);
        try {
            con.setRequestMethod("GET");
            for(Map.Entry<String, String> header :requestHeaders.entrySet()) {
                con.setRequestProperty(header.getKey(), header.getValue());
            }


            int responseCode = con.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출
                return readBody(con.getInputStream());
            } else { // 오류 발생
                return readBody(con.getErrorStream());
            }
        } catch (IOException e) {
            throw new RuntimeException("API 요청과 응답 실패", e);
        } finally {
            con.disconnect();
        }
    }


    private static HttpURLConnection connect(String apiUrl){
        try {
            URL url = new URL(apiUrl);
            return (HttpURLConnection)url.openConnection();
        } catch (MalformedURLException e) {
            throw new RuntimeException("API URL이 잘못되었습니다. : " + apiUrl, e);
        } catch (IOException e) {
            throw new RuntimeException("연결이 실패했습니다. : " + apiUrl, e);
        }
    }


    private static String readBody(InputStream body){
        InputStreamReader streamReader = new InputStreamReader(body);

        try (BufferedReader lineReader = new BufferedReader(streamReader)) {
            StringBuilder responseBody = new StringBuilder();

            String line;
            while ((line = lineReader.readLine()) != null) {
                responseBody.append(line);
            }

            return responseBody.toString();
        } catch (IOException e) {
            throw new RuntimeException("API 응답을 읽는 데 실패했습니다.", e);
        }
    }

우리 프로젝트에 맞게 제네릭 형태로 responseBody를 반환해주는 형식으로 변경을 했는데 JSON형태의 검색결과가 나오지 않았다. 그래서 반환 값을 String으로 변경해서 다시 출력 해봤다.


반환 값을 String으로 변경하니까 아까와 다르게 형태가 바뀌긴 했지만 아직까지 JSON 형태로 출력이 되지 않아 네이버 도서 검색 API에 대한 자료들을 열심히 찾아보았다.


다른 형태의 NAVER API 호출 메서드

    public String searchResult(String keyword) {
        String encode = Base64.getEncoder().encodeToString(keyword.getBytes(StandardCharsets.UTF_8));

        URI uri = UriComponentsBuilder.fromUriString("https://openapi.naver.com/")
                .path("v1/search/book.json")
                .queryParam("query", keyword)
                .queryParam("display", 3)
                .queryParam("start", 1)
                .encode()
                .build()
                .toUri();

        RestTemplate restTemplate = new RestTemplate();


        RequestEntity<Void> req = RequestEntity
                .get(uri)
                .header("X-Naver-Client-Id", clientId)
                .header("X-Naver-Client-Secret", clientSecret)
                .build();

        ResponseEntity<String> result = restTemplate.exchange(req, String.class);

        return result.getBody();
    }
  • UriComponentsBuilder로 검색 요청 시 받은 keyword와 함께 NAVER API로 연결할 URI를 생성
  • 생성된 URI로 RequestEntity 객체에 생성된 URI, 연결할 header값들을 build
  • ReuqestEntity를 build한 값들을 RestTemplate클래스에 내장되어 있는 exchange메서드로 NAVER API에 연결하여 도서 검색을 요청한 값들을 ResponseEntity로 반환


JSON형태의 값들로 반환하는 것을 확인할 수 있다.


데이터 Parsing

Parsing을 하기 위해서는 JSONParsing 관련 의존성 추가가 필요하다. 아래 코드를 build.gradle에 추가하면 된다.

implementation group: 'com.googlecode.json-simple', name: 'json-simple', version: '1.1.1'

NAVER Developer에서 제공해준 NAVER API 예제와 JSON관련된 코드들을 추가하였다.

        public String searchResult(String keyword){

            String bookTitle = null;

            try {
                bookTitle = URLEncoder.encode(keyword, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("검색어 인코딩 실패",e);
            }

            String apiURL = "https://openapi.naver.com/v1/search/book?query=" + bookTitle + "&display=" + 5 + "&start=" + 1;


            Map<String, String> requestHeaders = new HashMap<>();
            requestHeaders.put("X-Naver-Client-Id", clientId);
            requestHeaders.put("X-Naver-Client-Secret", clientSecret);
            String responseBody = get(apiURL,requestHeaders);

            try{
                JSONParser jsonParser = new JSONParser();
                JSONObject jsonObject = (JSONObject) jsonParser.parse(responseBody);
                JSONArray items = (JSONArray) jsonObject.get("items");

                for(int i = 0; i < items.size(); i++){
                    JSONObject tmp = (JSONObject) items.get(i);
                    String title = (String) tmp.get("title");
                    String link = (String) tmp.get("link");
                    String image = (String) tmp.get("image");
                    String author = (String) tmp.get("author");
                    String publisher = (String) tmp.get("publisher");
                    String isbn = (String) tmp.get("isbn");

                    test += "title = " + title + "\r\n"
                            + "link = " + link + "\r\n"
                            + "image = " + image + "\r\n"
                            + "author = " +  author + "\r\n"
                            + "publisher = " + publisher + "\r\n"
                            + "isbn = " + isbn + "\r\n"
                            + "\r\n"
                            + "\r\n";

                }
            } catch (ParseException e) {
                throw new RuntimeException("Parsing Exception");
            }
            return test;
        }
        
        private static String get(String apiUrl, Map<String, String> requestHeaders){
                HttpURLConnection con = connect(apiUrl);
                try {
                    con.setRequestMethod("GET");
                    for(Map.Entry<String, String> header :requestHeaders.entrySet()) {
                        con.setRequestProperty(header.getKey(), header.getValue());
                    }


                    int responseCode = con.getResponseCode();
                    if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출
                        return readBody(con.getInputStream());
                    } else { // 오류 발생
                        return readBody(con.getErrorStream());
                    }
                } catch (IOException e) {
                    throw new RuntimeException("API 요청과 응답 실패", e);
                } finally {
                    con.disconnect();
                }
            }


        private static HttpURLConnection connect(String apiUrl){
                try {
                    URL url = new URL(apiUrl);
                    return (HttpURLConnection)url.openConnection();
                } catch (MalformedURLException e) {
                    throw new RuntimeException("API URL이 잘못되었습니다. : " + apiUrl, e);
                } catch (IOException e) {
                    throw new RuntimeException("연결이 실패했습니다. : " + apiUrl, e);
                }
            }


        private static String readBody(InputStream body){
                InputStreamReader streamReader = new InputStreamReader(body);

                try (BufferedReader lineReader = new BufferedReader(streamReader)) {
                    StringBuilder responseBody = new StringBuilder();

                    String line;
                    while ((line = lineReader.readLine()) != null) {
                        responseBody.append(line);
                    }

                    return responseBody.toString();
                } catch (IOException e) {
                    throw new RuntimeException("API 응답을 읽는 데 실패했습니다.", e);
                }
            }

먼저 도서 검색한 데이터들을 Pasing에 성공한 것을 확인할 수 있다. 그런데 JSON형태의 값으로 나타나지 않아서 직접 JSON형태로 만들어야하나? 라는 단순한 생각을 시작으로 했다. 이렇게 단순한 생각을 시작으로 다른 방식을 찾다가 JSON 형태가 Map의 Key, Value 형태이기 때문에 Map으로 Parsing 해보기로 했다.


데이터 Parsing 코드 수정

        public ResponseDto<?> searchResult(String keyword){

            String bookTitle = null;

            try {
                bookTitle = URLEncoder.encode(keyword, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException("검색어 인코딩 실패",e);
            }

            String apiURL = "https://openapi.naver.com/v1/search/book?query=" + bookTitle + "&display=" + 5 + "&start=" + 1;


            Map<String, String> requestHeaders = new HashMap<>();
            requestHeaders.put("X-Naver-Client-Id", clientId);
            requestHeaders.put("X-Naver-Client-Secret", clientSecret);
            String responseBody = get(apiURL,requestHeaders);

            List<Map<String, Object>> searchList = new ArrayList<>();


            try{
                JSONParser jsonParser = new JSONParser();
                JSONObject jsonObject = (JSONObject) jsonParser.parse(responseBody);
                JSONArray items = (JSONArray) jsonObject.get("items");

                for(int i = 0; i < items.size(); i++){
                    JSONObject tmp = (JSONObject) items.get(i);
                    String title = (String) tmp.get("title");
                    String link = (String) tmp.get("link");
                    String image = (String) tmp.get("image");
                    String author = (String) tmp.get("author");
                    String publisher = (String) tmp.get("publisher");
                    String isbn = (String) tmp.get("isbn");

                    Map<String, Object> bookInfo = new HashMap<>();

                    bookInfo.put("image", image);
                    bookInfo.put("title", title);
                    bookInfo.put("isbn", isbn);
                    bookInfo.put("no", i+1);

                    searchList.add(bookInfo);
                    
                }
            } catch (ParseException e) {
                throw new RuntimeException("Parsing Exception");
            }

            return ResponseDto.success(searchList);
        }

        private static String get(String apiUrl, Map<String, String> requestHeaders){
                HttpURLConnection con = connect(apiUrl);
                try {
                    con.setRequestMethod("GET");
                    for(Map.Entry<String, String> header :requestHeaders.entrySet()) {
                        con.setRequestProperty(header.getKey(), header.getValue());
                    }


                    int responseCode = con.getResponseCode();
                    if (responseCode == HttpURLConnection.HTTP_OK) { // 정상 호출
                        return readBody(con.getInputStream());
                    } else { // 오류 발생
                        return readBody(con.getErrorStream());
                    }
                } catch (IOException e) {
                    throw new RuntimeException("API 요청과 응답 실패", e);
                } finally {
                    con.disconnect();
                }
            }


        private static HttpURLConnection connect(String apiUrl){
                try {
                    URL url = new URL(apiUrl);
                    return (HttpURLConnection)url.openConnection();
                } catch (MalformedURLException e) {
                    throw new RuntimeException("API URL이 잘못되었습니다. : " + apiUrl, e);
                } catch (IOException e) {
                    throw new RuntimeException("연결이 실패했습니다. : " + apiUrl, e);
                }
            }


        private static String readBody(InputStream body){
                InputStreamReader streamReader = new InputStreamReader(body);

                try (BufferedReader lineReader = new BufferedReader(streamReader)) {
                    StringBuilder responseBody = new StringBuilder();

                    String line;
                    while ((line = lineReader.readLine()) != null) {
                        responseBody.append(line);
                    }

                    return responseBody.toString();
                } catch (IOException e) {
                    throw new RuntimeException("API 응답을 읽는 데 실패했습니다.", e);
                }
            }

responseBody안에 데이터들을 pasing 하여 items안에 있는 검색된 책들을 List와 Map으로 프론트엔드에 필요한 항목들의 값들만 보낼 수 있도록 하게 되었다.

profile
gyery
post-custom-banner

0개의 댓글