본문 바로가기
사이드 프로젝트/음악추천 챗봇 서비스

음악추천챗봇4.1 RDS 데이터저장 및 lambda배포

by 카프리썬_ 2021. 6. 8.
728x90
728x90

후..뭔가 진행방식이 꼬인 기분이다..

무튼 이전에 개발환경(나의 로컬환경)에서 spotify api 테스트해보았는데,

흠.. 이젠 이걸 aws의 lambda로 옮겨야한다. 아 이래서 CI/CD가 생긴건가 겁나 번거롭네...

우선 개발환경에서 다 테스트해보고, 나중에 람다로 넘길까한다. 

 

그리고 동시에 spotify API로 얻는 결과들을 AWS 데이터베이스에 저장하려고 한다. 

 

아 그리고 잊을뻔했는데 이번 프로젝트의 목표는 데이터파이프라인 구축이다.

그리고 스포티파이 API로 필요한 데이터들을 수집하고,

MYSQL, DynamoDB, S3까지 다양한 데이터저장소를 사용해서 저장하고, 사용해보는 것이다. 

RDS(mysql)에 데이터저장 

사실 RDS를 쓸지 말지 고민을 했다. 내가 알기론 dynamodb는 VPC외부에 있는 서비스라 연결이 쉽지만,

rds는 vpc 내부에서만 연결하는 db이기때문에 추가적인 설정이 많을것 같았기 때문이다.

그럼에도 언젠가 쓸 수 있는 구성이겠지 하며 시작했다.. 

 

데이터를 저장하기에 앞서 이전에 API를 테스트해보면서 그 결과와 테이블구조를 다시 한번 돌아보았다. 

Artist정보는 테이블형식을 가지고 있는 RDB에 저장하기로..! 

2021.05.31 - 음악챗봇서비스 2.1 데이터 수집 및 저장-Aritist정보 RDB저장

 

음악챗봇서비스 2.1 데이터 수집 및 저장-Aritist정보 RDB저장

어떤 데이터를 수집할 것인가? 이에 대한 대답을 하기 위해서 어떤 정보를 나타낼것인지 어떤 데이터가 필요한지 정해야한다. 그래서 일단 가장 간단하게 '아티스트명'으로 검색한 '아티스트 정

pearlluck.tistory.com

 

로컬환경(개발환경)에서 MySQL 생성

RDS에 저장하기전에, 나는 나의 로컬환경에서 먼저 테스트를 해보았다.

(테스트하려고 로컬환경에 MYSQL도 깔았다..) >> mysql 설치하기 

create database musicdb;

create table artists
( artist_id varchar(255) primary key,
artist_name varchar(255),
followers int(11),
popularity int(11),
artist_url varchar(255),
image_url varchar(255)
);

 

백엔드 코드작성1- insert/select 로직구현 

insert하면서 알게 된 건데 mysql에선 문자열에 ''를 넣어야한다..

pymysql.err.OperationalError: (1054, "Unknown column 'BTS' in 'where clause'")

위의 에러는 아래의 코드에서 발생한 문제이다.

select_query="SELECT * from artists where artist_name ={}".format(artist_item['name'])

구글링해보니 문자열을 인식하지 못한것으로 보아 '{}'로 변경해서 해결했다. 

 

백엔드에서 insert하고 select하는 로직은 간단하다.  

  • 카카오 챗봇으로 통해 입력한 '아티스트명'이 mysql에 있는 경우 select -> 카카오봇return 
  • 없을 경우, Search API를 통해 리턴받은 결과를 mysql에 insert 

get_artist(artist_name,headers) 코드 (스포티파이api결과를 받아온 이후 해야할작업)

더보기
  #searchAPI 정상적으로 가져온 경우
    artist_item= search_ar['artists']['items'][0]
    #DB에 있으면,mysql에 검색
    select_query="SELECT artist_id,artist_name,image_url from artists where artist_name ='{}'".format(artist_item['name'])
    cursor.execute(select_query)
    db_result = cursor.fetchall()
    #print("select row 성공")
    #print(db_result)

    #db에 있는거면, select결과 리턴
    if len(db_result)>0:
        #id,name,url=db_result[0]
        return db_result[0]

    #DB에 없으면,mysql에 insert row 저장
    artist_data ={
        'artist_id' : artist_item['id'],
        'artist_name' : artist_item['name'],
        'popularity' : artist_item['popularity'],
        'followers' : artist_item['followers']['total'],
        'artist_url' : artist_item['external_urls']['spotify'],
        'image_url' : artist_item['images'][0]['url'],
        #'genres' : artist_item['genres']
    }
    insert_row(cursor,artist_data,'artists')
    conn.commit()
    #print("insert row 성공")

    #db에 없는거면 () 리턴
    return db_result

 

백엔드 코드작성2- 카카오챗봇 response 메세지 구현 

위의 두가지 경우에 따라 response되는 상황이 다르다. 이에 따라서 메세지를 작성했다. 

카카오에서는 response메세지를 아래와 같은 몇가지 형식으로 제공된다. 

그리고 형식에 따라서 리턴하는 json형식이 다르다. 그래서 이에 맞춰줘야한다...후..

왼쪽부터 심플타입, 베이직타입, 리스트타입, 

  • 심플타입 : 단순한 텍스트출력 메세지
  • 베이직타입 : 이미지와 설명, 버튼이 함께 있는 카드 형태의 메시지
  • 리스트타입 : 여러 줄로 구성된 리스트형 메시지 (제목에 이미지를 넣을 수 없는것으로 변경)

일단 나는 심플타입으로 우선진행했다. (바뀔수 있음)
db에 없는 아티스트를 검색했을때는 이렇게

{'version': '2.0', 'template': {'outputs': [{'simpleText': {'text': '오! 새로운 아티스트인걸?! ㄴ저장ㄱ 다시검색 해보세요'}}]}}

db에 있는 아티스트를 검색했을때는 이런 형식으로 메세지형식을 만들었다. 

{'version': '2.0', 'template': {'outputs': [{'simpleText': {'text': '님이 알고 싶은 아티스트 BTS 맞나요?!'}}]}}

 

응답별 메시지 타입은 공식문서를 참고했다. 

 

 

백엔드 코드작성3- 메인함수 구현 및 insert/select 테스트

테스트를 하기 위해 실제처럼 트리거가 발생하는 상황이 필요하다.

그래서 나는 카카오챗봇에서 제공하는 request JOSN형식으로 입력받고,

lambda_handler()함수를 메인함수처럼 테스트를 진행했다.

또는 람다 로그를 통해 event가 어떤 형식인지 로그를 찍고 그걸 계속 이용하면 될듯! 

 

이렇게 된 JSON 형식의 request메세지이다. 이를 테스트코드로 사용했다. 

스킬테스트할때 저장해두었다.  예를 들어 카카오챗봇을 통해 'bts'라는 아티스트명을 입력했을때 request이다. 

더보기
{
  "intent": {
    "id": "tkjvgti4rz87t5ryzm6zd93m",
    "name": "블록 이름"
  },
  "userRequest": {
    "timezone": "Asia/Seoul",
    "params": {
      "ignoreMe": "true"
    },
    "block": {
      "id": "tkjvgti4rz87t5ryzm6zd93m",
      "name": "블록 이름"
    },
    "utterance": "발화 내용",
    "lang": null,
    "user": {
      "id": "125695",
      "type": "accountId",
      "properties": {}
    }
  },
  "bot": {
    "id": "60b628c87e223a78e8750a68",
    "name": "봇 이름"
  },
  "action": {
    "name": "0ux7czbb0t",
    "clientExtra": null,
    "params": {
      "group": "bts"
    },
    "id": "iw3qo984604vmxofkf2xvk2r",
    "detailParams": {
      "group": {
        "origin": "bts",
        "value": "bts",
        "groupName": ""
      }
    }
  }
}

 

무튼 저런 request가 들어왔다고 직접 입력하면서  테스트를 진행했다. 

마치 누군가가 bts라는 아티스트명을 처음으로 입력한 것 같은 상황이다. 

이떄 db에 새로 데이터가 insert 되는걸 확인했다. 

또한 같은 아티스트명을 한번더 입력한것 같은 상황일때를 테스트해보니 select결과도 이렇게 확인되었다. 

 

이후 리턴받은 아티스트id를 저장해서 top_tracks 데이터를 가져와야한다. 

이 track에 대한 데이터들은 dynamodb에 저장할 계획이다. 

 

또한, select한 결과를 이제 카카오봇으로 리턴시켜줘야 한다. 

 

메인함수 코드보기 >>

더보기
def lambda_handler(event):
	
    #카카오챗봇으로 입력받은 발화명 내용 
    artist_name = event['userRequest']['utterance']
    
    search_result = get_artist(artist_name,get_header())
    #print(search_result)

    #select결과가 있을때
    if search_result:
        id,name,url=search_result
        result = response_select(name)
    #select결과가 없을때
    else:
        result=response_insert()

    print(result)

    return {
        'statusCode':200,
        'body': json.dumps(result),
        'headers': {
            'Access-Control-Allow-Origin': '*',
        }
    }

 

 

AWS로..!!RDS생성 및 Lambda배포

그리고 이제 코드가 확인이 되었으니 이걸 AWS환경으로 이관한다.  

사실 처음부터 바로 AWS에서 작업을 해도 된다, 하지만 개발을 하면서 비용이 들까봐 나는 로컬에서 진행한 뒤 

마이그레이션(?)처럼 변경하고 진행했다. 사실 대부분은 AWS에서 같은 개발환경을 만들텐데..나는 거지라서..

 

그래서 RDS를 생성하고, Lambda코드를 업데이트 했다. 

 

1. RDS생성 

사실 데이터베이스는 외부에선 접속이 되지 않도록 prviate subnet에 구성하는게 일반적이다. 

나의 서비스상 public에 두어도 큰 문제는 없어서 (사실 비용이 걱정되서..) public subnet에 둘 예정이다. 

혹시 private상에서 rds를 생성해서 람다와 연결한다면 여길 참고하자  

  • 퍼블릭액세스 가능 -예 (private에서만 접근이 가능하도록 설정하려면 '예')
  • 보안그룹 새로생성  :  3306 인바운드 오픈 
  • 이외 백업, 모니터링 등 추가설정 옵션들 대부분 비활성화 (프리티어 버전이라 멀티AZ도 아니다)

RDS생성이 완료되면, 엔드포인트와 데이터베이스 이름을 잘 기억해두자. 

 

아, 그런데 퍼블릭하게 RDS를 생성하던 중에 아래와 같은 오류가 생겼다. >>구글링은 여기에서 함 

구글링해보니 RDS를 퍼블릭하게 생성할 경우 엔드포인트가 생성이 되는데 그게 내부IP로 잡힌다는 소리같다

그래서 VPC로 들어온 주소가 외부로 나갈 수 있는지 IGW를 확인해봤는데 잘 연결되어 있었다.

그리고 VPC에서 'DNS 호스트네임'과, DNS확인'을 편집해서 활성화 시켰다. 

 

2.Lambda에서 rds연결 (pymysql 레이어추가) >> 여기참고 

람다에선 파이썬의 패키지를 사용하기 위해선 레이어를 사용해야한다 >> 레이어 참고 

2021.06.10 - [AWS] 람다에서 Python 패키지 사용하기 | Layer(계층)란?

 

그리고 위에서 테스트를 진행했을때 연결한 DB정보를 바꿔준다. 

  • rds_host ='public상에 있는 DB의 엔드포인트'
  • rds_user ='admin'
  • rds_pwd = 'RDS 생성시 지정한 비밀번호'
  • rds_db = 'RDS 생성시 지정한 데이터베이스명'

 

아, 그리고 mysql workbench를 써서 public으로 오픈된 db에 접근해서 테이블을 생성해야한다. 

로컬에선 hostname이 localhost였는데, aws rds를 연결하는 경우에는 rds의 엔드포인트를 hostname으로 넣어주면 됨.

 

https://ichi.pro/ko/pymysqleul-sayonghaneun-rdswa-aws-lambda-39226957514440

 

pymysql을 사용하는 RDS와 AWS Lambda

이 자습서에서는 pymysql 라이브러리를 사용하여 RDS 데이터베이스에서 데이터를 쿼리하는 Lambda 함수를 생성하는 방법을 보여줍니다. 1.

ichi.pro

 

 

728x90
반응형