[JAVA] 한국투자증권 OpenAPI 사용 (Rest)

Sadie·2025년 3월 9일
0

Spring And JPA

목록 보기
10/12

주식 데이터를 가져올 때 많이 사용하는 한국투자증권 OpenAPI 사용법에 대해 정리해보고자 한다
(평소 주식투자에서도 잘 활용하고 있는 한국투자증권... 최고!)


접속방법

한국투자증권 OPENAPI 개발자센터
해당 주소로 이동하면 한국투자증권 OPENAPI 개발자센터로 이동한다

한국투자증권 OPENAPI 개발자센터 샘플코드
한투에서는 친절하게 샘플코드를 준다
참고해서 개발을 해도 좋을 것 같다


개발 전 해야할 것

  1. 한국투자증권 계좌 개설
  2. 개발자 센터 사이트에 접속 -> 오른쪽 상단의 'API신청'
  3. 로그인(본인인증) 후, KIS Developers 서비스 신청하기
  4. API 키 발급받기
    • 모의투자의 경우 모의투자 신청 후 가능
    • 실물계좌의 경우 바로 가능
    • 모의투자의 경우에는 사용 불가능한 api도 존재

해당 과정을 통해 APP key와 APP Secret를 발급받을 수 있습니다


Rest 코드

국내주식기간별시세(일/주/월/년)[v1_국내주식-016]을 예시로 정보를 받아오고 데이터베이스에 저장하는 코드를 작성해보겠습니다


yml

hantu-openapi:
  domain: https://openapivts.koreainvestment.com:29443 #모의 도메인
   #https://openapi.koreainvestment.com:9443 #실전 도메인
  appkey: [발급받은 app key] #${api_appkey}
  appsecret: [발급받은 app secret] #${api_appsecret}

application.yml

발급받은 키와 비밀번호를 yml에 등록시켜줍니다


Controller

	// DB에 주식 정보 저장하는 코드
    @PostMapping("/fetch-stock-data")
    public String fetchStockData(
            @RequestParam String marketCode,
            @RequestParam String stockCode, // SK하이닉스 000660, 삼성전자: 005930
            @RequestParam String startDate,
            @RequestParam String endDate,
            @RequestParam String periodCode) {
        stockService.fetchAndSaveStockData(marketCode, stockCode, startDate, endDate, periodCode);
        return stockCode+" Stock data fetched and saved successfully";
    }

Controller에 다음과 같이 선언해주었습니다

필요한 파라미터는 Request의 Query Parameter에서 확인 할 수 있습니다


Entity

public class Stock {

    // 날짜, 상한가, 하한가, 누적거래량

    @Id
    @Column(name = "stock_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String stockCode;
    private LocalDate date;
    private String maxPrice;
    private String minPrice;
    private String accumTrans;
    private String openPrice;
    private String closePrice;

}

결과로 받는 Response 중 저장하고 싶은 데이터를 Entity로 만들어줍니다


Service

@Service
@Slf4j
public class StockService {

    // https://apiportal.koreainvestment.com/apiservice/apiservice-domestic-stock-quotations2#L_a08c3421-e50f-4f24-b1fe-64c12f723c77 // 한투 openapi 문서
    // https://www.ktb.co.kr/trading/popup/itemPop.jspx // 종목코드 검색

    @Autowired
    private StockRepository stockRepository;

    @Value("${hantu-openapi.domain}")
    private String apiDomain;

    @Value("${hantu-openapi.appkey}")
    private String appKey;

    @Value("${hantu-openapi.appsecret}")
    private String appSecret;

    // api 받아와서 db에 저장
    public void fetchAndSaveStockData(String marketCode, String stockCode, String startDate, String endDate, String periodCode) {
        TokenResponse accessToken = getAccessToken();
        String responseBody = getStockPriceData(marketCode, stockCode, startDate, endDate, periodCode, accessToken.getAccess_token());
        //log.info("responseBody: " + responseBody);
        saveStockData(responseBody, stockCode);
    }

    // api 데이터 받아옴
    private String getStockPriceData(String marketCode, String stockCode, String startDate, String endDate, String periodCode, String accessToken) {
        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        headers.set("authorization", "Bearer " + accessToken);
        headers.set("appkey", appKey);
        headers.set("appsecret", appSecret);
        headers.set("tr_id", "FHKST03010100");

        // 국내주식기간별시세(일/주/월/년)[v1_국내주식-016]
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(apiDomain + "/uapi/domestic-stock/v1/quotations/inquire-daily-itemchartprice")
                .queryParam("FID_COND_MRKT_DIV_CODE", marketCode)
                .queryParam("FID_INPUT_ISCD", stockCode)
                .queryParam("FID_INPUT_DATE_1", startDate)
                .queryParam("FID_INPUT_DATE_2", endDate)
                .queryParam("FID_PERIOD_DIV_CODE", periodCode)
                .queryParam("FID_ORG_ADJ_PRC", "0");

        HttpEntity<?> entity = new HttpEntity<>(headers);

        ResponseEntity<String> response = restTemplate.exchange(
                builder.toUriString(),
                HttpMethod.GET,
                entity,
                String.class);

        return response.getBody();
    }

    // db에 저장함
    private void saveStockData(String responseBody, String stockCode) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            JsonNode rootNode = mapper.readTree(responseBody);
            JsonNode output2 = rootNode.get("output2");

            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

            for (JsonNode node : output2) {
                Stock stock = new Stock();
                stock.setStockCode(stockCode);
                stock.setDate(LocalDate.parse(node.get("stck_bsop_date").asText(), formatter));
                stock.setMaxPrice(node.get("stck_hgpr").asText());
                stock.setMinPrice(node.get("stck_lwpr").asText());
                stock.setAccumTrans(node.get("acml_vol").asText());
                stock.setOpenPrice(node.get("stck_oprc").asText());
                stock.setClosePrice(node.get("stck_clpr").asText());

                stockRepository.save(stock);
            }
        } catch (Exception e) {
            e.printStackTrace();
            // 에러 처리 로직 추가
        }
    }

}

데이터를 받아와서 데이터베이스에 추가하는 코드입니다


접근토큰발급

위 과정에서 접근토큰을 넣어줘야합니다
토큰의 유효기간은 24시간이고 갱신발급주기는 6시간입니다

	// 접근토큰발급(P)[인증-001]
    public TokenResponse getAccessToken() {

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        String requestBody = String.format(
                "{\"grant_type\":\"client_credentials\",\"appkey\":\"%s\",\"appsecret\":\"%s\"}",
                appKey, appSecret
        );

        HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);

        ResponseEntity<TokenResponse> response = restTemplate.exchange(
                apiDomain + "/oauth2/tokenP",
                HttpMethod.POST,
                entity,
                TokenResponse.class
        );

        if (response.getStatusCode() == HttpStatus.OK) {
            return response.getBody();
        } else {
            throw new RuntimeException("Failed to obtain access token");
        }
    }

다음과 같이 토큰을 발급받고 저장해놓은 후, api를 불러올때마다 확인/갱신해줍니다


Postman 테스트

정상적으로 DB에 저장되는 것을 확인할 수 있습니다

0개의 댓글