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">&times;</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

+ Recent posts