AUTO TRADE/[대신증권] CYBOS PLUS

대신증권 CYBOS PLUS 프로그램 구현 (14) - 차트 데이터 저장하기 ①

프로그램 구현 목표

  • MySQL 설치 및 구조 이해하기
  • MySQL과 파이썬 연결하기 : sqlalchemy
  • 데이터베이스 존재 여부 확인하기

이번 게시글에서는 이전에 제작한 차트 데이터 저장 함수를 활용하여 획득한 차트 데이터를 MySQL이라는 데이터베이스 프로그램을 설치한 후, MySQL의 구조를 이해하고 우리가 조회한 차트 데이터를 저장하고 또 다시 불러오는 방법에 대해 살펴보고자 한다.

 

 

MySQL 설치 및 구조 이해하기

이 프로그램을 설치하는 방법은 워낙에 다양한 게시글에서 설명하고 있으니 이 게시글에서는 설치 방법에 대한 설명은 제외하고, 참고하면 좋은 게시글의 링크를 첨부하여 둘테니 그 게시글의 설치 방법을 참고하도록 하자.
※ 설치할 때 입력한 비밀번호는 기억해두도록 하자. 아래에서 사용할 때 필요하다.

 

[MySQL] 0. MySQL 설치 방법과 설치 확인하기

MySQL 설치 방법은 혼공 사이트에서 참고한 내용입니다. 개인공부 및 기록용으로 블로그에 따로 작성하였습니다. 출처: https://hongong.hanbit.co.kr/mysql-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EC%84%A4%EC%B9%9

giveme-happyending.tistory.com

이제 프로그램 설치를 완료했다면, MySQL의 데이터베이스 구조를 살펴보도록 하자. MySQL은 기본적으로 데이터베이스(DataBase, 이하 DB)와 테이블(Table)이라는 두 가지 구조로 이루어져 있는데, 간단하게 설명하자면 테이블(Table)은 데이터베이스(DB)의 하위 구조이다. 보다 직관적으로 설명하자면 우리가 저장하고자 하는 차트 데이터들은 모두 테이블(Table) 안에 들어가게 되고, 데이터베이스(DB)는 단순히 그 테이블(Table)의 제목에 해당한다. 그렇다고 해서 데이터베이스가 단순하게 제목으로서의 기능만을 갖는 것은 아니다. 데이터베이스는 기본적으로 우리가 MySQL을 통해 테이블(Table)에 저장되어 있는 차트 데이터에 접근하고자 할 때, 가장 우선적으로 접근해야만 하는 범주에 해당한다. 다시 말해, 파이썬 언어를 통해 MySQL에 연결할 때에는 '데이터베이스의 이름'을 대상으로 연결해야만 그 데이터베이스의 하부에 있는 테이블(Table, 차트 데이터)에 접근할 수 있다는 것이다. 이 내용을 간단하게 정리하면 아래와 같은 모양으로 정리할 수 있다.

즉, 테이블 이름은 똑같을지언정 서로 다른 데이터베이스에 소속되어 있기 때문에 아무런 문제가 되지 않는다. 또한 데이터베이스에 접근할 때에는 기본저긍로 일봉 차트의 경우에는 `day_chart`라는 이름의 데이터베이스에 접근한 후에 그 아래에 있는 테이블에 접근하게 되고, 15분봉 차트의 경우에는 `min15_chart`라는 이름의 데이터베이스에 접근한 후에 그 아래에 있는 테이블에 접근하게 된다.

 

 

MySQL과 파이썬 연결하기 : sqlalchemy

위와 같은 구조가 이해가 갔다면, 우리가 파이참 내부에서 MySQL과 연결한다는 것의 의미는 바로 파이참을 통해 해당 데이터베이스에 접근할 수 있도록 한다는 것으로 이해할 수 있다. 그렇다면 이때, 파이참에서 MySQL에 접근할 수 있도록 하는 기능은 어떤 모듈이 수행할까 ? 바로 `pymysql`과 `sqlalchemy`이다. 이 중에서 `sqlalchemy`을 사용할 예정이다. 왜냐하면 이 라이브러리(모듈)가 ORM(객체라는 개념을 구현한 클래스와 테이블을 자동으로 연결하는 기능)을 활용할 수 있기 때문이다. 너무 깊게 파고 들어가면 어려우니 그냥 '간편하고 좋다.' 정도로만 생각하고 넘어가도록 하자. 그럼 이제 본격적으로 코드를 구현하기 전에 앞서, 파이참에서 이 라이브러리(모듈)을 설치해보도록 하자. 설치는 파이참 좌측 상단의 [File] - [Setting]을 눌러서 아래의 사진을 따라가면 된다.

그럼 이제 다시 파이참으로 돌아와서 데이터베이스에 접근하고 데이터를 저장하거나 하기 위해 사용할 함수들을 넣어줄 파일을 con_mysql이라는 이름으로 생성한 후 `sqlalchemy`를 임포트하면 되는데, 이 라이브러리에서 MySQL과 연결할 때 사용할 함수는 `create_engine` 함수이므로 아래와 같이 임포트해주도록 하자.   

## con_mysql.py ##

## DataBase
import sqlalchemy as sqlal

그렇다면 이제 `create_engine`이라는 함수를 통해 MySQL과 연결하기 위해서는 이 함수에 몇 가지 정보를 전달해주어 인스턴스를 생성한 후에 `connect()` 함수를 통해 연결해주어야 하는데, 이 때 `create_engine`에 전달해주어야 하는 내용은 아래의 코드를 살펴보도록 하자. 비밀번호는 앞서 프로그램을 설치할 때 입력해주었던 비밀번호를 입력해주면 되고, 그 뒤 부분의 데이터베이스 이름 부분은 우리가 추후에 생성한 후에 사용해주어야 한다. (아직 없는 데이터베이스에 연결하고자 하면 오류가 발생하는 게 당연하다.)

## (참고용) create_engine 사용 구조
sqlal.create_engine('mysql+mysqldb://root:비밀번호@127.0.0.1:3306/데이터베이스이름', encoding='utf-8')

## MySQL 연결 1. create_engine 인스턴스 생성
engine = sqlal.create_engine('mysql+mysqldb://root:비밀번호@127.0.0.1:3306/데이터베이스이름', encoding='utf-8')

## MySQL 연결 2. 인스턴스로 MySQL에 연결하기
connection = engine.connect()

대충 어떠한 구조로 MySQL과 연결할 수 있는지 살펴보았으니 이제 con_mysql.py 파일에서 엔진 인스턴스를 반환해줄 수 있는 함수를 하나의 캡슐로 만들어보도록 하자. 그 전에 이 함수는 기본적으로 연결하고자 하는 데이터베이스의 이름을 함께 전달받아야 하기 때문에, 함수의 인자로 `db_name`을 전달받은 후에 Line 7의 코드에서 f'~~~{}'를 활용하여 `db_name`을 곧바로 전달하도록 해서 그 데이터베이스의 인스턴스를 바로 생성할 수 있도록 하자. 그 후에 마지막으로 `connect()` 함수를 활용하여 MySQL에 접근할 수 있도록 한 후에, 그를 `conn`이라는 변수에 대입한 후 `return` 함수를 활용하여 반환해주도록 하자.

## con_mysql.py ##

## DataBase
import sqlalchemy as sqlal

def con_db(db_name):
    engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
    conn = engine.connect()
    return conn
    
    ## Line 8, 9 부분을 아래와 같이 한 줄로 작성해도 됩니다.
    return engine.connect()

이제 여기까지 구현해주었으면, MySQL과 파이참 간에 연결하는 작업은 완료된 것이다.

 


반응형
728x90

 

데이터베이스 존재 여부 확인하기

이제 파이참과 MySQL을 연결하는 작업은 모두 구현하였으니, 우리가 앞으로 사용할 데이터베이스(DataBase, DB)와 관련된 작업들을 처리해보도록 하자. 앞서 구현한 함수에서도 볼 수 있듯이 `db_name`을 하나의 인자로 전달받아 처리하고 있는데, 그와 관련된 내용을 설명하면서 '아직은 데이터베이스가 없으므로 연결하게 되면 오류가 발생한다.'고 설명했었다. 따라서, 우리는 프로그램이 실행되는 동시에 우리가 사용하고자 하는 데이터베이스의 존재 여부를 확인하고 존재하지 않는다면 생성하는 작업을 수행하는 코드를 구현할 예정이다. 이 코드도 con_mysql.py 파일 내부에서 구현해보도록 하자. 해당 기능을 수행할 클래스의 이름은 check_db이다.

## con_mysql.py ##

## DataBase
from sqlalchemy import create_engine

def con_db(db_name):
    engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
    return engine.connect()

class check_db:
    def __init__(self):
        pass

NoModule named 'MySQLdb' 오류가 발생한다면

더보기

engine의 mysqldb 부분을 pymysql로 변경해주시면 됩니다.
engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
engine = sqlal.create_engine(f'mysql+pymysql://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')

만약 pymysql이 없다는 오류가 발생한다면 파이참에서 pymysql을 설치해주시면 됩니다.

이제 데이터베이스의 존재 여부를 확인하고 만약 없다면 데이터베이스를 생성해주는 작업을 수행하는 코드를 구현해보도록 하자. 여기서 데이터베이스의 존재 여부를 확인하는 함수는 `pymysql`이나 `mysql.connector`를 사용했다면 "SHOW DATABASES"와 같은 구문을 통해 확인했었겠지만, `sqlalchemy` 라이브러리에서는 `inspect` 함수를 통해 획득한 인스턴스에 대해 `get_schema_names()` 메서드를 사용하여 데이터베이스 목록을 확인할 수 있다. 그렇다면 데이터베이스 존재 여부를 판단할 수 있는 클래스를 `class check_db`라는 이름으로 하여 새롭게 생성해주고 기존에 제작했던 `def con_db(db_name):` 함수도 이 클래스 내부로 편입시켜보자.

## con_mysql.py ##

## DataBase
import sqlalchemy as sqlal

class check_db:
    def __init__(self):
        pass

    def con_db(self, db_name):
        engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
        return engine.connect()

if __name__ == "__main__":
    check_db()

이제 MySQL과 연결되는 엔진을 새롭게 생성해주어야 하는데, con_db 함수에서 `db_name`을 인자로 받아 해당 데이터베이스에 대한 엔진을 생성했던 것과는 달리 특별한 데이터베이스를 지정하지 않고 MySQL과 연결해줄 것이다. 아래 코드 구문의 Line: 8 부분을 보면 포트번호 3306 뒤에 별도의 데이터베이스를 전달하지 않은 것을 확인할 수 있는데, 그렇게 작성하게 되면 특별한 데이터베이스와의 연결을 하는 게 아니라 MySQL 그 자체와 연결을 진행하게 된다. 다시 말해, `self.engine`은 MySQL에 대한 인스턴스이고 그 아래에 있는 함수 내부의 `engine`은 데이터베이스(`db_name`)에 대한 인스턴스인 것이다.

## con_mysql.py ##

## DataBase
import sqlalchemy as sqlal

class check_db:
    def __init__(self):
        self.engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306', encoding='utf-8')

    def con_db(self, db_name):
        engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
        return engine.connect()

if __name__ == "__main__":
    check_db()

이제 `sqlalchemy` 라이브러리(모듈)에서 제공하는 `inspect` 함수를 활용하여 MySQL 내부에 어떠한 데이터베이스들이 있는지 확인할 수 있다. 이 함수는 `subject`라는 파라미터를 인자로 전달해주어야 하는데 이 인자로는 `self.engine`을 전달해주면 된다. 그렇다면 그 결과값을 `insp`라는 변수에 입력해준 후에 `get_schema_names()`라는 함수(메서드)를 사용하여 데이터베이스 목록을 확인해보도록 하자.
※ DataBase와 Schema는 동일한 개념이다.

## con_mysql.py ##

## DataBase
import sqlalchemy as sqlal

class check_db:
    def __init__(self):
        self.engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306', encoding='utf-8')
        insp = sqlal.inspect(self.engine)
        print(insp.get_schema_names())

    def con_db(self, db_name):
        engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
        return engine.connect()

if __name__ == "__main__":
    check_db()

▶ 실행 결과 확인하기(필자의 경우에는 키움증권 Open API로 작업했던 데이터베이스 목록이 있으므로 결과값이 다를 수 있음)

더보기

['15min_data', '3min_data', '5min_data', 'all_data', 'condition', 'information_schema', 'item_data', 'kos_data', 'mysql', 'performance_schema', 'sys', '?? ??']

여기까지가 현재 MySQL 내부에 존재하는 데이터베이스를 가져오는 코드이다. 그렇다면 이제 우리가 def con_db(): 함수에 전달해주었던 `db_name`이라는 데이터베이스가 존재하는 데이터베이스인지 아닌지를 판단하기 위한 코드를 제작해보자. 일단 초기화 함수 내부에서 `get_schema_names()` 메서드를 통해 구한 데이터베이스 목록을 `DB_list`라는 변수에 담아주도록 하자. 그 다음으로는 def con_db(): 함수를 통해 특정 데이터베이스(인자로 전달한 `db_name`에 해당하는 데이터베이스)와 연결하기 전에 앞서 그 데이터베이스가 존재하는지를 확인하기 위해서는 조건문을 사용하면 된다.

다만 이 때 주의해야 할 점이 있다면 앞서 `DB_list`로만 정의해두었던 데이터베이스 목록 변수의 앞에 `self`를 추가해주어야 한다는 것이다. 여기서 `self`가 갖는 의미는 단순하게 그 클래스 내부에서라면 어디서든지 사용할 수 있는 변수임을 지정해주는 것 정도로 이해하고 넘어가면 된다. 깊게 들어가자면 또 너무 길어질테니 궁금하다면 직접 '전역 변수''지역 변수'에 대해 검색해보도록 하자. 여기서 `self`가 붙어있는 변수는 그 클래스 내부에서라면 다른 함수를 포함하여 어디서든지 사용할 수 있는 '전역 변수'가 된다. (없으면 '지역 변수'로만 사용할 수 있다.) 아래의 코드와 함께 실행 결과를 확인해보도록 하자.
※ Line: 10, 11, 14 ~ 19, 22(con_db 함수 실행시켜서 실행 결과 확인하기)

## con_mysql.py ##

## DataBase
import sqlalchemy as sqlal

class check_db:
    def __init__(self):
        self.engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306', encoding='utf-8')
        insp = sqlal.inspect(self.engine)
        self.DB_list = insp.get_schema_names()
        print(self.DB_list)

    def con_db(self, db_name):
        if db_name in self.DB_list:
            engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
            print(f"{db_name} 데이터베이스와 연결되었습니다.")
            return engine.connect()
        else:
            print(f"{db_name}는 존재하지 않는 데이터베이스입니다.")

if __name__ == "__main__":
    check_db().con_db('15min_data')

▶ 실행 결과 확인하기(15min_data 데이터베이스와 연결하고자 할 경우, 이미 존재하면 아래와 같은 결과물이 출력됨)

더보기

['15min_data', '3min_data', '5min_data', 'all_data', 'condition', 'information_schema', 'item_data', 'kos_data', 'mysql', 'performance_schema', 'sys', '?? ??']
15min_data 데이터베이스와 연결되었습니다.

▶ 실행 결과 확인하기(Line 22의 con_db()의 인자로 test라는 데이터베이스를 전달했을 때의 결과 데이터)

더보기

['15min_data', '3min_data', '5min_data', 'all_data', 'condition', 'information_schema', 'item_data', 'kos_data', 'mysql', 'performance_schema', 'sys', '?? ??']
test는 존재하지 않는 데이터베이스입니다.


더 나아가 데이터베이스의 존재 여부만을 확인하고자 할 경우에는 아래와 같이 `def _if_exist(self, db_name):` 함수와 같은 내용으로 코드를 만들고 `def con_db(self, db_name):` 함수 내부에서는 `self._if_exist(db_name)` 결과값이 True인 경우에만 해당 데이터베이스와 연결을 진행하도록 구현할 수 있다.

아래와 같이 코드를 수정하기 전과 후의 차이점을 생각해보았을 때 가장 큰 장점은 바로 '추후에 데이터베이스의 존재 여부만을 확인하고 싶을 때 유용하다.'는 점이다. 수정하기 전(def _if_exist 함수를 제작하기 전)의 경우에는 단순히 def con_db 함수를 통해 데이터베이스와 연결하는 작업만을 수행할 수 있으며 그 전에 데이터베이스의 존재 여부를 확인해주는 부가적인 작업이 있는 것이다. 하지만 수정 후(def _if_exist 함수를 제작한 후)의 경우에는 굳이 데이터베이스와 연결하지 않고도 특정 데이터베이스가 이미 존재하는지 존재하지 않는지에 대해서도 확인할 수 있고 원하는 경우에는 def con_db 함수를 사용하여 데이터베이스와 연결할 수 있다.

## con_mysql.py ##

## DataBase
import sqlalchemy as sqlal

class check_db:
    def __init__(self):
        self.engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306', encoding='utf-8')
        insp = sqlal.inspect(self.engine)
        self.DB_list = insp.get_schema_names()
        print(self.DB_list)

    def con_db(self, db_name):
        if self._if_exist(db_name):
            engine = sqlal.create_engine(f'mysql+mysqldb://root:비밀번호@127.0.0.1:3306/{db_name}', encoding='utf-8')
            print(f"{db_name} 데이터베이스와 연결되었습니다.")
            return engine.connect()

    def _if_exist(self, db_name):
        if db_name in self.DB_list:
            return True
        else:
            print(f"{db_name}는 존재하지 않는 데이터베이스입니다.")
            return False

if __name__ == "__main__":
    check_db().con_db('test')

이제 다음 게시글에서는 프로그램이 실행됨과 함께 데이터베이스의 존재 여부를 확인하고 만약 존재하지 않는다면 데이터베이스를 생성하는 기능을 구현해보도록 하자.

 

 


728x90
반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.