Spring으로 외부 API 호출하기 - (4) 카카오톡 퀵 API 분석하기 (주문, 가격 조회하기)

sliver gun·2024년 12월 14일

egovFramework

목록 보기
11/14

🔰목차

  1. 서론
  2. 주문 조회 과정
    1. 호출방식
    2. 요청 헤더
    3. 요청 파라미터
    4. 요청 예시코드
    5. 요청 응답
  3. 주문 조회 적용하기
  4. 가격 정보 조회 과정
    1. 호출방식
    2. 요청 헤더
    3. 요청 예시코드
    4. 요청 응답
  5. 가격 조회 적용하기
  6. 결론
  7. 레퍼런스

서론

지난 편에서는 주문 정보를 전달하는 POST 요청을 보내고 응답을 jsp파일에서 띄우는 것까지 진행해보았다. 이번에는 주문 조회와 가격 조회 기능을 추가하는 내용에 대해 적어보도록 하겠다.


주문 조회 과정

호출방식

[API_ENDPOINT]/api/v2/orders/[partnerOrderId]으로 GET 요청한다.

요청 헤더

ParameterDescription
Authorization시작하기에서 생성한 Authorization 파라미터 값
vendorkakaoT 퀵∙도보 배송 API Sandbox에서 발급받은 Vendor 아이디
Content-Typeapplication/json

주문 정보 요청과 같은 헤더 값이다.

왠만한 요청에는 Authorization 값이 헤더로 들어가기 때문에 따로 함수를 만들어 관리하기로 했다.

public String getAuthorization() throws InvalidKeyException, NoSuchAlgorithmException {
		// 입력값
    final String timestamp = String.valueOf(System.currentTimeMillis());
    final String nonce = "121212";
    final String apiKey = apiConfig.getKeyValue();
    System.out.println("tesT");

    // 서명 및 Authorization 생성
    String sign = kakaoAuthComponent.generateSignature(timestamp, nonce, apiKey);
    String authorization = kakaoAuthComponent.generateAuthorization(timestamp, nonce, sign);
    
    return authorization;
}

요청 파라미터

NameTypeDescriptionRequired
partnerOrderIdString연동사 주문 아이디필수

요청 파라미터는 다음과 같이 연동사의 주문 아이디 하나만 필요하다.

그 주문 아이디는 호출 방식에서 API_ENDPOINT에 포함되며 @PathVariable로 쓸 수 있다.

요청 예시코드

curl -X 'GET' 
  'https://open-api-logistics.kakaomobility.com/goa-sandbox-service/api/v2/orders/${partnerOrderId}' 
  -H 'accept: application/json' 
  -H 'vendor: ${vendor_id}' 
  -H 'Authorization: XXX' 

GET 요청이기 때문에 따로 Body는 없다.

요청 응답

요청응답은 다음과 같이 주문 정보와 같은 JSON 형식으로 받을 수 있다.

{
  "requestId": ${requestId},
  "partnerOrderId": ${partnerOrderId},
  "pickup": {
    "location": {
      "basicAddress": "서울 강남구 역삼동 xxx",
      "detailAddress": "1층",
      "latitude": 37.4354059,
      "longitude": 126.74551
    },
    "contact": {
      "name": "전달하는 사람 이름",
      "phone": "010-1000-0001"
    },
    "statusUpdatedAt": "2023-02-27T15:40:32+09:00"
  },
  "dropoff": {
    "location": {
      "basicAddress": "서울 강남구 일원동 xxx",
      "detailAddress": "2층",
      "latitude": 37.569691,
      "longitude": 126.825791
    },
    "contact": {
      "name": "받는 사람 이름",
      "phone": "010-1000-0002"
    },
    "statusUpdatedAt": "2023-02-27T15:40:32+09:00"
  },
  "receipt": {
    "orderId": ${orderId},
    "orderType": ${orderType},
    "status": "MATCHING",
    "priceInfo": {
      "totalPrice": 100000,
    },
  },
  "histories": [
    "status": "MATCHING",
    "updatedAt": "2023-02-27T15:40:32+09:00"
  ]
}

주문 조회 적용하기

Authorization 인증 요청과 비슷하게 GET 요청을 위한 함수를 작성한다.

여기서 미리 만들어 놓은 getAuthorization() 함수를 써 코드를 단축하였다.

@GetMapping("/order/{partnerOrderId}.do")
public String orderCheck(@PathVariable String partnerOrderId, Model model) {
	try {
		ApiConfig apiconfig = ApiConfig.getApiConfigSingleton();
		
		String authorization = getAuthorization();
		String vendorID = apiConfig.getVendorID();
		
		// API 호출을 위한 URL 설정
    String apiUrl = apiConfig.getHostURL() + "/api/v2/orders/" + partnerOrderId;

    // HttpHeaders 객체 생성
    HttpHeaders headers = new HttpHeaders();
    headers.set("accept", "application/json");
    headers.set("Authorization", authorization);  // 동적으로 생성된 Authorization
    headers.set("Content-Type", "application/json");
    headers.set("vendor", apiConfig.getVendorID());  // vendor_id 설정

    // HttpEntity 생성 (헤더 포함)
    HttpEntity<String> entity = new HttpEntity<>(headers);

    // GET 요청 보내기
    ResponseEntity<String> response = restTemplate.exchange(apiUrl, HttpMethod.GET, entity, String.class);
    
    // 응답 처리
    model.addAttribute("responseCode", response.getStatusCodeValue());
    model.addAttribute("responseBody", response.getBody());
          
	} catch (Exception e) {
		e.printStackTrace();
		model.addAttribute("responseBody", "API 요청 중 오류 발생: " + e.getMessage());
  }
	
	return "result";
}

이걸 통해 받는 result.jsp는 Authorization 인증 때와 똑같이 통일했다.

<body>
    <h1>Order Result</h1>
    <p>Response Code: ${responseCode}</p>
    <p>Response Body: ${responseBody}</p>
</body>

여기서 필요한 것은 partnerOrderId를 입력받고 GET 요청을 보내는 페이지다.

<form id="orderCheck" method="get">
	<label for="orderCheck">파트너 ID</label>
	<input type="text" id="partnerOrderId" name="partnerOrderId">
	<button type="button" onclick="submitForm()">조회</button>
</form>

위와 같이 text로 입력받아 id=”partnerOrderId”로 넘겨주도록 했다.

function submitForm() {
	const form = document.getElementById("orderCheck");
	const ID = document.getElementById("partnerOrderId").value;
	
	if (ID) {
		form.action = '/order/' + ID + '.do';
		form.method = 'GET';
		form.submit();
	} else {
		alert("Invaild ID");
	}
}

이런식으로 사용자가 입력한 partnerOrderId 를 받고 form.action으로 설정한 뒤 form을 제출한다.

여기서 .do를 넣게 되는데 그 이유는 web.xml에서 filter-mapping을 .do로 설정했기 때문이다.

<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>

여기서 문제가 생기는데

~~@GetMapping("/order/{partnerOrderId}.do") 여기서 {partnerOrderId}를 하드 코딩 했을 때는 정상적으로 200 응답이 오지만 PathVariable로 받으려고 하면 404가 뜨는 상황이다.~~

문제가 무엇인지 찾았다.

이 프로젝트는 context 경로가 /kakaoTest로 되어있지만 jsp 파일에서는 그걸 포함하지 않고 바로 /order/… 로 보냈기에 주소가 달라서 404 에러가 뜨는 것이었다.

아무튼 이런 식으로 200 요청 코드를 받을 수 있었다.


가격 정보 조회 과정

호출방식

[API_ENDPOINT]/api/v2/orders/pricePOST 요청한다.

요청 헤더

이 헤더는 위 내용과 같습니다.

요청 예시코드

curl -X 'POST' 
  'https://open-api-logistics.kakaomobility.com/goa-sandbox-service/api/v2/orders/price' 
  -H 'accept: application/json' 
  -H 'vendor:${vendor_id}'  
  -H 'Content-Type: application/json' 
  -H 'Authorization: XXX' 
  -d '{
  "orderType": "QUICK",
  "pickup": {
    "location": {
      "basicAddress": "서울특별시 강남구 역삼동 754",
      "detailAddress": "1층",
      "latitude": 37.498551,
      "longitude": 127.0464387
    }
  },
  "dropoff": {
    "location": {
      "basicAddress": "서울특별시 강남구 일원동 741",
      "detailAddress": "2층",
      "latitude": 37.490694420048,
      "longitude": 127.079607
    }
  },
  "productSize": "XS"
}'

주문 정보 요청 하기의 일부 정보를 받아 POST를 받는다

요청 응답

요청응답은 다음과 같이 물품 가격을 JSON 형식으로 받을 수 있다.

{
  "totalPrice": 17800
  "maximum": 17800 // 도보 min max
  "minimum": 15000 // 퀵은 null
}

가격 조회 적용하기

가격 조회에 필요한 body를 사용자로부터 받아내는 페이지는 주문정보 전달하기에서 일부만 수정해서 사용했다.

<body>
    <h1>가격 조회하기</h1>
    <form id="orderForm" method="post" action="/kakaoTest/price.do">
        <label for="orderType">주문 유형:</label><br>
        
        ...

        <button type="submit">Submit Order</button>
    </form>
</body>

그리고 price.do 에서 처리하도록 Controller도 구성했으며 대부분 주문 정보 전달하기의 order.do 와 유사하다.

@RequestMapping(value = "/price.do", method = RequestMethod.POST)
public String CheckPrice(OrderVO orderVO, Model model) {
	try {
      	final String API_ENDPOINT = apiConfig.getHostURL() + "/api/v2/orders/price";
        final String VENDOR_ID = apiConfig.getVendorID();
        
        final String authorization = getAuthorization();
          
        JSONObject json = new JSONObject();

        ...

        // 최종 JSON 객체에 데이터 삽입
        json.put("orderType", orderVO.getOrderType());
        json.put("pickup", pickup);
        json.put("dropoff", dropoff);
        json.put("productSize", orderVO.getSize());
          
          System.out.println("Generated JSON: " + json.toString());
          
          // HTTP Header 설정
          HttpHeaders headers = new HttpHeaders();
          headers.set("Content-Type", "application/json");
          headers.set("Authorization", authorization);
          headers.set("vendor", VENDOR_ID);

          // HTTP 요청 생성
          HttpEntity<String> requestEntity = new HttpEntity<>(json.toString(), headers);
          System.out.println("Authorization : " + authorization);
          
          // API 호출
          ResponseEntity<String> response = null;
          try {
              response = restTemplate.postForEntity(API_ENDPOINT, requestEntity, String.class);

              System.out.println("Response Status Code: " + response.getStatusCode());
              System.out.println("Response Body: " + response.getBody());
			        
			        ...
			        
          } catch (Exception e) {
              System.out.println("기타 예외 발생");
              e.printStackTrace();
          }

          // 응답 처리
          model.addAttribute("responseCode", response.getStatusCodeValue());
          model.addAttribute("responseBody", response.getBody());
          
          return "result";

      } catch (Exception e) {
          e.printStackTrace();
          System.out.println("Error: " + e.getMessage());
          
          return "result";
      }
}

jsp에서 받은 body 정보를 JSONObject화 헤더값과 함께 POST 요청으로 보냈고, 늘 응답받던 result.jsp에서 해당 결과를 받도록 설정했다.


결론

생각보다 어이없는 이유로 생긴 에러때문에 시간을 많이 잡아먹히고 있다.

일단 지금은 간단히 요청을 보내고 받는 부분까지만 하고 있지만 나중엔 UI/UX를 개선하고 RestAPI까지 구성할 계획이다.


레퍼런스

https://logistics-developers.kakaomobility.com/document/get-orders

0개의 댓글