[AWS] Web Application with Serverless μ€μ΅
νκ·Έ: API Gateway, AWS, DynamoDB, Lambda, S3
μΉ΄ν κ³ λ¦¬: AWS
AWS TechCamp μλ²λ¦¬μ€ κ΄λ ¨ μν¬μ΅ μμ½ μ 리
μ€μ΅ κ³Όμ μμ½
κ° μλΉμ€μ μν
- S3 : μ μ μΉμ¬μ΄νΈ νΈμ€ν
- DynamoDB : κ°λ¨ν μΉ μ΄ν리μΌμ΄μ μ λ°μ΄ν°λ₯Ό μ μ₯νλ μ©λ. no-sql κΈ°λ°.
- API Gateway : μλν¬μΈνΈμ REST API λ₯Ό κ΄λ¦¬ν¨. μ¬μ©μκ° μ€μ ν λΌμ°ν μ€μ μ λ°λΌ κ° μλν¬μΈνΈλ₯Ό λ리νμ¬ μμ²κ³Ό μλ΅μ λ°λ νλ‘μ μν μ νλ€. lambda μ μ°κ²°.
- Lambda : μ± μλ²
DynamoDB μμ±
RDBMS μ κ²½μ°, μ€ν€λ§λ₯Ό μ μνκ³ μ΄ μ€ν€λ§λ₯Ό μ΄μ©ν΄ λ°μ΄ν°λ₯Ό μ μ₯νκ² λλ€. κ·Έλ¬λ DynamoDB μ κ°μ NoSQL λ°μ΄ν°λ² μ΄μ€λ μ€ν€λ§λ₯Ό μ μν νμ μμ΄ key-value ννλ‘ μμ λ‘κ² λ°μ΄ν°λ₯Ό μ μ₯ν μ μλ€.
ν μ΄λΈ μμ± λ²νΌμ λλ¬ ν μ΄λΈ μ΄λ¦κ³Ό νν°μ ν€λ₯Ό μ λ ₯ νλ€. νν°μ ν€λ κ²μμ νμν ν€κ°μΌλ‘ νμμ μΌλ‘ μ λ ₯ν΄μΌ νλ€.
- ν
μ΄λΈ μ΄λ¦ :
hello-member
- νν°μ
ν€ :
name
Lambda μμ±
Lambda λ₯Ό μ΄μ©ν΄ λ°±μλ μλΉμ€ μ½λλ₯Ό λ§λ€μ μλ€.
AWS μ λνμ μΈ μλ²λ¦¬μ€ μλΉμ€. μλ²μ λν μ€μ μ΄λ κ΄λ¦¬λ₯Ό κ³ λ―Όν νμ μμ΄ κ°λ¨νκ² μλ²λ₯Ό λ§λ€ μ μλ€. μλ₯Ό λ€μ΄ λ§μ μμ² λ°μ μ μλμΌλ‘ νμ₯λκ³ , κ΄λ¦¬λλ―λ‘ μλΉμ€μλ§ μ§μ€ν μ μλ€. κ°λ¨νκ³ λΉ λ₯΄κ² μλ²λ₯Ό λ§λ€κ³ μΆμ κ²½μ° μ¬μ©ν μ μλ€.
ν¨μ μμ± λ²νΌμ λλ¬ μλμ κ°μ΄ μ λ ₯νλ€.
- μ ν μ΅μ
:
μλ‘ μμ±
- ν¨μ μ΄λ¦ :
api-service-create
- λ°νμ :
Python 3.9
ν¨μμ λν κΆν μ€μ λΆλΆμ μλμ κ°μ΄ μ§ννλ€.
μ΄ ν¨μλ μΆν DynamoDB μ μ°κ²°ν μμ μ΄λ€. AWS μλΉμ€λΌλ¦¬ μλ‘ μ°κ²°νκ±°λ μ΄μ©νκ±°λ νΈμΆνκ±°λ ν λμλ κΆνμ΄ νμνλ€.
- μν μ΄λ¦ :
my-lambda-role
- μ μ±
ν
νλ¦Ώ :
λ¨μ λ§μ΄ν¬λ‘μλΉμ€ κΆν - DynamoDB
μ½λ λΆλΆμ μλμ κ°μ΄ μ
λ ₯ ν Deploy
λ²νΌμ λλ₯Έλ€.
μ½λ보기
μ΄ μ½λλ λ©€λ²μ μ΄λ¦κ³Ό κΈ°λΆ μνλ₯Ό λλ€μΌλ‘ 맀νμμΌμ£Όκ³ , μ΄λ₯Ό DynamoDBμ μ μ₯νλ κΈ°λ₯μ΄λ€.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 import json import boto3 import random import json def lambda_handler(event, context): member_name = ['Ama','Jone','Zon','Penny','Jessie'] member_status = ['Happy','Sad','Serious','Satisfied','Free'] dynamodb = boto3.resource('dynamodb',endpoint_url='http://dynamodb.ap-northeast-2.amazonaws.com') member_table = dynamodb.Table('hello-member') name = member_name[random.randint(0,4)] status = member_status[random.randint(0, 4)] member_table.put_item( Item={ 'name': name, 'status': status, } ) documents = {'name':name,'status':status} print(documents) return { 'statusCode': 200, 'headers': {'Access-Control-Allow-Origin': '*'}, 'body': json.dumps(documents) }
Test
λ²νΌμ λλ¬ μλμ κ°μ΄ ν
μ€νΈ μ΄λ²€νΈλ₯Ό κ΅¬μ± ν μ μ₯ λ²νΌμ λλ₯Έλ€.
- μ΄λ²€νΈ μ΄λ¦ :
my-api-test
- ν
νλ¦Ώ :
hello-world
ν μ€νΈ κ²°κ³Όκ° μλμ κ°μ νμμΌλ‘ λμ¨λ€λ©΄ μ λλ‘ μ€μ λ κ²μ΄λ€.
μ΄μ DynamoDB μ λ°μ΄ν°κ° μ λ€μ΄κ°λμ§ νμΈν΄λ³΄μ. DynamoDB λ‘ μ΄λν΄ hello-member
ν
μ΄λΈμ λλ₯Έλ€.
ν νλͺ© νμμ λλ₯Έλ€.
μλμ κ°μ΄ Lambda μ μ½λκ° μ€νλμ΄ DynamoDB μ κ°μ΄ μ λ€μ΄κ° μλ€.
μ¬κΈ°κΉμ§ Lambda μ DynamoDB λ₯Ό λ§λ€κ³ , μ λμλλ κ²μ νμΈνλ€. μ΄μ ν΄λΌμ΄μΈνΈμμ μ΄ Lambda μ ν¨μλ₯Ό νΈμΆν μ μλλ‘ μλν¬μΈνΈλ₯Ό μ 곡ν΄μΌ νλ€.
API Gateway ꡬμ±νκΈ°
μλΉμ€μμ API Gateway λ₯Ό κ²μ ν REST API μ νμ μ ννλ€.
μ API λ₯Ό μ ννκ³ API μ΄λ¦μ my-api
λ₯Ό μ
λ ₯νλ€.
my-api
μ ν ν λ©μλλ₯Ό μμ±νλ€.
μλμ κ°μ΄ λ©μλ μΈλΆ μ 보λ₯Ό μ€μ ν λ©μλλ₯Ό μμ±νλ€.
- λ©μλ μ ν :
GET
- ν΅ν© μ ν :
Lambda ν¨μ
- Lambda νλ‘μ ν΅ν© ν κΈ
ON
- Lambda ν¨μ μ νμμ κΈ°μ‘΄μ λ§λ€μ΄ λμλ
api-service-create
λ₯Ό μ ννλ€.
μμ±ν APIμ 리μμ€ λ©λ΄μμ λ°©κΈ μμ±ν GET λ©μλλ₯Ό λλ₯΄λ©΄ μ΄μ λ μ€λ₯Έμͺ½μ ν΄λΉ λ©μλμ κ΄λ ¨λ μ λ³΄κ° λ³΄μ΄κ² λλ€. ν μ€νΈ νμ μ ν ν ν μ€νΈλ₯Ό μ€λΉνλ€.
ν μ€νΈ λ²νΌμ λλ¬ API νΈμΆμ ν μ€νΈ ν΄λ³Έλ€. μλΉμ€κ° 볡μ‘ν΄μ§λ©΄ Request Body λ Header μ κ°μ μΆκ°ν μλ μλ€.
ν μ€νΈ μ±κ³΅ μ μν μ½λμ 200 κ³Ό ν¨κ» μλ΅ λ³Έλ¬Έμ Lambda ν¨μ μ€ν κ²°κ³Όκ° μ¬λ°λ₯΄κ² μΆλ ₯λλ€λ©΄ μ±κ³΅μ΄λ€.
CORS μ€μ
API gateway λ₯Ό λ§λ€μμ§λ§, λ°λ‘ νΈμΆνλ©΄ CORS κ΄λ ¨ μλ¬κ° λ°μνλ€. λΈλΌμ°μ μμ μ€ννλ μ€ν¬λ¦½νΈμ μμ²μ νμ©νλ €λ©΄ APIμ λν CORS(cross-origin resource sharing)λ₯Ό ꡬμ±ν΄μΌνλ€. CORSλ λ€λ₯Έ λλ©μΈμμ 리μμ€λ₯Ό μμ²ν λ λ°μνλ 보μ μ μ± μ΄λ€. API Gatewayμ λ©μλλ₯Ό λ§λ€μλ€ νλλΌλ, κΈ°λ³Έμ μΌλ‘λ CORSκ° νμ©λμ§ μμΌλ―λ‘ λΈλΌμ°μ μμ λ€λ₯Έ λλ©μΈ(μ: ν΄λΌμ΄μΈνΈ μλ²μ API μλ²μ λλ©μΈμ΄ λ€λ₯Ό κ²½μ°)μΌλ‘ μμ²μ λ³΄λΌ λ λ¬Έμ κ° λ°μν μ μλ€.
리μμ€ λ©λ΄μμ /
κ²½λ‘λ₯Ό μ ν ν CORS λ₯Ό νμ±ν νλ€.
CORS μ€μ νλ©΄μμ Access-Control-Allow-Methods μ GET λ©μλλ₯Ό μ ννλ€. GET μμ²μ λν΄ λ¦¬μμ€ μ κ·Όμ νμ©νλ€.
μ°Έκ³ λ‘ μ μ¬μ§μμ Access-Control-Allow-Methods νλͺ©μ OPTIONS λ©μλκ° κΈ°λ³Έκ°μΌλ‘ ν¬ν¨λμ΄ μλλ°, μ΄λ ν리νλΌμ΄νΈ μμ²(Preflight Request) μ΄λ€. ν΄λΌμ΄μΈνΈμμ μμ²νλ €λ URLμ΄ μΈλΆ λλ©μΈμΌ κ²½μ°, λΈλΌμ°μ λ μμ νμ§ μμ HTTP μμ²(μ: POST, PUT, DELETE)μ 보λ΄κΈ° μ μ OPTIONS λ©μλλ₯Ό μ¬μ©νμ¬ μλ²μ ν리νλΌμ΄νΈ μμ²μ 보λΈλ€. μλ²κ° μ΄ μμ²μ λν΄ μ μ ν μλ΅μ νμ§ μμΌλ©΄ λΈλΌμ°μ λ CORS μλ¬λ₯Ό λ°μμν¨λ€. μ€μ μμ²μ΄ μ ν¨νμ§ μλ²κ° 미리 νμ ν μ μλλ‘ νλ μλ¨μ΄λ€.
CORS μ€μ μ λ§μ³€μΌλ©΄ API λ°°ν¬ λ²νΌμ λλ¬ μ΄ api λ₯Ό μ€μ μ¬μ©ν μ μκ² μ€λΉνλ€.
- μ€ν μ΄μ§ : μ μ€ν μ΄μ§
- μ€ν
μ΄μ§ μ΄λ¦ :
dev
λ°°ν¬κ° μλ£λλ©΄ νΈμΆ URL μ΄ νμλλ€.
μ΄ URL μ λΈλΌμ°μ μ λΆμ¬λ£κ³ νΈμΆνλ©΄ μλμ κ°μ μλ΅μ μ»μ μ μλ€.
Lambda μ API gateway λ₯Ό μ°κ²°ν΄ ν΄λΌμ΄μΈνΈμμ μ κ·Ό κ°λ₯ν μλν¬μΈνΈλ₯Ό λ§λλ μμ μ΄ μλ£λμμ΅λλ€. μ΄μ μ΄ λ°μ΄ν°λ₯Ό S3 μ μ μ μΉμ¬μ΄νΈ νΈμ€ν κΈ°λ₯μ μ¬μ©ν΄ html νμ΄μ§μ νμνλ μμ μ μ§νν΄λ³΄κ² μ΅λλ€.
S3 μΉμλ² κΈ°λ₯ μ¬μ©νκΈ°
AWS μλΉμ€μμ S3 λ₯Ό κ²μ ν λ²ν·μ μμ±νλ€. λ²ν·μ μ΄λ¦μ κ³ μ ν΄μΌ νλ€.
μλμͺ½μΌλ‘ λ΄λ € νΌλΈλ¦ μμΈμ€ μ°¨λ¨ μ€μ μ ν΄μ νλ€. μ μ μΉ νΈμ€ν μ©λλ‘ μ¬μ©νκΈ° μν΄ λͺ¨λ μ κ·Όμ νμ©ν΄μΌ ν΄λΌμ΄μΈνΈ(λΈλΌμ°μ )μμ html νμΌμ λ΄λ €λ°μ μ μλ€.
λ²ν· μμ±μ λλ₯΄κ³ μμ±μ΄ μλ£λλ©΄ ν΄λΉ λ²ν·μ λλ¬ λ€μ΄ κ°λ€. κ·Έ ν μλμ κ°μ html μ½λλ₯Ό index.html νμΌλ‘ λ§λ€μ΄ λ²ν·μ μ λ‘λ νλ€.
μ½λ보기
μμμ API gateway λ‘ λ§λ url λ‘ ajax νΈμΆμ νκ³ , λ°μ΄ν°λ₯Ό κ°μ Έμ νλ©΄μ λΏλ¦¬λ μν μ νλ κ°λ¨ν html μ½λλ€.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<html>
<head>
<meta charset="utf-8" name="viewport"
content="width=device-width, height=device-height, minimum-scale=1.0, maximum-scale=1.0, initial-scale=1.0">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<title>Hello World!</title>
<style>
#title {
font-family: arial;
font-size: 2em;
color: #eb971a;
margin-top: 50px;
text-align: center;
}
button {
background-color: #eb971a;
border: none;
color: white;
border-radius: 5px;
width: 40%;
height: 35px;
font-size: 13pt;
margin-top: 30px;
text-align: center;
}
#sentence {
font-size: 17pt;
margin-top: 30px;
font-weight: bold;
color: #eb971a;
}
</style>
</head>
<body>
<p id="title">Hello World From <b>Lambda</b></p>
<hr id="lambda-line" width="800px" align="center" color="#eb971a;">
<center><button onclick="checkEvent();">Who are you?</button></center>
<center>
<div id="sentence"></div>
</center>
</body>
<script type="text/javascript">
function checkEvent() {
$.ajax({
type: "GET",
url: "URLμμ
λ ₯νμΈμ",
dataType: 'json',
success: function (data) {
document.getElementById('sentence').innerHTML = data.status + " " + data.name
},
error: function (error) {
alert('ERROR::');
console.log(error)
}
});
}
</script>
</html>
index.html νμΌμ μ λ‘λ νμΌλ©΄ μ μ μΉμ¬μ΄νΈ νΈμ€ν κΈ°λ₯μ νμ±ν νκ³ , κΆνμ λΆμ¬ν΄μΌ νλ€.
μμ± νμμ 맨 μλμͺ½μΌλ‘ μ€ν¬λ‘€νλ©΄ μ μ μΉ μ¬μ΄νΈ νΈμ€ν κΈ°λ₯μ΄ μλ€.
νΈμ§μ λλ¬ νμ±ν νλ€. μΉμ¬μ΄νΈ μ²μ μ§μ μ κΈ°λ³Έ νμ΄μ§κ° λ νμΌμ μ λ ₯νλ€.(index.html)
λ³κ²½ μ¬ν μ μ₯μ λλ₯΄λ©΄ μ μ μΉ μ¬μ΄νΈ νΈμ€ν μ΄ μμλλ€.
κ·Έλ¬λ ν΄λΉ URL ν΄λ¦ μ μμ§ κΆνμ΄ μμ΄ μ μμ΄ μλλ€. μΈλΆμμ νμΌμ μ½μ΄ λ€μΌμ μλλ‘ κΆνμ λΆμ¬ν΄μΌ νλ€. λ€μ κ·Έλ¦Όκ³Ό κ°μ΄ κΆν νμμ λ²ν· μ μ± μ JSON μΌλ‘ μμ±λ κΆν μ€μ μ ν΄μ€λ€.
JSON μ€μ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"Version": "2024-09-18",
"Statement": [
{
"Sid": "Stmt1709405011428",
"Action": [
"s3:GetObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::my-bucket-238423", // λ³ΈμΈλ²ν· λ²νΈ
"arn:aws:s3:::my-bucket-238423/*" // λ³ΈμΈλ²ν· λ²νΈ
],
"Principal": "*"
}
]
}
κΆν μ€μ μ΄ λ§λ¬΄λ¦¬λλ©΄ S3 νΈμΆ URL λ‘ μ΄λνλ€. index.html μ νλ©΄μ΄ μ λμ€κ³ λ²νΌμ λλ₯Όλλ§λ€ Lambda λ‘ ajax νΈμΆν΄ λ°μ΄ν°λ₯Ό κ°μ Έμ€λ κ²μ λ³Ό μ μλ€.
κ²°κ³Ό
μλ²λ¦¬μ€ μ¬μ© μ΄μ μ spring μ ν΅ν΄ μΉ μ΄ν리μΌμ΄μ μλ²λ₯Ό λ°°ν¬νκ³€ νλ€. REST API λ₯Ό μ€νλ§μμ μ 곡νλλ‘ λ§λ€κ³ , μ΄ λ°±μλ μλ²λ₯Ό AWS EC2 κ°μ ν΄λΌμ°λμ λ°°ν¬ν΄ κΈ°λμν€λ ννμλ€.
κ·Έλ¬λ μλ²λ¦¬μ€ μν€ν μ³λ₯Ό νμ©νκ² λλ©΄ μλ² κ΅¬μ±μ΄λ κ΄λ¦¬μ λν μ€μ /μ½λ λ±μ μμ±ν νμ μμ΄ κ·Έλ₯ μλΉμ€ μ½λλ₯Ό Lambda μ λ£μ΄λκ³ μ΄λ₯Ό νΈμΆνλλ‘ κ΅¬μ±νλ©΄ λλ€. λν API gateway λ₯Ό ν΅ν΄ ν΄λΌμ΄μΈνΈμκ² API λ₯Ό μ 곡νκ² ν¨μΌλ‘μ¨ Lambda μ μ½λμ μ°λμ΄ κ°λ₯νλ€λ κ²μ μκ² λμλ€.
μλ²λ¦¬μ€λ λ€μκ³Ό κ°μ νΉμ§μ κ°μ§λ€.
- νΉμ μμ μ νΈμΆν λμλ§ ν¨μκ° νΈμΆλλ μ리μ΄λ€ 보λ νΈμΆλ λ§νΌλ§ λΉμ©μ μ§λΆνλ€.
- μΈνλΌ κ΄λ¦¬μ μ κ²½ μΈ νμκ° μλ€.
- λ€νΈμν¬ μ²λ¦¬λμ λ°λΌ μλ²μ κ°―μλ₯Ό μλμΌλ‘ λλ €μ€λ€. (auto-scaling)
- ν ν¨μκ° νλ² νΈμΆλ λ μ νμ΄ μλ€.(1500MB, 300μ΄) μ¦, μΉμμΌ κ°μ΄ κ³μ μΌλμΌ νλ μμ μ νκΈ° νλ€λ€.
- λ‘컬 λ°μ΄ν°μ μ κ·Ό λΆκ°λ₯νλ€. μ΄λ Lambda κ° stateless νκΈ° λλ¬Έμ΄λ€. Lambda ν¨μλ μ€νλ λλ§λ€ μλ‘μ΄ μΈμ€ν΄μ€κ° μμ±λκ³ , λλ€ ν¨μ λ΄μμλ λ΄λΆμ μΌλ‘ λ³μ κ°μ λ³νλ±μ μ μ₯νκ±°λ μΆμ νμ§ μλλ€. λ°λΌμ νΈμΆ μ¬μ΄μ μνλ₯Ό μ μ₯νκ±°λ 곡μ ν νμκ° μλ€λ©΄ S3 λ DynamoDB, RDS λ±μ μ°λν΄μ μ¬μ©ν΄μΌ νλ€.
μ΄λ° νΉμ§λ€μ μ΄ν΄λ³΄λ©΄, λ¨μΌ μμ μ νλ κ°λ¨ν μλ² κ°μ κ²½μ°λ μλ²λ¦¬μ€λ₯Ό ν΅ν΄ μ½κ² λ§λ€ μ μλ€λ μ₯μ μ΄ μλ κ² κ°λ€. κ·Έλ¬λ κ·Έλ λ€κ³ μ€νλ§κ³Ό κ°μ νλ μμν¬κ° νμ μλ€λ μλ―Έλ μλλ€. 볡μ‘ν λΉμ¦λμ€ λ‘μ§μ κ°μ§ μν°νλΌμ΄μ¦ κΈμ ν΅ν© μμ€ν μμλ λͺ¨λ λ‘μ§μ ν¨μλ‘ λλμ΄ κ΄λ¦¬ν κ²½μ° κ΄λ¦¬κ° μ΄λ €μ μ§ μ μλ€. λν, μ΄λ―Έ μ€νλ§ κ°μ νλ μμν¬λ νλΆν μνκ³λ₯Ό κ°μ§κ³ μμ΄ λ€μν λΌμ΄λΈλ¬λ¦¬μ λͺ¨λμ μ¬μ©ν΄ κ°λ°ν μ μλ€λ μ₯μ μ΄ μλ€.
λκΈλ¨κΈ°κΈ°