AWS

AWS를 이용한 서버리스 아키텍처 구축하기

위니윈 2023. 5. 12. 21:27

이번 포스팅에서는 AWS를 이용한 서버리스 아키텍처를 구성하기 위해 Api gateway와 s3를 이용해 S3에 이미지를 업로드하는 API와 aws lambda function을 이용해서 api를 구성하는 방법에 대해 알아보도록 하겠습니다.

 

1. API gateway + S3 : 이미지 업로드 API 만들기

 

먼저 API Gateway에 대한 IAM 역할을 만들어야 합니다.

IAM 역할에 들어가서 역할 만들기를 합니다.

 

신뢰할 수 있는 엔터디 유형에서 AWS 서비스, API Gateway를 선택합니다.

 

s3에 대한 권한은 나중에 추가할 것입니다. 지금 보이는 AmazonAPIGateway 권한은 API Gateway가 로그를 사용자 계정으로 푸시하도록 허용하는 권한입니다. 우선 다음으로 넘어갑니다.

역할 이름을 설정합니다. 저는 api-gateway-upload-to-s3로 설정했습니다. 이제 역할을 생성할 수 있습니다.

 

역할이 생성되었다는 메시지가 안내됩니다.

 

새로 생성한 역할을 선택하면 아래와 같은 상세 정보가 뜹니다. 여기서 권한 추가를 해보도록 하겠습니다. 우리는 인라인 정책을 생성할 것입니다.

 

서비스에는 s3를 검색합니다. 

 

작업은 GetObject와 PutObject를 선택합니다. S3에 이미지를 업로드하고 읽어와야 하기 때문입니다. 리소스는 특정 리소스를 선택하겠습니다. 그리고 미리 생성해뒀던 s3 bucket의 ARN을 붙여넣으면 됩니다.

 

이에 대한 정책 명으로 저는 s3-image-upload를 선택했습니다. 

 

기존의 api-gateway-upload-to-s3 역할에 s3-image-upload 권한이 새롭게 추가되었음을 확인할 수 있습니다.

 

이제 API Gateway와 연결할 차례입니다.

API Gateway에 새로운 API를 만들 때 저는 REST API로 구축해보도록 하겠습니다.

 

API명은 imageUpload로 정했습니다.

 

API에 대한 리소스를 생성해보도록 합니다.

 

저장할 폴더명을 리소스로 만들 예정입니다.

 

{folder} 하위 항목에 다시 한번 하위 리소스를 만들어 줍니다.

 

완성된 리소스는 다음과 같습니다.

 

이제 메소드 작업을 할 차례입니다.

먼저 PUT 메소드를 생성하면 됩니다.

 

PUT 메소드에 대한 설정을 아래와 같이 하면 됩니다. 이 때 실행 역할은 우리가 맨 처음에 만들었던 역할에 대한 ARN을 적으면 됩니다.

 

이제 PUT 메서드에 대한 파라미터 매핑을 구성할 차례입니다.

통합 요청을 눌러주세요.

 

통합 요청의 URL 경로 파라미터를 확장해봅니다. 이름에는 우리가 저장하고자 하는 s3 bucket명을 저장하고, 다음에서 매핑됨에는 method.request.path.folder를 입력해주면 됩니다.

 

이 과정을 한번 더 반복해서, key와 method.request.path.object로 저장합니다.

 

이제 API에 대한 이진 미디어 설정을 해보겠습니다. API gateway에서 설정을 누르고 이진 미디어 형식을 확인합니다. /로 처리할 경우 모든 미디어 파일을 이진 미디어로 설정할 수 있습니다.

 

이제 API 배포만이 남았습니다.

 

새 스테이지를 선택해서 v1으로 배포하면 성공적으로 API가 개발되었음을 알 수 있습니다.

 

그러면 이 과정을 더 응용해봅시다.

이번에 우리는 aws lambda를 이용해서 api를 만들어보겠습니다.

 

함수 생성을 누르고 만들 함수를 지정해주세요. 함수 이름은 자유롭게 적으면 됩니다. 저는 파이썬이 더 익숙하기 때문에 런타임을 python으로 선택하도록 하겠습니다.

 

저는 myHostEvent를 생성했습니다.

 

먼저 코드를 짜봅시다. 저는 특정 아이디가 host인 모든 행사 목록을 불러오는 API를 개발하고자 합니다.

import boto3
import json
import pymysql

# 데이터베이스 연결 정보
db_host = 'your host'
db_name = 'your database'
db_user = 'your userid'
db_password = 'your password'
db_port = 3306

# RDS 연결
def connect_to_db():
    try:
        conn = pymysql.connect(host=db_host, user=db_user, passwd=db_password, db=db_name, connect_timeout=5)
    except Exception as e:
        print("Unable to connect to the database")
        print(e)
        return None
    return conn
    
def get_host_event(hostid):
    conn = connect_to_db()
    if conn is None:
        return None
    cursor = conn.cursor()
    
    query = "SELECT tb_event.event_id, event_name, event_date, event_loc FROM tb_event, tb_host WHERE tb_host.user_id = %s and tb_host.event_id = tb_event.event_id"
    cursor.execute(query, (hostid))
    
    result = cursor.fetchall()
    
    # date 타입을 문자열로 변환하고 딕셔너리 형태로 변경
    result = [{"event_id": id, "event_name": name, "event_date": date.strftime('%Y-%m-%d'), "event_loc": loc} for (id, name, date, loc) in result]
    cursor.close()
    conn.close()
    return result
    
    
def get_all_events():
    conn = connect_to_db()
    if conn is None:
        return None
    cursor = conn.cursor()
    
    query = "SELECT event_id, event_name, event_date, event_loc FROM tb_event"
    cursor.execute(query)
    
    result = cursor.fetchall()
    
    # date 타입을 문자열로 변환하고 딕셔너리 형태로 변경
    result = [{"event_id": id, "event_name": name, "event_date": date.strftime('%Y-%m-%d'), "event_loc": loc} for (id, name, date, loc) in result]
    cursor.close()
    conn.close()
    return result
    

# Lambda 함수
def lambda_handler(event, context):
    
    hostid = event["queryStringParameters"].get("hostid")
    if hostid:
        events = get_host_event(hostid)
    else:
        events = get_all_events()
    
    return events

 

이 코드는 꽤 간단합니다. 먼저 데이터베이스 연결 정보는 RDS에 연결하기 위한 기본적인 정보라고 생각하면 됩니다. 그리고 RDS에 접속하는 코드를 작성합니다. 그런데, 저는 query param으로 hostid가 주어질 경우, 특정 유저가 주최한 모든 행사를 보여주고, 아무것도 주어지지 않을 때에는 현재 오픈되어 있는 모든 행사를 보여주고자 합니다. 따라서 함수를 get_host_event와 get_all_events로 나눴습니다. 다음으로는 원하는 정보를 얻을 수 있도록 sql 쿼리를 짭니다. 결과는 딕셔너리 형태로 저장했습니다. 그리고 마지막으로 결과물을 return 하는 간단한 코드입니다.

 

문제는 이 코드에 import pymysql이 쓰였다는 점입니다. 바로 실행하면 오류가 날 것입니다. 우리는 이 라이브러리를 lambda 함수에 layer로 직접 넣어줘야 합니다.

 

자, 로컬에 python 폴더를 만들어보도록 하겠습니다.

 

이제 cmd를 켜서 python 폴더로 이동하고 아래 명령어를 입력해보겠습니다. 그러면 위와 같이 패키지가 다운로드 되는 것을 확인할 수 있습니다.

pip install pymysql -t .

 

이 python 폴더를 zip 파일로 압축해주면 됩니다.

저는 헷갈리는 것을 방지하기 위해 pymysql.zip으로 파일명을 변경했습니다.

 

다시 lambda 함수로 돌아옵시다. 내가 만든 함수에서 벗어나 계층을 보면 계층 생성이 있습니다.

 

이름은 pymysql로 하는 것을 권장합니다. 그리고 zip 파일 업로드로 방금 압축한 파일을 올려주면 됩니다. 호환 런타임은 lambda 함수를 생성했을 때 지정했던 런타임으로 선택하면 됩니다.

 

이제 다시 lambda 함수로 돌아와서 계층을 누르면 add a layer가 보입니다.

 

사용자 지정 계층을 누르면 우리가 업로드했던 계층을 선택할 수 있습니다. 추가해줍시다.

 

이제 api gateway를 만들면 됩니다. 과정은 위와 대부분 유사합니다.

다만 차이점이 있다면 Get 메소드를 만들어줬고, lambda 함수와 연결하기 위해서 통합 유형을 lambda 함수로 설정했습니다. lambda 함수란에는 우리가 연결하고자 하는 lambda 함수명을 적으면 됩니다.

 

이후 과정도 동일합니다. 배포를 마친 후 포스트맨에서 확인해보겠습니다.

 

정상적으로 출력되는 것을 확인할 수 있습니다. 이제 우리는 이 모든 과정을 응용해서 AWS를 이용해 서버리스 아키텍처 구축을 위한 API를 생성할 수 있습니다.

 

감사합니다.

'AWS' 카테고리의 다른 글

AWS RDS 프리티어로 설정하기  (0) 2023.03.29
졸업프로젝트 아키텍처에 대한 고민  (0) 2023.03.22
EC2 vs Elastic Beanstalk  (0) 2023.03.22
AWS 협업을 위한 IAM 설정  (0) 2023.03.21