본문 바로가기
파이썬 주식 자동매매 봇 만들기 프로젝트

자동매매 봇 만들기 #3] 외계어 해독기: 거대한 JSON 덩어리에서 삼성전자 현재가 구출하기

by triz-hong 2026. 5. 31.

[파이썬 주식 자동매매 봇 만들기 #3] 외계어 해독기: 거대한 JSON 덩어리에서 삼성전자 현재가 구출하기

지난 2편에서 무자비한 400 에러를 극복하고 마침내 증권사 서버의 임시 출입증인 접근 토큰(Access Token)을 손에 넣었습니다. 부대 내 개인 정비 시간과 저녁 연등 시간을 쪼개어 코드를 작성하다 보니 진도가 아주 빠르지는 않지만, 어찌어찌 가장 까다롭다는 인증 단계를 넘겼다는 생각에 마음이 한결 가벼워졌습니다. 이제 남은 것은 그저 종목 코드를 넣고 현재 가격을 물어보는 것뿐이라고 아주 순진하게 생각했습니다.

하지만 API의 세계는 끝없는 규격과 형식의 연속이었습니다. 테스트 삼아 대한민국 국민 주식인 삼성전자(종목코드: 005930)의 현재가를 조회해 달라고 요청을 보냈더니, 화면을 까맣게 뒤덮는 알 수 없는 영문자 조합과 수백 줄의 데이터가 쏟아져 내렸습니다. 오늘 3편에서는 이 거대하고 불친절한 JSON 데이터 덩어리 속에서 내가 원하는 알맹이만 쏙쏙 뽑아내는 데이터 파싱(Parsing) 과정과 삽질의 기록을 담았습니다.

1. 시세 조회 요청의 숨은 장벽: 엄청나게 많아진 Header 요구사항

토큰을 발급받았으니 파이썬의 requests.get() 함수로 요청만 띡 보내면 끝일 줄 알았습니다. 하지만 공식 문서를 확인해보니, 주식 현재가 시세를 조회하기 위해 서버가 요구하는 Header(헤더) 값의 종류가 인증 때와는 비교도 안 되게 늘어나 있었습니다. 마치 겹겹이 쌓인 보안문을 통과하는 기분이었습니다.

  • authorization: 지난번 발급받은 토큰 문자열 앞에 반드시 "Bearer "라는 글자와 띄어쓰기 한 칸을 꼭 붙여서 넣어야 했습니다.
  • appkey & appsecret: 1편에서 환경변수에 저장해 둔 본인의 마스터 키 값을 헤더에 또 넣어주어야 합니다.
  • tr_id (Transaction ID): 저를 가장 당황하게 만든 항목입니다. 한국투자증권 API는 내가 지금 하려는 행동이 '시세 조회'인지, '매수 주문'인지, '매도 주문'인지를 이 암호 같은 코드로 구분하고 있었습니다. 국내 주식 현재가 조회의 tr_id는 FHKST01010100이었습니다.

헤더뿐만이 아니었습니다. 이번에는 GET 방식이므로 URL 뒤에 쿼리 파라미터(Query Parameter)를 붙여야 하는데, 종목코드(FID_INPUT_ISCD) 외에도 시장 분류 코드(FID_COND_MRKT_DIV_CODE)를 "J" (주식, ETF)로 하드코딩해서 넘겨주어야 했습니다.

2. 당당하게 요청을 보냈지만... 모니터를 덮친 JSON 지옥

문서가 시키는 대로 꼼꼼하게 헤더와 파라미터를 구성하여 파이썬 코드를 실행했습니다. 이번에는 에러 없이 200 OK 상태 코드가 떨어졌습니다. "드디어 성공했다!" 속으로 환호성을 질렀지만, 콘솔 창에 출력된 결과물을 보고 저는 곧바로 할 말을 잃고 말았습니다.

깔끔하게 {"price": 80000} 이렇게 상식적으로 나올 줄 알았던 저의 기대와 달리, 모니터에는 수백 개의 알 수 없는 키(Key) 값들이 나열되어 있었습니다. stck_prpr, prdy_vrss, acml_vol, stck_hgpr... 마치 정체불명의 외계어 같았습니다. 이게 전부 무슨 뜻일까요?

3. API 명세서를 돋보기로 들여다보며 암호 해독하기

이대로는 도저히 매매 로직을 짤 수가 없어서 다시 KIS Developers 포털의 API 명세서를 켰습니다. 그리고 화면을 듀얼로 띄워놓고 출력된 JSON 키값들과 명세서의 설명을 하나하나 대조하는 막노동을 시작했습니다.

알고 보니 KIS API는 변수명을 지을 때 한글 발음이나 영어 단어를 극도로 축약해서 사용하고 있었습니다.

stck_prpr = 주식 현재가 (Stock Present Price의 약자일까요?)
prdy_vrss = 전일 대비 (전일 대비 상승/하락폭)
acml_vol = 누적 거래량 (Accumulated Volume)
stck_oprc = 주식 시가 (Stock Open Price)

명세서가 없었다면 평생 가도 무슨 뜻인지 몰랐을 이 암호들을 해독하고 나니, 그제야 이 거대한 JSON 덩어리가 하나의 유의미한 정보 창고로 보이기 시작했습니다. 전체 응답 데이터는 크게 output이라는 껍데기 안에 제가 원하는 알짜배기 정보들이 딕셔너리(Dictionary) 형태로 들어있는 중첩 구조였습니다.

4. 파이썬 딕셔너리(Dictionary) 파싱으로 알맹이만 구출하기

이제 암호를 완벽히 해독했으니, 파이썬 코드로 내가 원하는 값만 정확히 핀셋처럼 추출할 차례입니다. 여기서 초보자들이 가장 많이 하는 실수가 하나 있습니다. KIS API가 반환하는 가격이나 거래량 데이터는 숫자가 아니라 문자열(String) 형태라는 점입니다.

나중에 자동매매 봇이 "현재가가 8만 원 이하일 때 매수하라" 같은 조건문을 실행하려면, 이 문자열 데이터를 반드시 파이썬 내장 함수를 이용해 정수형(int)으로 형 변환(Casting)해 주어야 합니다. 이 과정을 모두 반영하여 시세 조회 함수를 작성했습니다.



import requests

import os

from dotenv import load_dotenv

# 2편에서 만든 get_access_token() 함수를 불러온다고 가정합니다.

# from kis_client import get_access_token 

load_dotenv()

BASE_URL = "https://openapivts.koreainvestment.com:29443"

def get_current_price(ticker="005930"):

    """

    특정 종목코드(ticker)의 현재가를 조회하는 함수

    기본값은 삼성전자(005930)

    """

    path = "/uapi/domestic-stock/v1/quotations/inquire-price"

    url = f"{BASE_URL}{path}"

    

    # 2편에서 구현한 함수로 토큰을 가져옵니다 (생략)

    # access_token = get_access_token() 

    access_token = "여기에_발급받은_토큰_입력"

    

    # 엄청나게 길어진 Header 구성

    headers = {

        "content-type": "application/json; charset=utf-8",

        "authorization": f"Bearer {access_token}",

        "appkey": os.getenv("KIS_APP_KEY"),

        "appsecret": os.getenv("KIS_APP_SECRET"),

        "tr_id": "FHKST01010100" # 주식 현재가 조회 고유 ID

    }

    

    # Query Parameter 구성

    params = {

        "FID_COND_MRKT_DIV_CODE": "J", # J: 주식, ETF, ETN

        "FID_INPUT_ISCD": ticker       # 종목코드 (예: 005930)

    }

    

    response = requests.get(url, headers=headers, params=params)

    

    if response.status_code == 200:

        data = response.json()

        

        # 거대한 JSON 덩어리에서 'output' 키 내부로 진입

        output = data.get("output", {})

        

        # 문자열로 반환되는 데이터를 정수형(int)으로 안전하게 변환하며 추출

        current_price = int(output.get("stck_prpr", 0))

        volume = int(output.get("acml_vol", 0))

        diff_from_yesterday = int(output.get("prdy_vrss", 0))

        

        print(f"[{ticker}] 시세조회 성공!")

        print(f"현재가: {current_price:,}원")

        print(f"전일대비: {diff_from_yesterday:,}원")

        print(f"누적거래량: {volume:,}주")

        

        return current_price

    else:

        print(f"[ERROR] 시세조회 실패: {response.status_code}")

        return None

if __name__ == "__main__":

    get_current_price("005930")

코드를 이렇게 다듬고 나서 터미널을 실행하니, 비로소 제가 상상했던 깔끔한 결과물이 출력되었습니다.

[005930] 시세조회 성공!
현재가: 80,000원
전일대비: 1,000원
누적거래량: 15,234,500주

5. 다음 편 예고: 떨리는 첫 매수 주문, 그리고 계좌번호 체계의 함정

이제 봇에게 시장의 흐름을 실시간으로 읽어오는 '눈'이 생겼습니다. 눈이 생겼으니 이제 손을 움직여볼 차례입니다. 다음 목표는 받아온 현재가 데이터를 바탕으로 증권사 서버에 직접 매수 주문을 쏘아보는 것입니다.

하지만 산 넘어 산이라고, 한국투자증권은 매수 주문 단계에서 또 하나의 거대한 함정을 파놓고 저를 기다리고 있었습니다. 바로 종합 계좌번호의 앞 8자리와 뒤 2자리를 무조건 쪼개서 분리해 넣어야 하는 해괴한 규칙입니다. 본격적인 매매 로직의 서막이 열리는 4편, 첫 매수 주문과 계좌번호 분리 삽질기로 돌아오겠습니다!