@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class GetStockRequest {
@NotBlank(message = "{not_blank}")
@Schema(
name = "PDNO",
description = "stock id",
type = "String",
requiredMode = Schema.RequiredMode.REQUIRED,
example = "000660"
)
private String stockCode;
}
/*
* API를 사용해서 주식 정보를 가져올 때 주식 코드가 필요하다.
* User는 주식 코드를 query Parameter에 포함해서 보내고,
* 서버는 이것을 받아서 GetStockRequest 객체로 저장한다.
*/
public Mono<String> getStockInfoByCode(String code){
code = code.substring(Math.max(code.length() - 6, 0));
log.info(code);
String fullUrl = baseUrl + "/uapi/domestic-stock/v1/quotations/inquire-price" + "?fid_cond_mrkt_div_code=J&fid_input_iscd=" + code;
return webClient.get()
.uri(fullUrl)
.header("content-type","application/json")
.header("authorization","Bearer " + accessToken)
.header("appkey",appKey)
.header("appsecret",appSecretKey)
.header("tr_id","FHKST01010100")
.retrieve()
.bodyToMono(String.class);
}
/*
* User가 넘긴 stockCode로 주식 정보를 한투 서버에서 받아온다.
*/
// Swagger 관련 어노테이션 생략
public ResponseEntity<StockDTO> stock(@ModelAttribute("PDNO") GetStockRequest request) throws JsonProcessingException {
System.out.println(request.getStockCode());
if (request.getStockCode().length() > 6){
System.out.println("stock id should be smaller than 6 digits");
return ResponseEntity.badRequest().body(null);
}
Mono<String> stockNameByCode = kisService.getStockNameByCode(request.getStockCode()); // 주식 코드로 주식 이름 조회
Mono<String> stockInfoByCode = kisService.getStockInfoByCode(request.getStockCode()); // 주식 코드로 주식 정보 조회
String stockNameResponse = stockNameByCode.block();
String stockInfoResponse = stockInfoByCode.block();
log.info(stockNameResponse);
log.info(stockInfoResponse);
ObjectMapper objectMapper = new ObjectMapper();
Map<String,String> stockMap =
(Map<String, String>) objectMapper.readValue(stockNameResponse, Map.class).get("output");
Map<String,String> stockInfoMap =
(Map<String, String>) objectMapper.readValue(stockInfoResponse, Map.class).get("output");
// 응답 Json이 두번 감싸져 있는 형태 {"output" : {"stockName" : "samsung"}} 이런식으로
log.info(stockInfoMap.toString());
StockDTO stockDTO = StockDTO.builder()
.stockCode(stockMap.get("pdno")) // 주식 코드
.stockName(stockMap.get("prdt_abrv_name")) // 주식 이름
.stockPriceIndex(stockInfoMap.get("rprs_mrkt_kor_name")) // KOSPI200, KOSPI, KOSDAQ 등
.price(Long.valueOf(stockInfoMap.get("stck_oprc"))) // 가격 (당일 종가)
.theme(stockInfoMap.get("bstp_kor_isnm")) // 분야
.status(stockInfoMap.get("iscd_stat_cls_code")) // status Code
.build();
stockService.save(stockDTO);
return ResponseEntity.ok().body(stockDTO);
}
클라이언트에게 주식 코드를 받아서 저장한다.
해당 주식 코드에 해당되는 주식 이름, 가격, 분야, 상태 코드를 DB에 저장한다.
자세하게 말하자면, kisService
의 메소드를 사용해서 응답 메시지 바디를 Mono<String>
타입으로 받아온다.
받아온 JSON 형식의 바디를 ObjectMapper
로 Map 객체로 변환한다.
Map 객체에서 원하는 정보를 빼내어 stockDTO로 변환한 후, stockServcie를 사용해서 DB에 저장한다.
webClient 객체를 사용하다가 이런 의문이 들었다.
Webclient는 요청 때마다 생성 안해도 되나?
Webclient는 스프링 빈으로 등록되어 한 유저가 하나의 Webclient만을 사용한다.
결론부터 말하자면 webClient 객체를 하나만 생성해도 여러 요청에서 공유해서 사용할 수 있다.
webClient.get() 또는 webClient.post() 등
하나의 webClient 객체에 Http Method를 적용하면, Mono<ResponseEntity<T>>
타입의 객체를 리턴한다.
Mono<ResponseEntity<T>>
객체를 커스터마이징해서 Http 요청을 구조화한다고 생각하면 된다.
요청 메시지를 만든 뒤에는 retrieve
메소드를 호출해서 응답 결과를 어떻게 추출할 것인지 명시한다. (아직 실제 요청이 보내진 것이 아니다!!)
block()
메소드를 호출하면 실제로 Http 요청 메시지를 보내고 응답 메시지를 받아온다.
java.lang.illegalargumentexception: host is not specified
오류가 발생하면 Uri를 다시 한번 확인하도록 하자..uri(uriBuilder -> uriBuilder
.path(baseUrl + "/uapi/domestic-stock/v1/quotations/search-info")
.queryParam("PDNO","000660")
.queryParam("PRDT_TYPE_CD","300")
.build())
결론: 인코딩 문제 있으니까 다른 방법 사용하자.
// 해결
String fullUrl = baseUrl + "/uapi/domestic-stock/v1/quotations/inquire-price" + "?fid_cond_mrkt_div_code=J&fid_input_iscd=" + code;
return webClient.get()
.uri(fullUrl)
스프링 부트 자체 오류인듯
https://github.com/spring-projects/spring-framework/issues/24787