Blog

[Spring]48 NAVER ‘Searching’ Open API Test

Category
Author
Tags
PinOnMain
1 more property
developers.naver.com
네이버의 개발자 플랫폼을 통해서는 네이버가 만드는 각종 Open API를 활용 할 수 있다.
기본적으로 네이버의 부가 기능들과 연결되어 있으며 목록은 다음과 같다.
API명
설명
호출제한
검색
네이버 블로그, 이미지, 웹, 뉴스, 백과사전, 책, 카페, 지식iN 등 검색
25,000회/일
네이버 로그인
외부 사이트에서 네이버 로그인 기능 구현
없음
네이버 회원 프로필 조회
네이버 회원 이름, 이메일 주소, 휴대전화번호, 별명, 성별, 생일, 연령대, 출생연도, 프로필 조회
없음
Papago 번역
Papago 번역 인공신경망 기반 기계 번역
10,000글자/일
CLOVA Face Recognition
입력된 사진을 입력받아 얼굴윤곽/부위/표정/유명인 닮음도를 리턴
1,000건/일
데이터랩(검색어트렌드)
통합검색어 트렌드 조회
1,000회/일
데이터랩(쇼핑인사이트)
쇼핑인사이트 분야별 트렌드 조회
1,000회/일
캡차(이미지)
자동 입력 방지용 보안 이미지 생성 및 입력값 비교
1,000회/일
캘린더
로그인한 사용자 캘린더에 일정 추가 가능
5,000회/일
카페
특정 네이버 카페 가입하기
50회/일
카페
네이버 회원이 가입한 카페 게시판에 글 쓰기
200회/일
단축URL
입력된 URL을 me2.do 형태의 짧은 URL로 변환
25,000회/일
공유하기
네이버 블로그, 카페 공유하기
없음
우선 눈에 들어오는 기능들
검색, 네이버 로그인, 네이버 회원 프로필 조회, Papago 번역, 검색어 트렌드 조회, 쇼핑인사이트 분야별 트렌드 조회, 캡차, 캘린더 기능은 웹 서비스에서 자주 사용될 것 같다. 이중에 네이버 검색 요청 API를 SpringBoot으로 구현하고자 한다.
예전에 CLOVA 기능들을 잠시 살펴본적이 있었는데 보다 많은 기능들이 있었다. AI관련 기능도 있었으며 데이터사이언스 분야의 기능들도 있었던것으로 기억한다. 종류는 생각보다 아주 많아서 융복합 아이디어를 구상 할 때 핵심적으로 사용 될 듯 하다.
그 외 다음의 주소검색 API도 유명한것 같고, 아마도 CLOVA안에 있는듯 한데 지도 및 GPS기능 등이 있을 것이다. 카카오도 주력 서비스들과 연관된 Open API를 가지고 있으며 중복되는 것들도 많다. 사실 어떤 기능을 선택 하는것은 어떤 서비스를 만들 지 계획하고 기획 단계에서 생각할 문제이다.
나는 클라이언트 입장이다.
실제로 요청을 보내는 시작점은 내 서버로부터 시작된다.
바로 전 실습했던 내용 중 내가 클라이언트 입장이다.
네이버가 서버 역할을 하게 된다. 뭔 API 메소드가 되있는지 알 수 없다. 하지만 그 요청 헤더, 바디, 방식 등 모든 것들은 해당 API 도큐먼트에서 지정하고 있다. 그 양식에 맞도록 요청을 보내고 반환 타입을 설계해두면 된다.
나는 요청을 보내고 최종 결과를 받는 API를 개발하면 된다.
우선 NAVER Developers에서 애플리케이션을 생성
사용할 API를 선택하고 (”검색 기능”)
환경 설정(”WEB”)
서비스 URL(”http://localhost”)
이렇게 하면 기본적인 설정은 종료된다. 여기서 중요한 것은 Client ID, Client Secret 이며 요청 시 헤더에 해당 정보를 담아서 요청해야 데이터를 받을 수 있다.
프로젝트에서 Client ID와 Client Secret을 숨기자.
Github로 해당 비밀키들을 노출 시키는 순간 해킹 또는 테러의 목표가 될 수 있다. 그냥 요청만 수만번 보내버리면 해당 사용자 계정은 모든 무료 사용량을 다 소모 할 수도 있으며, 유료 API 요청인 경우엔 과금으로도 이어 질 수 있다.
따라서 프로젝트를 Github를 통해 공유 할 때 이러한 비밀키들은 숨겨둘 필요가 있다. 하지만 아래 코드에서 나타나겠지만 헤더에 입력하려면 입력해야 한다. 어떻게 해결할까?
바로 여기서도 DI를 이용하면 된다. 우리는 application.properties 파일로부터 다양한 설정들을 프로젝트 전반에 적용 받고 있다. 그 이유는 말 그대로 프로젝트 설정 파일이기 때문이다. 동일한 원리로 적용 시키고 그 값들을 불러오기만 하면된다. 그런데 키를 어떻게 관리하는가? application.properties 마저도 팀원들과 공유 될 수도 있고.. 해결 방법은
main/resources/ application-API-KEY.properties 파일을 만든다.
.gitignoreapplication.properties , application-API-KEY.properties 를 작성해둔다.(미리 안작성하면 또 git rm해야되니 잘 모르면 그냥 이 순서대로 한다.)
application-API-KEY.properties
... !**/src/test/**/build/ # 아래처럼 application.properties application-API-KEY.properties # 이렇게 아무대나 그냥 넣어도 된다 ### STS ### .apt_generated .classpath .factorypath ...
Java
복사
application-API-KEY.properties 을 열어서 아래와 같이 Naver Developers에서 애플리케이션에 나타나는 Client ID, Client Secret를 입력
naver.client.id="_adflkjsfdkjaf" naver.client.secret="DKALKSKD9a1"
Java
복사
application.properties 를 열어서 이 부분을 추가한다. 이러면 위 파일 application-API-KEY.properties도 똑같이 프로젝트 설정 파일처럼 작동하게 된다. 중요한 키들을 저기에 보관해도 된다.(무조건 .gitignore가 정상 등록되어 추적제외중인지 확인해야 한다. 그리고 이것 조차도 아주 기본적인 보안 방안일 뿐이고, 로컬에 남아있기 때문에 보다 치밀하게 작업해야 할 필요도 있다.)
spring.profiles.include=API-KEY
Java
복사
이제 사용하기 원하는 클래스에서 아래 코드처럼 Dependency Injection으로 주입하고 사용하면 된다. 그러기 위해서는 파일의 내용을 읽을 수 있도록 설정해주어야 한다. 또한 Dto 클래스 처럼 해당 키들의 클래스와 생성자, 값들을 얻어 올 수 있는 @Getter 메소드가 필요하다.
@ConfigurationProperties(prefix = "naver.client") 이 부분이 위 키들을 가리키게 해준다.
ApiKey.java
@Getter @Configuration @ConfigurationProperties(prefix = "naver.client") public class ApiKey { private String naver_client_id; private String naver_client_secret; // Getter 메서드 public String getId() { return naver_client_id; } public String getSecret() { return naver_client_secret; } @Override public String toString() { return "ApiKey [apiKey=" + naver_client_id + "]"; } }
Java
복사
우리가 다른 클래스나 기능들을 DI하던 것과 똑같다. 다만 Builder를 통해서 해야하는 점 정도가 특이한것일 뿐 그냥 똑같다.
@Slf4j(topic = "NAVER API") @Service public class NaverApiService { ... private final ApiKey apiKey; @Autowired public NaverApiService(RestTemplateBuilder builder, ApiKey apiKey) { this.restTemplate = builder.build(); this.apiKey = apiKey; // apiKey 필드를 생성자에서 초기화 } ...
Java
복사
이렇게 되면 위 클래스에서 실제로 이 코드가 사용되는 부분에는 직접적인 Secret Key를 쓰지 않고 Getter를 통해서 값만 가져와서 사용 하게 된다.
RequestEntity<Void> requestEntity = RequestEntity .get(uri) .header("X-Naver-Client-Id", apiKey.getId()) .header("X-Naver-Client-Secret", apiKey.getSecret()) .build();
Java
복사
이제 본격적으로 네이버의 검색 API를 사용해보자.
요청을 보내는 (나-Client) Controller는 다음과 같다.
@RestController @RequestMapping("/api") public class NaverApiController { private final NaverApiService naverApiService; public NaverApiController(NaverApiService naverApiService) { this.naverApiService = naverApiService; } @GetMapping("/search") public List<ItemDto> searchItems(@RequestParam String query) { return naverApiService.searchItems(query); } }
Java
복사
GET방식 요청으로 /search 라는 URL에 맵핑했다.
실제 비지니스 로직인 searchItems() 메소드가 있는 Service는 다음과 같다.
@Slf4j(topic = "NAVER API") @Service public class NaverApiService { private final RestTemplate restTemplate; private final ApiKey apiKey; @Autowired public NaverApiService(RestTemplateBuilder builder, ApiKey apiKey) { this.restTemplate = builder.build(); this.apiKey = apiKey; // apiKey 필드를 생성자에서 초기화 } public List<ItemDto> searchItems(String query) { // 요청 URL 만들기 URI uri = UriComponentsBuilder .fromUriString("https://openapi.naver.com") .path("/v1/search/shop.json") .queryParam("display", 15) .queryParam("query", query) .encode() .build() .toUri(); log.info("uri = " + uri); RequestEntity<Void> requestEntity = RequestEntity .get(uri) .header("X-Naver-Client-Id", apiKey.getId()) .header("X-Naver-Client-Secret", apiKey.getSecret()) .build(); ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity, String.class); log.info("NAVER API Status Code : " + responseEntity.getStatusCode()); return fromJSONtoItems(responseEntity.getBody()); } public List<ItemDto> fromJSONtoItems(String responseEntity) { JSONObject jsonObject = new JSONObject(responseEntity); JSONArray items = jsonObject.getJSONArray("items"); List<ItemDto> itemDtoList = new ArrayList<>(); for (Object item : items) { ItemDto itemDto = new ItemDto((JSONObject) item); itemDtoList.add(itemDto); } return itemDtoList; } }
Java
복사
네이버 서버가 반환해주는 String 타입의 문자열 데이터를 변환해야 한다.
StringJSONObject
첫 부분에서는 JSONObject로 변환하기 위해서 json 라이브러리가 필요하다. build.gradle에 해당 라이브러리가 주입되었는지 확인한다.
// json implementation 'org.json:json:20230227'
Java
복사
JSONObjectDto를 통해서 하나하나 객체로 변환
객체들은 리스트 참조형 변수에 요소로 할당해야 한다. 이 변환 과정은 전 글의 마지막 부분 [4]번 기능에서 다루었던 내용이다.
ItemDto.java
@Getter @NoArgsConstructor public class ItemDto { private String title; private String link; private String image; private int lprice; public ItemDto(JSONObject itemJson) { this.title = itemJson.getString("title"); this.link = itemJson.getString("link"); this.image = itemJson.getString("image"); this.lprice = itemJson.getInt("lprice"); } }
Java
복사