1. 서버리스란?
- 관리해야 할 서버가 없고, 우리가 원하는 기능, 설정값들만 맞춰주면 알아서 정적 웹 사이트 기능을 관리해주는 것
- 서버리스라고 서버가 없는게 아니고 어딘가 존재하지만 우리는 몰라도 됩니다.
2. 람다란?
- Lambda는 S3처럼 백엔드를 서버리스(Serverless)로 운영할 수 있는 서비스입니다. S3가 별도의 서버, 관리 없이도 프론트 페이지를 운영할 수 있는 것처럼, 백엔드도 인프라를 신경쓰지 않고 운영할 수 있는 서비스입니다. S3와 Lambda의 역할은 비슷하다고 생각하시면 됩니다.
- 함수 단위로 배포
- Lambda 장점
- 비용절감 - 필요할때만 함수가 호출되고 비용이 부과되는 방식입니다. (최대 34% 더 나은 가격 대비 성능을 제공)
- 인프라 관리 부담의 효율 - 관리를 할 필요가 없다.
- 빠르게 백엔드를 구성 가능
- Lambda 단점
- 리소스 제한 - 메모리(최대 10GB), 처리시간(최대 900초, 15분)
- Cold Start - 오랫만에 실행하게 되면 딜레이가 발생한다.
- 동시성 제약 - 동시에 처리할 수 있는 요청의 수가 리전별로 1000개로 제한되어 있다.
백엔드 예제
import json
import boto3
import pymysql
from datetime import date
import math
def get_secret():
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name="ap-northeast-2"
)
get_secret_value_response = client.get_secret_value(
SecretId='rds-secret-01'
)
token = get_secret_value_response['SecretString']
return eval(token)
def db_ops():
secrets = get_secret()
try:
connection = pymysql.connect(
host=secrets['host'],
user=secrets['username'],
password=secrets['password'],
db='mydb',
charset='utf8mb4',
cursorclass=pymysql.cursors.DictCursor
)
except pymysql.MySQLError as e:
print("connection error!!")
return e
print("connection ok!!")
return connection
def lambda_handler(event, context):
paramType = event['queryStringParameters']['type']
conn = db_ops()
cursor = conn.cursor()
try:
if paramType == 'write':
if event['httpMethod'] == 'OPTIONS':
body = json.dumps({
"message": "success",
})
else:
today = date.today()
body = json.loads(event['body'])
cursor.execute("insert into bbs(title, content, regDate) value('" + body['title'] + "', '" + body['content'] + "', '" + today.strftime("%Y%m%d") + "')")
conn.commit()
body = json.dumps({
"message": "success",
})
elif paramType == 'list':
paramWord = event['queryStringParameters']['word']
if not paramWord:
cursor.execute("select idx, title, regDate from bbs")
result = cursor.fetchall()
else:
cursor.execute("select idx, title, regDate from bbs where title like '%"+paramWord+"%'")
result = cursor.fetchall()
body = json.dumps({
"result": "success",
"data": result
})
elif paramType == 'read':
idx = event['queryStringParameters']['idx']
cursor.execute("select * from bbs where idx="+idx)
bbs = cursor.fetchone()
body = json.dumps({
"result": "success",
"data": bbs
})
elif paramType == 'delete':
idx = event['queryStringParameters']['idx']
cursor.execute("delete from bbs where idx="+idx)
conn.commit()
body = json.dumps({
"message": "success",
})
elif paramType == 'deleteAll':
idxs = event['queryStringParameters']['idxs']
cursor.execute("delete from bbs where idx in("+idxs+")")
conn.commit()
body = json.dumps({
"message": "success",
})
return {
"statusCode": 200,
#Cross Origin처리
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST,GET,DELETE'
},
"body": body,
}
except Exception as e:
print(e)
return {
"statusCode": 500,
#Cross Origin처리
'headers': {
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST,GET,DELETE'
},
"body": json.dumps({
"message": "fail",
}),
}
프론트 예제
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>게시판</title>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
crossorigin="anonymous">
<!-- JS -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
crossorigin="anonymous"></script>
<!-- 구글폰트 -->
<link href="https://fonts.googleapis.com/css?family=Stylish&display=swap" rel="stylesheet">
</head>
<style type="text/css">
* {
font-family: "Stylish", sans-serif;
}
.wrap {
width: 900px;
margin: auto;
}
#post-box {
width: 500px;
margin: 20px auto;
padding: 50px;
border: black solid;
border-radius: 5px;
}
</style>
<script>
$(document).ready(function () {
getList();
});
function openClose() {
if ($("#post-box").css("display") == "block") {
$("#post-box").hide();
$("#btn-post-box").text("포스팅 박스 열기");
} else {
$("#post-box").show();
$("#btn-post-box").text("포스팅 박스 닫기");
}
}
let url = 'https://f3t7bvcxia.execute-api.ap-northeast-2.amazonaws.com/default/myFirstLambda';
function getList() {
let word = $("#word").val();
$.ajax({
url: `${url}?type=list&word=${word}`,
method: "GET",
success: function (result) {
clear()
let titleList = result['data'];
for (let i = 0; i < titleList.length; i++) {
let data = titleList[i]
$('#list').append(`<tr>
<th scope="row">3</th>
<td><input class="form-check-input" type="checkbox" name="idxs" value="${data['idx']}"></td>
<td><a href="#" onclick="getContent(${data['idx']})">${data['title']}</td>
<td>${data['regDate']}</td>
</tr>`);
}
},
})
}
function clear() {
$('#list').empty();
$('#page').empty();
}
function writeContent() {
let title = $("#title").val();
let content = $("#content").val();
$.ajax({
url: `${url}?type=write`,
method: "POST",
contentType: "application/json",
data: JSON.stringify({title: title, content: content}),
success: function (result) {
alert("저장되었습니다!!");
location.reload();
},
})
}
function getContent(idx) {
g_idx = idx;
$.ajax({
url: `${url}?type=read&idx=${idx}`,
method: "GET",
success: function (result) {
$("#readTitle").html(result['data']['title'])
$("#readContent").html(result['data']['content'])
$('#exampleModal').modal('toggle')
},
})
}
let g_idx = 0;
function deleteContent() {
$.ajax({
url: `${url}?type=delete&idx=${g_idx}`,
method: "DELETE",
success: function (result) {
$('#exampleModal').modal('toggle');
getList(1);
},
})
}
function deleteAllContent() {
var checkedAry = [];
$.each($("input[name='idxs']:checked"), function () {
checkedAry.push($(this).val());
});
console.log(checkedAry);
$.ajax({
url: `${url}?type=deleteAll&idxs=${checkedAry}`,
method: "DELETE",
success: function (result) {
getList(1);
},
})
}
</script>
<body>
<div class="wrap">
<div class="jumbotron">
<h1 class="display-4">게시판</h1>
<p class="lead"></p>
<hr class="my-4">
<p class="lead">
<button onclick="openClose()" id="btn-post-box" type="button" class="btn btn-primary">포스팅 박스 열기
</button>
</p>
</div>
<div id="post-box" class="form-post" style="display:none">
<div>
<div class="form-group">
<label for="title">제목</label>
<input id="title" class="form-control" placeholder="">
</div>
<div class="form-group">
<label for="content">내용</label>
<textarea id="content" class="form-control" rows="2"></textarea>
</div>
<button type="button" class="btn btn-primary" onclick="writeContent()">기사저장</button>
</div>
</div>
<nav aria-label="Page navigation example">
<div class="form-inline md-form mr-auto mb-4">
<input class="form-control mr-sm-2" type="text" id="word" placeholder="Search" aria-label="Search">
<button class="btn aqua-gradient btn-rounded btn-sm my-0" onclick="getList()">Search</button>
</div>
</nav>
<div id="cards-box">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col"></th>
<th scope="col">타이틀</th>
<th scope="col">등록일자</th>
</tr>
</thead>
<tbody id="list">
</tbody>
</table>
</div>
<div>
<nav aria-label="Page navigation example">
<div class="form-inline md-form mr-auto mb-4">
<button class="btn aqua-gradient btn-danger btn-sm my-0" onclick="deleteAllContent()">삭제</button>
</div>
</nav>
</div>
<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel"><span id="readTitle"></span></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" id="readContent">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" onclick="deleteContent()">Delete</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
</body>
</html
람다(AWS) + 파이썬 + 시크릿매니저(AWS) 프로잭트 배포 방법
강의 제공 자료
1. 세팅방법 : https://teamsparta.notion.site/1-a69f78c7ef2748fe8bd23c07dc1b140f
2. 코드 사용방법 : https://teamsparta.notion.site/2-0dd1026bdc1240d881b1580805273082
(람다로 기능 구현시 각각의 기능을 람다 하나하나로 만들어야 하지만 강의 편의상 한 곳에서 적용)
======== 배포 자동화 ========
위의 노가다를 자동화 시킴
2022.05.31 - [프로그래밍/AWS] - Sam + Lambda + python 배포자동화
'프로그래밍 > AWS' 카테고리의 다른 글
Sam + Lambda + python 배포자동화(2) + docker (0) | 2022.06.02 |
---|---|
Sam + Lambda + python 배포자동화(1) (0) | 2022.05.31 |
AWS Secrets Manager (0) | 2022.05.27 |
Springboot AWS 배포 세팅 (0) | 2022.05.27 |
서버리스 프론트엔드 (0) | 2022.05.26 |