BACKEND/JAVA & SPRING

카카오맵 open API - 키워드 검색하기

이-프 2023. 10. 31. 11:17

Toy Project #2에서 카카오맵에서 제공하는 키워드 장소 검색하기 open API를 적용한 경험을 남겨본다. 🌱

왜 카카오맵 open API 중 키워드 검색하기를 사용했는가?

Toy Project #2의 요구사항 중 “일정 정보에 위치 정보를 확인 및 추가하는 공통 유틸리티 클래스 제공”이 있었다. 이를 위해 일정 정보를 추가할 때, 해당 일정 장소를 검색하여 키워드를 토대로 위치정보를 확인하기 위해 카카오맵 open API를 사용했다.

패키지 구조

Toy Project #2는 Spring Boot를 사용하여 MVC구조에 맞춰 진행됐다. 카카오맵 open API의 패키지 구조는 다음과 같다.

domain
	└── itinerary
	    ├── controller
	    │   └── ItineraryRestController  
	    ├── dto    
	    │   └── ItinerarySearchResponseDTO   
	    └── service 
       	  └── ItineraryService  

ItineraryRestController

Restful Web Service에서 사용되는 @RestController를 선택하여 RESTful한 API를 구성했다.

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/itineraries")
public class ItineraryGetDeleteController {

    private final ItineraryGetDeleteService itineraryGetDeleteService;

    @GetMapping("/keyword/{query}")
    public ResponseEntity<ResponseDTO<List<ItinerarySearchResponseDTO>>> getPlaceByKeyword(
        @PathVariable String query)
        throws Exception {
        return ResponseEntity.status(HttpStatus.OK).body(
            ResponseDTO.res(HttpStatus.OK, itineraryService.getPlaceByKeyword(query),
                "성공적으로 키워드로 장소를 조회했습니다."));

    }
}
  • ResponseDTO를 사용하여 code, message, data 순으로 Response 형태를 지정해주었다.
  • query를 Path Variable로 받아와서 이를 itineraryService에 넘긴다.

ItinerarySearchResponseDTO

DTO를 활용하여 반환해야하는 데이터를 지정한다. 이번 프로젝트에서는 장소명, 도로명, 장소URL만 받아와서 반환하기로 했다.

@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ItinerarySearchResponseDTO {

    private String placeName;
    private String roadAddressName;
    private String placeUrl;
}

ItineraryService

Kakao open API의 전반적인 로직이 담긴 Service이다.

@Service
@Transactional
@RequiredArgsConstructor
public class ItineraryGetDeleteService {

    private final ItineraryRepository itineraryRepository;

    @Value("${kakao-api.api-url}")
    private String uri;

    @Value("${kakao-api.api-key}")
    private String key;

    private HttpEntity<String> httpEntity;

    /**
     * Kakao Open API [키워드 검색하기] 를 위한 httpEntity를 생성하는 메서드
     */
    @PostConstruct
    protected void init() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set(HttpHeaders.AUTHORIZATION, "KakaoAK " + key);
        httpEntity = new HttpEntity<>(headers);
    }

    /**
     * qeury를 통해 Kakao Open API [키워드 검색하기] 결과값을 생성하는 메서드
     *
     * @param query 키워드
     * @return 장소명, 도로명, 장소 url이 담긴 List
     */
    public List<ItinerarySearchResponseDTO> getPlaceByKeyword(String query) throws Exception {
        URI tmp = UriComponentsBuilder.fromHttpUrl(uri)
            .queryParam("query", query)
            .queryParam("page", 5)
            .encode(StandardCharsets.UTF_8)
            .build().toUri();

        Assert.notNull(query, "query");
        ResponseEntity<String> response = new RestTemplate().exchange(tmp, HttpMethod.GET,
            httpEntity, String.class);

        JSONObject jsonObject = new JSONObject(response.getBody().toString());
        JSONArray jsonArray = jsonObject.getJSONArray("documents");
        List<ItinerarySearchResponseDTO> itinerarySearchList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            JSONObject documentObj = jsonArray.getJSONObject(i);
            itinerarySearchList.add(
                ItinerarySearchResponseDTO.builder().placeName(documentObj.getString("place_name"))
                    .roadAddressName(documentObj.getString("road_address_name"))
                    .placeUrl(documentObj.getString("place_url")).build());
        }
        return itinerarySearchList;
    }
}
  • @Value("${kakao-api.api-url}")

    kakao API가 제공하는 URL 및 API KEY는 공개되면 안되므로 application-secret.yaml파일에 따로 보관한다. 이는 github와 같은 공개된 공간에도 올리면 안되므로 .gitignore에 해당 파일을 추가해둔다. 또한, Github CI Test에선 통과가 되야하므로 Github 내에서 Secret Key를 등록해둔다.

  • @PostConstruct

    open API request를 위한 Header를 구성하여 HttpEntity를 미리 생성한다. 이 Header에는 인증을 위한 API Key를 담아준다.

  • public List<ItinerarySearchResponseDTO> getPlaceByKeyword(String query)
    • UriComponentsBuilder를 활용하여 필요한 parameter들이 포함된 URI를 형성한다.
    • query가 NULL이 아님을 검사한다.
    • RestTemplate() : Spring에서 제공하는 HTTP 통신을 쉽게 처리하기 위한 클래스
    • exchange() : HTTP 요청을 보내는 메서드
      • URI, HttpMethod.GET(GET요청), httpEntity(헤더정보), String.class(응답형태)
    • ResponseEntity로 응답을 받은뒤, jsonObject와 jsonArray를 활용하여 필요한 부분만 parsing한다.
    • 결과값을 ItinerarySearchResponse에 넣어 build한뒤 이를 List에 추가하여 최종 반환한다.

결과(Postman Test)

결과적으로 query에 해당하는 원하는 데이터만 반환받을 수 있었다.

🔗 참고 URL


Uploaded by N2T

'BACKEND > JAVA & SPRING' 카테고리의 다른 글

올바른 URI 설계 - Path Variable 과 Query Parameter  (1) 2023.10.31
@Controller vs @RestController  (0) 2023.10.31
싱글톤 패턴이란?  (0) 2023.09.26
객체지향 프로그래밍이란?  (0) 2023.09.26
TestCode 작성법  (0) 2023.09.18