Spring으로 외부 API 호출하기 - (3) 카카오톡 퀵 API 분석하기 (주문 정보 전달하기)

sliver gun·2024년 12월 7일

egovFramework

목록 보기
10/14

🔰목차

  1. 서론
  2. 주문 정보 요청 과정
  3. 결론
  4. 레퍼런스

서론

지난 편에서는 API를 활용하기 위해 필요한 Authorization 값을 인증하는 과정을 적어보았다.

이번엔 그 Authorization값을 기반으로 주문 정보를 전달하는 과정을 적어보도록 하겠다.


주문 정보 요청 과정

호출방식

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

요청 헤더

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

헤더는 이전에 만들고 인증받은 Authorization과 vendor ID, Content-Type을 포함한다.

요청 파라미터

NameTypeDescriptionRequired
orderTypeEnum(String)주문 유형다음 중 하나:QUICK: 퀵QUICK_ECONOMY: 퀵 이코노미QUICK_EXPRESS: 퀵 급송DOBO: 도보 배송필수
partnerOrderIdString연동사 주문 아이디(내부에서 사용하시는 주문 아이디)필수
fleetOptionObject차량 선택선택
productInfoObject배송 유의사항필수
pickupObject출발지 정보필수
dropoffObject목적지 정보필수
extraOptionObject기타 옵션선택
ordererContactObject의뢰인 정보선택

요청 body는 다음과 같이 다양하게 존재한다

그마저도 세부 사항을 빼고 넣은 것이다

요청 예시코드

curl -X 'POST' \
  'https://open-api-logistics.kakaomobility.com/goa-sandbox-service/api/v2/orders' \
  -H 'accept: application/json' \
  -H 'vendor:${vendor_id}'  \
  -H 'Content-Type: application/json' \
  -H 'Authorization: XXX' \
  -d '{
  "partnerOrderId": "{연동사 주문 ID - 유니크 해야합니다}",
  "orderType": "QUICK",
  "pickup": {
    "location": {
      "basicAddress": "서울특별시 강남구 역삼동 xxx",
      "detailAddress": "1층",
      "latitude": 37.4354059,
      "longitude": 126.74551
    },
    "contact": {
      "name": "전달하는 사람 이름",
      "phone": "010-1000-0001"
    }
  },
  "dropoff": {
    "location": {
      "basicAddress": "서울특별시 강남구 일원동 xxx",
      "detailAddress": "2층",
      "latitude": 37.569691,
      "longitude": 126.825791
    },
    "contact": {
      "name": "받는 사람 이름",
      "phone": "010-1000-0002"
    }
  }
  "productInfo": {
    "trayCount": 1,
    "size": "XS",
    "totalPrice": 29800,
    "products": [
      {
        "name": "양념게장 (대)",
        "quantity": "1",
        "price": 29800,
        "detail": "터지면 큰일남"
      }
    ]
  }
}'

예시 코드와 같이 jsp에서 받은 파라미터를 JSON으로 변환해서 보내야 할 것 같다.

요청 응답

응답은 다음과 같은 형식으로 온다.

{
  "requestId": ${requestId},
  "partnerOrderId": ${partnerOrderId},
  "receipt": {
    "orderId": ${orderId},
      "orderType": "QUICK",
      "priceInfo": {
        "totalPrice":10000
      },
      "status": "matching"
  }
}
NameTypeDescriptionRequired
requestIdString요청 아이디선택
partnerOrderIdString연동사 주문 아이디선택
receiptObject주문 정보선택

적용하기

이제 주문 정보 전달하기를 적용해보자.

  1. 요청 헤더 값을 전달하기 위해 Authorization, VendorID를 준비한다.
  2. 요청 파라미터에 해당하는 값들을 불러올 jsp 파일을 만든다.
  3. jsp에서 form으로 보내오는 값들을 가져와 Controller에서 처리한다.
  4. 결과값을 보여주는 jsp를 만든다.

헤더 값 설정

호출하기 위해 필요한 URL과 헤더 값들을 준비한다.

try {
	   final String API_ENDPOINT = apiConfig.getHostURL();
     final String VENDOR_ID = apiConfig.getVendorID();
            
     // Authorization 동적으로 생성
     final String timestamp = String.valueOf(System.currentTimeMillis());
     final String nonce = "121212"; // 고유값 (임의로 지정)
     final String apiKey = apiConfig.getKeyValue();
     String sign = kakaoAuthComponent.generateSignature(timestamp, nonce, apiKey);
     final String authorization = kakaoAuthComponent.generateAuthorization(timestamp, nonce, sign);

주문 정보 jsp 설정

요청 파라미터를 form으로 보낼 jsp파일을 만든다.

<body>
    <h1>Create a New Order</h1>
    <form id="orderForm" method="get" action="/hello.do">
        <label for="partnerOrderId">연동사 주문 ID:</label><br>
        <input type="text" id="partnerOrderId" name="partnerOrderId" required><br><br>

        <label for="orderType">주문 유형:</label><br>
        <select id="orderType" name="orderType" required>
            <option value="QUICK"></option>
            <option value="QUICK_ECONOMY">퀵 이코노미</option>
            <option value="QUICK_EXPRESS">퀵 급송</option>
            <option value="DOBO">도보 배송</option>
        </select><br><br>
        
        ...
        
        <label for="productName">상품명:</label><br>
        <input type="text" id="productName" name="productName" required><br>
        <label for="productQuantity">상품 수량:</label><br>
        <input type="number" id="productQuantity" name="productQuantity" required><br>
        <label for="productPrice">상품 금액:</label><br>
        <input type="number" id="productPrice" name="productPrice" required><br>
        <label for="productDetail">상품 상세 정보:</label><br>
        <textarea id="productDetail" name="productDetail"></textarea><br><br>

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

주문 정보를 처리할 Controller 설정

jsp에서 form으로 보내는 수많은 값들을 받아온다.

이 값들을 Content-Type인 application/json 으로 변환하기 위해 JSONObject를 활용한다.

@RequestMapping(value = "/order.do", method = RequestMethod.POST)
public String submitOrder(@ModelAttribute OrderVO orderVO, Model model) {
	// 헤더값 설정 코드
	...
	
	// JSON 데이터 생성
  JSONObject json = new JSONObject();
  
  JSONObject pickupLocation = new JSONObject();
  pickupLocation.put("basicAddress", orderVO.getPickupBasicAddress());
  pickupLocation.put("detailAddress", orderVO.getPickupDetailAddress());
  pickupLocation.put("latitude", orderVO.getPickupLatitude());
  pickupLocation.put("longitude", orderVO.getPickupLongitude());

  ...

  JSONObject productInfo = new JSONObject();
  productInfo.put("trayCount", orderVO.getTrayCount());
  productInfo.put("size", orderVO.getSize());
  productInfo.put("totalPrice", orderVO.getProductPrice() * orderVO.getProductQuantity());
  productInfo.put("products", productsArray);

  // 최종 JSON 객체에 데이터 삽입
  json.put("partnerOrderId", orderVO.getPartnerOrderId());
  json.put("orderType", orderVO.getOrderType());
  json.put("pickup", pickup);
  json.put("dropoff", dropoff);
  json.put("productInfo", productInfo);
}

파라미터에 값들이 너무 많아져서 따로 OrderVO를 만들어 @ModelAttribute를 사용해 처리하였다.

(JSON으로 변환하는 부분도 따로 함수로 만들어서 쓰면 코드가 깔끔해질 것 같다.)

POST 요청을 보내기 위해 Header 값과 body 값(JSON)을 HttpEntity로 캡슐화 한다.

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

이후 restTemplate로 API 호출 후 model에 담고 따로 만들어둔 result.jsp에 출력해보기로 했다.

	// API 호출
	ResponseEntity<String> response = restTemplate.postForEntity(API_ENDPOINT, requestEntity, String.class);

	// 응답 처리
  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";
}

아래는 테스트용으로 만든 result.jsp이다.

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

이 과정에서 500 Server Error가 떠서 확인해본 결과

  • POST로 보낼 JSON의 순서가 뒤죽박죽이라 형식에 맞지 않았던 것
  • 출발지(도착지) 경도 위도가 실제 위치값이어야 한다는 것

두 가지 문제점을 파악할 수 있었다.

또한 반복해서 요청을 보내면 partnerOrderId 가 중복되어 409 Conflict Error가 뜨기 때문에 테스트할 때는 신경 쓸 필요가 있는 것 같다.

결과

여러가지 에러가 나와서 골치아프긴 했지만 어찌저찌 Response를 받아오는데에 성공했다.

다만 인코딩 문제로 한글이 ?로 나오는 것은 따로 해결해야 할 것 같다.


결론

분명 외부 API를 가져오고 쓰는거 자체는 간단할 거라 생각했는데 막상 구현해보니 예상치 못한 에러들이 많이 나와 구현하는데 생각보다 시간이 오래걸렸다.
다음엔 다른 기능과 함께 RestController를 통한 RESTAPI 구축을 해보도록 하겠다.


레퍼런스

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

0개의 댓글