원래 봇 로그 데이터 저장엔 Pandas를 활용해 csv 형태로 저장했지만,
Cloudtype 상에서 csv를 확인한다는 게 여간 귀찮은 일이 아니었다.
접근성이 떨어지는 만큼 보려는 의욕 자체가 떨어진다고 할까...
그래서 이 불편함을 타파하고 나름대로 DB 공부를 해보고자 이걸 DB로 전부 교체하기로 결정했다.
원래는 Cloudtype에서 제공하는 MariaDB 템플릿을 쓰려고 했지만...
HeidiSQL 상에서 접속을 시도했는데 안되더라?
클라우드에서 제공한 가이드도 현재의 UI랑 달랐다.
외부에서 TCP 접속이 막힌 느낌이긴 한데 그냥 안 되는 김에 다른 DB를 찾아보기로 했다.
집에 있는 라즈베리파이를 DB로 쓸까 잠깐 스쳐 지나가듯 생각했지만 파기.
적은 트래픽을 사용한다면 무료로 쓸 수 있는 DB가 뭐가 있냐 하니 역시 떠오르는 건 AWS.
그중에 눈에 띈 것은 「DynamoDB」(이하 "다이나모")였다.
이름이 맘에 들었다. FF14를 오래 플레이해 온 플레이어가 낯선 곳에서 발견한
다이나모라는 말에 눈을 돌리지 않을 수 있을까.
1. DynamoDB
다이나모는 NoSQL이다. Not only SQL의 준말.
자세한 것까지 설명하면 본말전도니 다음 링크를 참고하라.
https://www.oracle.com/kr/database/nosql/what-is-nosql/
여하튼 서버리스고 NoSQL은 접해본 적이 없기 때문에 이걸 써보기로 결정했다.
일반적인 관계형 DB만 써본 나에겐 도전이었다 할 수 있다.
1-1. 테이블
다이나모에서의 테이블은 일반 관계형 DB의 테이블과 크게 다르지 않다.
하지만 다이나모의 테이블은 그 구성이 조금 다르고 스키마라는 개념이 없는 것처럼 보인다.
위 이미지를 보면 「파티션 키」와 「정렬 키」라는 것이 보일 것이다.
정렬 키는 대충 느낌이 오는데 파티션 키는 감이 안 온다.
파티션 키는 관계형 DB의 기본 키와 유사한 개념이라 볼 수 있다.
정렬 키와 같이 복합 키의 형태로 사용할 수 있다.
여하튼 난 위처럼 기본 테이블을 구성했다.
line() 명령어 하나에 저 3개의 테이블을 다 쓰고, 2개는 사실상 명령어 전용으로 만들어 둔 것이기도 하다.
아직까진 NoSQL을 제대로 활용하진 못했다.
일단은 작동에 의의를 뒀다.
1-2. Python 코드 작성
별 중요치 않은 소가 길어졌는데, 어쨌든 Python용 AWS SDK인 「boto3」를 사용해 코드를 작성해야 했다.
import boto3
from botocore.exceptions import ClientError
from boto3.dynamodb.conditions import Key, Attr
import json
with open("./keys.json", 'r') as f:
cfg = json.load(f)
Accees_Key_ID = cfg['AwsKeys']['Accees_Key_ID']
Secret_Access_Key = cfg['AwsKeys']['Secret_Access_Key']
class awsDynamo:
"""A Class that handle AWS DynamoDB"""
def __init__(self):
super().__init__()
# Get the service resource.
awsDynamo.dyn_resource = boto3.resource('dynamodb', region_name='ap-northeast-2',
aws_access_key_id=Accees_Key_ID,
aws_secret_access_key=Secret_Access_Key)
awsDynamo.table = None
위처럼 클래스 운을 뗐다.
외부 클라우드에 올려서 관리할 것이기 때문에 별도의 API 키를 발급받아 사용한다.
그리고 아래와 같은 함수들을 여럿 구현해 DB를 사용할 준비를 했다.
# Check there is a user on the list who reqeust entry
@staticmethod
def checkExist(lNum, userID):
awsDynamo.table = awsDynamo.dyn_resource.Table('entrylist')
rs = awsDynamo.table.query(
ProjectionExpression="entryuserID",
KeyConditionExpression=Key('linenumber').eq(lNum) & Key('entryuserID').eq(userID),
ScanIndexForward = False,
Limit=1
)
try:
if (len(rs['Items']) == 0):
awsDynamo.table = None
return False
if (userID == rs['Items'][0]['entryuserID']):
awsDynamo.table = None
return True
except:
awsDynamo.table = None
raise
와 쿼리를 하는 게...
뭔가 공식 문서를 봐도 머리에 들어오지 않았다.
구글과 스택 오버플로우가 없으면 코드 몽키 하기도 쉽지 않겠다 싶었다.
구글 검색 엔진의 무한한 은혜 덕에 내게 필요한 사용법을 배우고 겨우 적용했다.
이걸 봇의 다른 코드들에 적용하면 대충 이런 느낌이다.
#Logging function
@staticmethod
def pushLog(lTypes, content, datetime):
from Database.dynamo import awsDynamo as ad
handle = ad()
data = {
"LOG": "LOG",
"logNumber": handle.getLogNumber() + 1,
"content": content,
"dateTime": f'{datetime}',
"logType": lTypes
}
handle.push(data, 'log')
json 형태로 데이터를 미리 만들어 두고 DB에 push 하는 느낌으로 진행이 된다.
위에서 클래스를 런타임에서 불러온 이유는 import time에서 클래스를 불러오면,
Circular import 오류가 생기기 때문에 런타임에서 불러온 것이다.
2. Bot의 변경점
사실 아직도 다이나모가 어떤 것인지 잘 모르겠다.
중요한 건 봇을 어떻게 업데이트했냐가 되겠다.
2-1. DB의 도입
이전에 사용하던 csv 포맷도 DB라면 DB라고 할 수 있지만 접근성에 문제가 있었다.
다이나모로 싹 갈아치우니 보기도 좋더라.
당연히 관리도 편해진다.
pandas는 당연히 더 이상 사용하지 않는다.
2-2. 줄 나가기 버튼 추가
위처럼 나가는 버튼 하나 만들었다.
줄을 설 수 있으면 빠질 수도 있어야 하지 않겠는가?
사용자들의 요구는 없었지만 그냥 만들었다.
2-3. 로깅 클래스 수정
원래 로깅 클래스는 아래와 같은 형태로 만들어져 있었다.
Logger.writeLog(1, 'Blah Blah')
정보면 1이고, 에러면 2고 뭐 이런 식으로 해놨는데 이건 나도 쓰기 불편했다.
그래서 아래와 같은 형태로 쓸 수 있게 클래스를 수정했다.
Logger.info(f"There was no Entry at {interaction.user.display_name}'s {prize} Line.")
별도의 함수들을 만들어서 정보, 에러, 디버그 등을 직관적으로 구분해 로깅할 수 있도록 했다.
3. 결론?
역시 사람은 익숙한 게 짱이다.
하지만 NoSQL 사용을 시도해서 손해를 봤다는 것은 아니다.
하나하나 알아가는 게 또 중요한 것이 아니겠나?
야밤에 쓰고 있자니 글이 너무 두서없긴 한데,
어색하거나 내용이 빠진 건 나중에 글을 수정해서 채워 넣을 수 있도록 해야겠다...
'Study > Python' 카테고리의 다른 글
[Python] 단위 변환 (0) | 2023.05.21 |
---|---|
[Python / GPT] 봇 코드 리팩터링 (0) | 2023.05.17 |
[Python] 로깅 클래스 추가 (0) | 2023.04.03 |
[Python] 봇에 Epoch Converter 추가 (0) | 2023.03.29 |
[Python] 디스코드 봇에 환율 확인 기능 추가 (0) | 2023.02.12 |