처음 Tech&Fin 블로그를 시작했을 때보다 비교적 많은 분들이 블로그를 찾아주시고 올려드린 비트코인 자동매매 프로그램을 설치하여 사용중인 것 같습니다.
사용하시는 분이 많아질 수록 예상치 못한 오류가 발생하는 케이스도 많아지고 있는데요. 이번 시간에는 지금까지 댓글로 문의를 받았던 오류들을 해결하면서 알게된 내용을 공유드리는 시간을 가져 보려고 합니다.

일반적인 오류 해결 방법
어떤 프로그램이나 마찬가지겠지만 오류가 발생하는 원인은 한 가지가 아니라 매우 다양합니다. 또한 같은 메세지를 내 뱉는 오류라 할 지라도 오류의 원인은 다를 수 있습니다.
그렇기 때문에 오류를 해결하는 방법이 한 가지일 수는 없으며 매우 다양한 케이스가 존재할 수 있습니다. 이럴 때 오류를 해결할 수 있는 가장 좋은 방법은 의심이 가는 곳에서 로그를 발생시켜 원인을 차근차근 분석 해 찾아내는 방법입니다.
[2022/03/12 02:00:31 PM][ERROR][test_module.py:31]:Traceback (most recent call last): File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module> accounts = upbit.get_accounts('Y', 'KRW') File "C:\Python-Project\trade_bot\module\upbit.py", line 785, in get_accounts if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']: TypeError: string indices must be integers
예를 들어 위와 같은 오류가 발생한 경우 실제 에러가 발생한 내용은 [TypeError: string indices must be integers] 입니다. 스트링의 인덱스가 정수가 아닌 경우에 발생하는 에러인데요. 그럼 왜 이런 에러가 발생했을까요?
앞서 말씀 드렸듯이 원인은 한가지가 아닐 수 있습니다. 그렇기 때문에 가능한 의심이 가는 곳에 로그를 발생 시켜 원인을 차근차근 찾아 보아야 합니다.
File "C:\Python-Project\trade_bot\module\upbit.py", line 785, in get_accounts if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']:
먼저 바로 위에 줄에 trace가 찍힌 곳부터 차근차근 살펴보면 되는데요. upbit.py의 785 번째 라인에서 에러가 발생했음을 알수 있습니다.
res = send_request("GET", server_url + "/v1/accounts", "", headers) account_data = res.json() for account_data_for in account_data: for market_item_list_for in market_item_list: # 해당 마켓에 있는 종목만 조합 if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']: # KRW 및 소액 제외 if except_yn == "Y" or except_yn == "y":
해당 위치로 가서 살펴보니 해당 마켓에 있는 종목만 걸러내기 위한 로직에서 에러가 발생했음을 알 수 있으며 해당 if문 내의 어디선가 오류가 발생했음을 짐작할 수 있습니다.
① market_code
② account_data_for
③ market_item_list_for
우선 세 가지 오브젝트의 내용이 정상적으로 들어오지 않았는지 로그를 발생시켜 확인해 보는 것이 좋습니다.
res = send_request("GET", server_url + "/v1/accounts", "", headers) account_data = res.json() for account_data_for in account_data: for market_item_list_for in market_item_list: logging.info(market_code) logging.info(account_data_for) logging.info(market_item_list_for) # 해당 마켓에 있는 종목만 조합 if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']: # KRW 및 소액 제외 if except_yn == "Y" or except_yn == "y":
7~9번 라인과 같이 에러가 발생한 곳 직전에 의심이 가는 세 가지 오브젝트를 로그로 출력해 보았습니다.
[2022/03/12 02:32:18 PM][INFO][upbit.py:784]:KRW [2022/03/12 02:32:18 PM][INFO][upbit.py:785]:error [2022/03/12 02:32:18 PM][INFO][upbit.py:786]:{'market': 'KRW-BTC', 'korean_name': '비트코인', 'english_name': 'Bitcoin'} [2022/03/12 02:32:18 PM][ERROR][test_module.py:30]:Exception 발생! [2022/03/12 02:32:18 PM][ERROR][test_module.py:31]:Traceback (most recent call last): File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module> accounts = upbit.get_accounts('Y', 'KRW') File "C:\Python-Project\trade_bot\module\upbit.py", line 789, in get_accounts if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']: TypeError: string indices must be integers
로그 내용을 살펴보니 2번째 줄에서 account_data_for의 내용이 정상적이지 않고 'error'라는 텍스트 스트링이 들어 있음을 확인할 수 있습니다. 그로 인해 딕셔너리 타입으로 예상하여 account_data_for['currency']라는 키를 가져오려고 한 부분에서 에러가 발생하는 것이 었습니다.
그럼 실질적인 에러의 원인은 다른 곳에 있다는 건데요. 왜 account_data_for에 잔고 데이터가 아닌 error가 입력되어 있는지를 찾아야 합니다.
res = send_request("GET", server_url + "/v1/accounts", "", headers) account_data = res.json()
account_data_for는 잔고를 조회하는 API를 호출한 결과에서 받아오는 내용인데 결국 업비트 API를 호출한 후 정상 결과가 아닌 error를 받았음을 짐작할 수 있습니다.
# ----------------------------------------------------------------------------- # - Name : send_request # - Desc : 리퀘스트 처리 # - Input # 1) reqType : 요청 타입 # 2) reqUrl : 요청 URL # 3) reqParam : 요청 파라메타 # 4) reqHeader : 요청 헤더 # - Output # 4) reponse : 응답 데이터 # ----------------------------------------------------------------------------- def send_request(reqType, reqUrl, reqParam, reqHeader): try: # 요청 가능회수 확보를 위해 기다리는 시간(초) err_sleep_time = 0.3 # 요청에 대한 응답을 받을 때까지 반복 수행 while True: # 요청 처리 response = requests.request(reqType, reqUrl, params=reqParam, headers=reqHeader) # 요청 가능회수 추출 if 'Remaining-Req' in response.headers: hearder_info = response.headers['Remaining-Req'] start_idx = hearder_info.find("sec=") end_idx = len(hearder_info) remain_sec = hearder_info[int(start_idx):int(end_idx)].replace('sec=', '') else: logging.error("헤더 정보 이상") logging.error(response.headers) break # 요청 가능회수가 3개 미만이면 요청 가능회수 확보를 위해 일정시간 대기 if int(remain_sec) < 3: logging.debug("요청 가능회수 한도 도달! 남은횟수:" + str(remain_sec)) time.sleep(err_sleep_time) # 정상 응답 if response.status_code == 200 or response.status_code == 201: break # 요청 가능회수 초과인 경우 elif response.status_code == 429: logging.error("요청 가능회수 초과!:" + str(response.status_code)) time.sleep(err_sleep_time) # 그 외 오류 else: logging.error("기타 에러:" + str(response.status_code)) logging.error("에러 코드:" + str(response.status_code)) logging.error("에러 내용:" + str(response.text)) break # 요청 가능회수 초과 에러 발생시에는 다시 요청 logging.info("[restRequest] 요청 재처리중...") return response # ---------------------------------------- # Exception Raise # ---------------------------------------- except Exception: raise
업비트 API를 호출하는 공통 모듈 부분입니다. 여기서 에러가 발생한건데요. 에러의 원인을 찾기 위해 50~52 라인에 오류의 내용을 로그로 출력하도록 했습니다.
기존에는 에러코드까지만 로그를 출력해서 원인 파악이 조금 힘들었는데 에러의 상세 내역(response.text)까지 출력하도록 하면 에러의 내용을 파악하는데 훨씬 도움이 됩니다.
C:\Python-Project\trade_bot\venv\Scripts\python.exe C:/Python-Project/trade_bot/test_module.py [2022/03/12 02:32:18 PM][ERROR][upbit.py:172]:기타 에러:401 [2022/03/12 02:32:18 PM][ERROR][upbit.py:173]:에러 코드:401 [2022/03/12 02:32:18 PM][ERROR][upbit.py:174]:에러 내용:{"error":{"name":"invalid_access_key","message":"잘못된 엑세스 키입니다."}} [2022/03/12 02:32:18 PM][INFO][upbit.py:784]:KRW [2022/03/12 02:32:18 PM][INFO][upbit.py:785]:error [2022/03/12 02:32:18 PM][INFO][upbit.py:786]:{'market': 'KRW-BTC', 'korean_name': '비트코인', 'english_name': 'Bitcoin'} [2022/03/12 02:32:18 PM][ERROR][test_module.py:30]:Exception 발생! [2022/03/12 02:32:18 PM][ERROR][test_module.py:31]:Traceback (most recent call last): File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module> accounts = upbit.get_accounts('Y', 'KRW') File "C:\Python-Project\trade_bot\module\upbit.py", line 789, in get_accounts if market_code + '-' + account_data_for['currency'] == market_item_list_for['market']: TypeError: string indices must be integers
다시 오류를 재현해보니 오류의 내용을 명확하게 알 수 있게 되었습니다. 바로 액세스키가 잘못되어서 발생한 오류였는데요. 참고로 이 오류는 제가 일부러 발생시킨 오류이며 업비트에서 오류로 넘어올 수 있는 내용은 아래와 같으니 참고하시면 좋을 것 같습니다.


에러 코드 관련한 업비트 API 공식 문서는 아래 링크를 통해 확인하실 수 있습니다.
업비트 개발자 센터
업비트 Open API 사용을 위한 개발 문서를 제공 합니다.업비트 Open API 사용하여 다양한 앱과 프로그램을 제작해보세요.
docs.upbit.com
자주 발생하는 오류 해결 방법
그럼 지금부터 그 동안 댓글로 문의 주신 내용들 중 빈번하게 발생하는 내용들에 대한 해결 방법을 알아보도록 하겠습니다. 파이썬의 버전 및 사용하는 환경이 달라 오류의 해결 방법이 다소 다를 수 있는 점은 미리 양해 부탁 드리겠습니다.
JWT 모듈 관련 오류
C:\Python-Project\trade_bot\venv\Scripts\python.exe C:/Python-Project/trade_bot/test_module.py [2022/03/12 02:50:27 PM][ERROR][test_module.py:30]:Exception 발생! [2022/03/12 02:50:27 PM][ERROR][test_module.py:31]:Traceback (most recent call last): File "C:\Python-Project\trade_bot\test_module.py", line 21, in <module> accounts = upbit.get_accounts('Y', 'KRW') File "C:\Python-Project\trade_bot\module\upbit.py", line 774, in get_accounts jwt_token = jwt.encode(payload, get_env_keyvalue('secret_key')) AttributeError: module 'jwt' has no attribute 'encode'
JWT는 Json Web Token의 약자로 인증을 위한 모듈입니다. 파이썬에서 설치할 수 있는 JWT 모듈은 총 2가지로 그냥 JWT와 PyJWT가 있습니다. 이 중에 PyJWT를 설치하는 것이 가장 좋으며 JWT를 설치하셨다면 공통모듈의 encode와 decode 부분을 조금 수정해 주어야 합니다.
그렇기 때문에 현재 설치되어 있는 JWT 모듈을 확인하고 JWT가 설치되어 있다면 삭제 후 PyJWT를 설치하여 오류를 해결 할 수 있습니다.

python -m pip list
먼저 파이참 하단의 터미널을 클릭하여 명령어 창을 연 후에 설치되어 있는 모듈을 검색하는 명령어를 수행합니다.

현재 설치되어 있는 모듈을 확인할 수 있으며 현재 pyJWT가 아닌 jwt 1.3.1이 설치되어 있음을 알 수 있습니다.

python -m pip uninstall jwt
먼저 jwt 모듈을 삭제합니다.

python -m pip install pyjwt
pyjwt 모듈을 설치해 줍니다.
인증키 관련 오류
그 외에 자주 발생하는 오류는 인증키 관련 오류인데요. 원인을 알기 힘든 오류가 발생 한다면 아래 인증키 관련 내용을 살펴 보시는 것이 도움이 될 수 있을것 같습니다.
① 허용된 IP 확인 : 업비트에서 API Key를 생성할 때 허용된 IP를 등록했다면 해당 IP에서 프로그램을 수행해야만 정상적으로 작동합니다. 업비트에 등록한 IP와 현재 프로그램을 수행하는 PC 또는 서버의 공인 IP를 확인합니다.
② 인증키 만료 확인 : 업비트에서 발급받은 API Key는 무기한 인증키가 아닙니다. 만료기간이 있기 때문에 이 부분을 잘 확인할 필요가 있습니다. 특히 잘 되던 프로그램이 갑자기 안되는 것은 인증키 만료의 경우일 가능성이 높습니다.
파이썬 버전 및 PIP 버전 오류
파이썬이 현재 시점으로 3.9 버전보다 낮거나 PIP의 버전이 최신이 아닌경우 모듈 설치 및 구동 시 런타임 에러가 발생할 수 있으니 가능한 최신 버전으로 유지하는 것이 좋습니다.

python --version
설치된 파이썬 버전은 위의 명령어로 확인할 수 있습니다. 참고로 리눅스에서 파이썬 3.9를 설치하는 방법은 아래 포스팅을 참고 부탁 드립니다.
2022.01.13 - [코딩스토리/리눅스] - 리눅스 서버에 파이썬 3.9 설치하기
리눅스 서버에 파이썬 3.9 설치하기
Tech&Fin 블로그에서 사용중인 서버는 오라클 클라우드에서 무료로 제공되는 프리티어 서버이며 RedHat 그리고 CentOS와 같은 계열인 오라클 리눅스 8 버전을 기준으로 블로그를 진행하고 있습니다.
technfin.tistory.com

python -m pip --version
PIP 버전은 위의 명령어로 확인할 수 있습니다.

python.exe -m pip install --upgrade pip
PIP는 위의 명령어로 업그레이드 할 수 있습니다.
마치며
이번 포스팅에서는 로그를 이용하여 에러를 찾는 기본적인 방법과 자주 발생하는 문제 해결 방법에 대해서 살펴 보았습니다.
하지만 앞서 말씀 드린바와 같이 에러가 발생하는 경우는 매우 다양할 수 있기 때문에 단순히 에러 내용으로 판단하기 보다는 앞뒤로 로그를 출력해가며 실제 원인을 파악하여 해결하는 방법에 익숙해 지시는 것을 추천 드립니다.
블로그를 구독하면 소식을 조금 더 빨리 받아 보실 수 있습니다. 감사합니다.
'프로젝트 > 비트코인 자동매매' 카테고리의 다른 글
[비트코인 자동매매 시즌2] #1 - 개발 환경 설명 및 설정 (0) | 2024.05.10 |
---|---|
비트코인 자동매매 프로그램 만들기 시즌2 안내 (1) | 2024.04.30 |
급등주 찾기 쿼리 - PostgreSQL 버전 (4) | 2022.05.03 |
업비트 웹소켓 데이터 PostgreSQL DB에 저장하기 - 파이썬 비트코인 자동매매 (12) | 2022.02.23 |
업비트 공지사항 크롤링하여 텔레그램으로 메세지 알림받기 (21) | 2022.01.29 |
급등주 찾기 - 업비트 파이썬 비트코인 자동매매 프로그램 (19) | 2022.01.26 |