본문 바로가기
JSP/성낙현의 JSP 자바 웹 프로그래밍

네이버 검색 API를 활용한 검색 결과 출력하기

by k-mozzi 2023. 7. 6.
반응형
Preface

 

이번 장에선 네이버의 검색 API를 활용하여 검색 결과를 출력하는 화면을 만들었다.

 

오픈 API를 활용하는 방법은 이미 유튜브에서 여러 번 접해서 익숙하지만,

 

자바스크립트와 jQeury Ajax를 사용하는 코드는 아직도 조금씩 헷갈린다.


 

1. API란?

 

 

- API(Application Programming Interface): 응용 프로그램 간에 상호 작용하기 위한 규칙과 기능을 제공하는 인터페이스

→ 소프트웨어 개발에서 다른 응용 프로그램이나 서비스와 상호 작용할 수 있도록 도와주는 중간 계층

 

 

- 오픈 API: 서비스를 제공하는 업체에서 외부 개발자가 자사 서비스의 기능을 간단히 호출해 이용할 수 있도록 공개해둔 API

1) 주로 HTTP 프로토콜로 통신한다.

2) 응답 데이터의 형태는 JSON 포맷을 주로 사용한다.

 

 

- 네이버 검색 API는 검색 결과를 XML이나 JSON으로 반환한다.

 

 

- JSON(JavaScript Object Notation): JS에서 객체나 배열을 만들 때 사용하는 표현 방식

→ 중괄호는 객체를, 대괄호는 배열을 뜻한다.

 

 

- JSON의 형식: 4가지의 기본 형식을 조합하여 사용할 수 있다.

1) '객체'는 키와 값으로 구분

{
	"name" : "k-mozzi", "age" : 24, "address" : "Daejeon"
}

2) '배열'은 값만으로 구성

[
	"Java". "Oracle", "HTML", "JSP"
]

3) 객체의 값으로 배열을 사용

{
	"firstName" : "Must", "lastName" : "Have",
    "Books" : ["JAVA", "JSP", "GO"]
}

4) 배열의 값으로 객체를 사용

[
	{"City" : "서울", "HotPlace" : "이태원"},
    {"City" : "부산", "HotPlace" : "해운대"},
    {"City" : "대구", "HotPlace" : "삼덕동"},
]

 


 

2. 오픈 API 이용 신청

 

 

- '네이버 개발자센터'에 접속하여 'Products'에서 원하는 API를 클릭한 후 애플리케이션 등록을 완료하면 된다.

1) ClientID와 ClientSecret은 따로 메모해두는 것이 좋다.

2) '상세 설명'에서 해당 API에 대한 자세한 정보를 얻을 수 있다.

3) 요청 변수: 요청 URL에 쿼리스트링으로 덧붙일 수 있는 변수

 


 

3. 구현

 

 

- 검색 서블릿

→ API 예제의 main 메소드 코드는 service 메소드로 복사한 후 수정했고, 나머지 코드는 그대로 가져와 사용했다.

package api;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/NaverSearchAPI.do")
public class SearchAPI extends HttpServlet {

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse resp) throws SecurityException, IOException {
		String clientId = ""; // 애플리케이션 클라이언트 아이디
		String clientSecret = ""; // 애플리케이션 클라이언트 시크릿

		int startNum = 0;
		String text = null;
		try {
			startNum = Integer.parseInt(req.getParameter("startNum"));
			String searchText = req.getParameter("keyword");
			text = URLEncoder.encode(searchText, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("검색어 인코딩 실패", e);
		}

		String apiURL = "https://openapi.naver.com/v1/search/blog?query=" + text + "&display=10&start=" + startNum; // JSON
																													// 결과
		// String apiURL = "https://openapi.naver.com/v1/search/blog.xml?query="+ text;
		// // XML 결과

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

		System.out.println(responseBody);
		resp.setContentType("text/html; charset=utf-8");
		resp.getWriter().write(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);
		}
	}
}

 

 

- 검색 화면 페이지

1) HTML, CSS, JS, JSP(jQuery Ajax)를 사용했다.

2) JS 코드에 있는 $.each( ) 메소드는 반복문을 생성하는 메소드이다.

1. DOM 요소를 선택한 후 반복 실행
$('선택자').each(function(index, item) {
	<!-- 선택한 요소의 반복 실행 문장 -->
}

<!-- 선택자: DOM 선택 후 반복 -->
<!-- index: 반복 요소의 인덱스(0부터 시작) -->
<!-- item: 반복 요소의 데이터 -->

2. 배열을 첫 번째 인수로 받아 반복 실행
$.each(배열, function(index, item)) {
	<!-- 선택한 요소의 반복 실행 문장 -->
}

<!-- index: 배열 요소의 인덱스(0부터 시작) -->
<!-- item: 배열 요소의 데이터 -->​​
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>검색 API</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>

<script>
// [검색 요청] 버튼 클릭 시 실행할 메서드를 정의합니다.
// HTML 문서의 로딩이 완료된 후에 내부의 코드가 실행되도록 하는 역할
$(function() {
    $('#searchBtn').click(function() {
      $.ajax({
        url : "../../NaverSearchAPI.do",  // 요청 URL
        type : "get",                  // HTTP 메서드
        data : {                       // 매개변수로 전달할 데이터
            keyword : $('#keyword').val(),                   // 검색어
            startNum : $('#startNum option:selected').val()  // 검색 시작 위치
        },
        dataType : "json",      // 응답 데이터 형식
        success : sucFuncJson,  // 요청 성공 시 호출할 메서드 설정
        error : errFunc         // 요청 실패 시 호출할 메서드 설정
      });
    });
});

// 검색 성공 시 결과를 화면에 뿌려줍니다.
function sucFuncJson(d) {
    var str = "";
    $.each(d.items, function(index, item) {
        str += "<ul>";
        str += "    <li>" + (index + 1) + "</li>";
        str += "    <li>" + item.title + "</li>";
        str += "    <li>" + item.description + "</li>";
        str += "    <li>" + item.bloggername + "</li>";
        str += "    <li>" + item.bloggerlink + "</li>";
        str += "    <li>" + item.postdate + "</li>";
        str += "    <li><a href='" + item.link + "' target='_blank'>바로가기</a></li>";
        str += "</ul>";
    });
    $('#searchResult').html(str);
}

// 실패 시 경고창을 띄워줍니다.
function errFunc(e) {
    alert("실패: " + e.status);
}
</script>

<style>
    ul{border:2px #cccccc solid;}
</style>
</head>
<body>
<div>
    <div>
        <form id="searchFrm">
            한 페이지에 10개씩 출력됨 <br />
            <select id="startNum">
                <option value="1">1페이지</option>
                <option value="11">2페이지</option>
                <option value="21">3페이지</option>
                <option value="31">4페이지</option>
                <option value="41">5페이지</option>
            </select>
            <input type="text" id="keyword" placeholder="검색어를 입력하세요." />
            <button type="button" id="searchBtn">검색 요청</button>
        </form>
    </div>
    <div class="row" id="searchResult">
        여기에 검색 결과가 출력됩니다.
    </div>
</div>
</body>
</html>

 

728x90
반응형

댓글