Back to blog
Backend2 min read2023년 7월 16일

[스프링부트] 스마트 택배 API ( sweet-tracker ) 기능 구현하기

스마트택배 API를 붙이면서 API 키 관리, 응답 매핑, 설정 분리를 어떻게 했는지 정리했습니다.

개발 하다보면 외부 API를 서버에서 처리해야 할 일이 꽤 있습니다.

스마트 택배 API를 통해 외부 API 응답 주고받는 방법을 설명드리겠습니다.

스마트 택배 API의 apiKey 발급

https://tracking.sweettracker.co.kr

스마트택배 API

스마트택배 API 페이지로 가서 회원가입을 진행합니다.

(이용요금은 현재 월 1000건 무료입니다. 사용 전 꼭 확인하시기 바랍니다.)

로그인 후 이런 페이지가 뜰텐데요, 좌측 API KEY 관리 -> 이용권 구매 -> 하단 등급 프리로 선택, 새로운 API KEY 선택 후 생성합니다.

구매(사실 무료라 구매가 아니죠) 후 KEY 목록에서 APIKEY를 복사하여, 프로젝트의 ENV파일에 저장합니다.

프로젝트 구조 프로젝트 구조

WaybillNumber

@Entity
@Getter @NoArgsConstructor
public class WaybillNumber extends BaseTimeEntity {

    @Id @GeneratedValue
    private Long id;

    /*
     * 택배사 코드
     * 스마트 택배 API 호출 용도
     * 04 : CJ 대한통운
     * 08 : 롯데택배
     * 46 : CU 편의점택배
     */
    private String  code;

    // 운송장 번호
    private String waybillNumber;

    // 주문 번호
    private String orderNumber;

    @Builder
    public WaybillNumber(String code, String waybillNumber, String orderNumber) {
        this.code = code;
        this.waybillNumber = waybillNumber;
        this.orderNumber = orderNumber;
    }

}

저희가 사용할 엔티티입니다. 저희 서버 내부에서 사용할 주문번호와 주문건에 해당하는 운송장 번호, 그리고 택배사 코드를 받습니다.

BaseTimeEntity는 createdDate와 lastModifiedDate를 저장하는 클래스입니다.

WaybillNumberRepository

package repick.repickserver.domain.waybill.dao;

import org.springframework.data.jpa.repository.JpaRepository;
import repick.repickserver.domain.waybill.domain.WaybillNumber;

public interface WaybillNumberRepository extends JpaRepository<WaybillNumber, Long> {

    WaybillNumber findByOrderNumber(String orderNumber);
}

JPA를 사용하는 기본적인 레포지토리입니다.

주문번호로 WaybillNumber 엔티티를 찾는 findByOrderNumber를 선언해둡니다.

JPA는 함수 이름으로 알아서 이름에 맞는 기능을 구현해줍니다. (구현체를 직접 작성하지 않아도 됩니다.)

WaybillNumberController

@RestController
@RequestMapping("/waybill-number")
@RequiredArgsConstructor
public class WaybillNumberController {

    private final WaybillNumberService waybillNumberService;

    @Operation(summary = "운송장 번호 등록", description = "운송장 번호를 등록합니다.")
    @PostMapping("/register")
    public ResponseEntity<Boolean> postWaybillNumber(@RequestBody WaybillNumberRequest request) {
        return ResponseEntity.ok()
                .body(waybillNumberService.postWaybillNumber(request));
    }

    @Operation(summary = "운송장 번호 조회", description = "주문 번호로 운송장 번호를 조회합니다.")
    @GetMapping("/get")
    public ResponseEntity<WaybillNumberResponse> getWaybillNumber(@RequestParam String orderNumber) {
        return ResponseEntity.ok()
                .body(waybillNumberService.getWaybillNumber(orderNumber));
    }

    @Operation(summary = "운송장 번호 조회", description = "주문 번호로 운송장 번호를 조회합니다.")
    @GetMapping("/get-status")
    public ResponseEntity<String> getWaybillStatus(@RequestParam String orderNumber) throws JsonProcessingException {
        return ResponseEntity.ok()
                .body(waybillNumberService.getWaybillStatus(orderNumber));
    }

}

컨트롤러입니다.

@Operation : Swagger 문서화를 위해 넣은것으로 생략하셔도 됩니다.

관리자가 주문번호에 맞는 운송장 번호를 기입하는 postWaybillNumber,

소비자가 직접 운송장 번호를 확인할 수 있는 getWaybillNumber와,

주문번호로 배송현황을 서버로부터 조회할 수 있는 getWaybillStatus가 있습니다.

WaybillNumberService

@Service
@Transactional
@RequiredArgsConstructor
public class WaybillNumberService {

    private final WaybillNumberRepository waybillNumberRepository;
    private final SweetTrackerProperties sweetTrackerProperties;

    /**
     * <h1>운송장 번호 등록</h1>
     * @param request (code, waybillNumber, orderNumber)
     * @return true
     * @author seochanhyeok
     */
    public Boolean postWaybillNumber(WaybillNumberRequest request) {
        WaybillNumber waybillNumber = WaybillNumber.builder()
                .code(request.getCode())
                .waybillNumber(request.getWaybillNumber())
                .orderNumber(request.getOrderNumber())
                .build();

        waybillNumberRepository.save(waybillNumber);

        return true;
    }

    /**
     * <h1>주문 번호로 운송장 번호 조회</h1>
     * @param orderNumber (주문 번호)
     * @return waybillNumberResponse (code, waybillNumber, orderNumber)
     * @author seochanhyeok
     */
    public WaybillNumberResponse getWaybillNumber(String orderNumber) {
        WaybillNumber waybillNumber = waybillNumberRepository.findByOrderNumber(orderNumber);
        return WaybillNumberResponse.builder()
                .code(waybillNumber.getCode())
                .waybillNumber(waybillNumber.getWaybillNumber())
                .orderNumber(waybillNumber.getOrderNumber())
                .build();
    }

    /**
     * <h1>주문 번호로 배송조회</h1>
     * @param orderNumber (주문 번호)
     * @return 배송조회 결과 (json)
     * @see SweetTracker API
     * @author seochanhyeok
     */
    public String getWaybillStatus(String orderNumber) {
        WaybillNumber waybillNumber = waybillNumberRepository.findByOrderNumber(orderNumber);

        HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(new HttpHeaders());

        RestTemplate rt = new RestTemplate();
        ResponseEntity<String> response = rt.exchange(
                "http://info.sweettracker.co.kr/api/v1/trackingInfo?t_key=" + sweetTrackerProperties.getApiKey() +
                        "&t_code=" + waybillNumber.getCode() +
                        "&t_invoice=" + waybillNumber.getWaybillNumber(),
                HttpMethod.GET,
                httpEntity,
                String.class
        );

        return response.getBody();
    }
}

서비스입니다.

다른 메서드 설명은 생략하고, getWaybillStatus만 설명드리겠습니다.

SweetTrackerProperties는 뭔데요? -> 게시글 최하단에 설명 있습니다.

WaybillNumberService/getWaybillStatus

public String getWaybillStatus(String orderNumber) {
    WaybillNumber waybillNumber = waybillNumberRepository.findByOrderNumber(orderNumber);

    HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(new HttpHeaders());

    RestTemplate rt = new RestTemplate();
    ResponseEntity<String> response = rt.exchange(
            "http://info.sweettracker.co.kr/api/v1/trackingInfo?t_key=" + sweetTrackerProperties.getApiKey() +
                    "&t_code=" + waybillNumber.getCode() +
                    "&t_invoice=" + waybillNumber.getWaybillNumber(),
            HttpMethod.GET,
            httpEntity,
            String.class
    );

    return response.getBody();
}
WaybillNumber waybillNumber = waybillNumberRepository.findByOrderNumber(orderNumber);

먼저, JPA를 통해 구현체 없이 만든 findByOrderNumber로 waybillNumber 엔티티를 찾아옵니다.

HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(new HttpHeaders());

HttpEntity는 RestTemplate를 통해 타 API로 요청을 보낼 때 담는 매개변수 중 하나입니다.

HttpHeaders 예시

// HttpHeaders 예시
// 스프링 시큐리티 -> oauth 서비스에서, oauth 서버로 요청할 때 이런식으로 헤더가 요구되는 경우가 있다.

// HTTP Header 생성
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + accessToken);
headers.add("Content-type", "application/x-www-form-urlencoded;charset=utf-8");

저희가 구현할 스마트택배 API 요청에는 요구되는 헤더가 없기 때문에, new HttpHeaders()로 빈 헤더를 보내지만, 필요한 경우 예시처럼 담을 수 있습니다.

headers.add( “{헤더명}”, “{헤더에 담을 내용”}); 를 통해 담으시면 됩니다.

RestTemplate rt = new RestTemplate();
ResponseEntity<String> response = rt.exchange(
        "http://info.sweettracker.co.kr/api/v1/trackingInfo?t_key=" + sweetTrackerProperties.getApiKey() +
                "&t_code=" + waybillNumber.getCode() +
                "&t_invoice=" + waybillNumber.getWaybillNumber(),
        HttpMethod.GET,
        httpEntity,
        String.class
);

Spring 5.0 이후 부터는 RestTemplate는 deprecated 되었습니다. (WebClient 사용 지향)

따라서 deprecated 된 경우 WebClient로 해당부분 구현 바랍니다.

다음으로, RestTemplate()를 선언합니다.

RestTemplate은 스프링의 HTTP 통신 템플릿으로, RESTful한 통신을 할 수 있는 Server to Server 템플릿입니다.

rt.exchange() 함수에 4개의 매개변수를 받습니다.

  1. url

  2. HTTP Method

  3. HttpEntity

  4. 반환받는 타입

String으로 반환타입을 지정한 이유는, 제 서비스에선 반환값을 그대로 프론트로 넘겨주고 파싱을 하도록 요구할 것이기 때문입니다.

이런식으로 response를 받습니다.

return response.getBody();

.getBody();를 통해 응답 내용을 return해주면 됩니다.

응답예시는 스마트 택배 API 문서에서 확인 가능합니다.

http://info.sweettracker.co.kr/apidoc/

SweetTrackerProperties는 뭔데요?

SweetTrackerProperties

@Component
@ConfigurationProperties("sweet-tracker")
@Data
public class SweetTrackerProperties {
    private String apiKey;
}

별 거 아니고 apiKey를 담는 env파일과 연결시키는 클래스입니다.

이렇게 두고 application-properties.yaml 파일에 환경변수를 넣으면 됩니다.

application-properties.yaml 예시

...

sweet-tracker:
  api-key: JBwQUiwAQwGimjQaMZpkEW

Topics

spring-bootapi-integration