AWS Lambda
는 PaaS
의 일종입니다! PaaS가 기억이 안난다면.. 여기를 확인하세욥! 우리가 따로 운영체제와 런타임 등을 관리할 필요가 없습니다. 런타임을 관리하지 않아도 된다는 것은 정말 큰 이점입니다! 중간에 서버가 다운될 수도 있고, 오류가 나서 의도치 않은 현상들이 발생 할 수도 있는데 그런 것에 대한 걱정을 할 필요 없이 함수만 뙇 올리면 되기 때문이죠.
AWS Lamdba
를 설명하기 전에, 다음과 같은 상황을 가정해보겠습니다. 우리가 Flask
를 이용해 특정 API 함수
를 만들어서 EC2
에 올렸다고 합시다. EC2
는 계속 켜져있기 때문에 많이 오든 적게 오든 요금은 계속 똑같이 나갈 것입니다. '난 쓴 만큼만 내고 싶은데 이렇게 계속 돈이 나가야하나..?' 싶을 수 있습니다. 그래서 API
만든 것을 Lambda
에 연결을 시키고, 오고 가는 요청의 갯수와 자원에 대해서만 요금을 내게 하는 것입니다.
즉, 예전에 EC2
에선 서버를 만들어서 서버 단위로 관리했었는데, Lambda
에서는 우리가 만든 함수를 단위로 이용한 만큼 요금을 지불하는 형식입니다.
사용자가 Python
, Node.js
등의 언어로 만들어진 Lambda Handler Function
을 구현하여 이를 AWS Lambda
에 등록하고, 이를 AWS API Gateway
에 연결하면, 외부에서 해당 API를 이용할 수 있습니다(AWS API Gateway
에서는 트래픽 관리를 하거나, 로그를 찍거나 하는 등등의 여러 기능이 있습니다.).
AWS Lambda 콘솔에 접속합니다. 우측의 함수 생성을 눌러서 함수를 생성합니다.
함수를 생성하는 창으로 이동하면 Lambda Handler 함수 이름과 런타임을 설정해줍니다. 런타임은 어떤 언어를 쓸지를 뜻합니다.
나머지는 설정은 딱히 건들지 않고 함수 생성을 누르도록 하겠습니다. 그럼 아래와 같이 람다 함수가 생성된 것을 확인할 수 있습니다.
아래로 내려가면 코드 소스
가 보입니다. 코드를 확인할 수 있습니다.
더 밑으로 내려가면 런타임 설정
에서 핸들러를 설정할 수 있는 곳이 나옵니다. 아래 사진에서 lambda_function.lambda_handler
의 의미는, lambda_function.py
안에 있는 lambda_handler
를 핸들러 함수로 이용하겠다라고 지정한 것입니다.
위 lambda_function.py
안에 있었던 lambda_hanlder
함수를 톺아보도록 하겠습니다!
def lambda_handler(event, context):
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
Parameter
event
요청이 들어왔을 때 이벤트에 관련된 정보들을 담고 있습니다. json
형식으로 온 body에 접근할 수 있습니다. 대체로 Python
의 dict
유형으로 옵니다. 하지만 list
, str
, int
, float
또는 NoneType
이 될 수도 있습니다.
하지만 Lambda Proxy
를 사용하면, Event 객체로 들어오는 데이터가 정말 많아집니다. Test 옆의 토글을 열어 Configure test event
를 클릭하고, 테스트 이벤트 구성 - 이벤트 템플릿 - Amazon API Gateway Proxy
를 클릭하면 정보를 알 수 있습니다.
{
"body": "eyJ0ZXN0IjoiYm9keSJ9",
"resource": "/{proxy+}",
"path": "/path/to/resource",
"httpMethod": "POST",
"isBase64Encoded": true,
"queryStringParameters": {
"foo": "bar"
},
"multiValueQueryStringParameters": {
"foo": [
"bar"
]
},
"pathParameters": {
"proxy": "/path/to/resource"
},
"stageVariables": {
"baz": "qux"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "max-age=0",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Host": "1234567890.execute-api.us-east-1.amazonaws.com",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Custom User Agent String",
"Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"Accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
],
"Accept-Encoding": [
"gzip, deflate, sdch"
],
"Accept-Language": [
"en-US,en;q=0.8"
],
"Cache-Control": [
"max-age=0"
],
"CloudFront-Forwarded-Proto": [
"https"
],
"CloudFront-Is-Desktop-Viewer": [
"true"
],
"CloudFront-Is-Mobile-Viewer": [
"false"
],
"CloudFront-Is-SmartTV-Viewer": [
"false"
],
"CloudFront-Is-Tablet-Viewer": [
"false"
],
"CloudFront-Viewer-Country": [
"US"
],
"Host": [
"0123456789.execute-api.us-east-1.amazonaws.com"
],
"Upgrade-Insecure-Requests": [
"1"
],
"User-Agent": [
"Custom User Agent String"
],
"Via": [
"1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
],
"X-Amz-Cf-Id": [
"cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
],
"X-Forwarded-For": [
"127.0.0.1, 127.0.0.2"
],
"X-Forwarded-Port": [
"443"
],
"X-Forwarded-Proto": [
"https"
]
},
위와 같은 정보를 알 수 있다고 하네요!
context
: 컨텍스트 정보를 담고 있습니다. 호출 및 함수, 런타임 환경에 관한 정보를 제공하는 메서드와 속성들을 제공합니다.Test
옆의 토글을 열어 Configure test event
를 클릭하고, 테스트 이벤트 구성 - 이벤트 템플릿 - Amazon API Gateway Proxy
를 클릭해서 계산기를 만들기 위해 테스트 이벤트 구성을 해봅시다!
다음과 같이 만들어주면 되는데요.
아래 코드를 그대로 복사 붙여넣기 하고 생성을 눌러주면 됩니다.
{
"body": "null",
"resource": "/{proxy+}",
"path": "/path/to/resource",
"httpMethod": "GET",
"isBase64Encoded": true,
"queryStringParameters": {
"a": "1",
"b": "2",
"operator": "+"
},
"multiValueQueryStringParameters": {
"a": [
"1"
],
"b": [
"2"
],
"operator": [
"+"
]
},
"pathParameters": {
"proxy": "/path/to/resource"
},
"stageVariables": {
"baz": "qux"
},
"headers": {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Encoding": "gzip, deflate, sdch",
"Accept-Language": "en-US,en;q=0.8",
"Cache-Control": "max-age=0",
"CloudFront-Forwarded-Proto": "https",
"CloudFront-Is-Desktop-Viewer": "true",
"CloudFront-Is-Mobile-Viewer": "false",
"CloudFront-Is-SmartTV-Viewer": "false",
"CloudFront-Is-Tablet-Viewer": "false",
"CloudFront-Viewer-Country": "US",
"Host": "1234567890.execute-api.us-east-1.amazonaws.com",
"Upgrade-Insecure-Requests": "1",
"User-Agent": "Custom User Agent String",
"Via": "1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)",
"X-Amz-Cf-Id": "cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA==",
"X-Forwarded-For": "127.0.0.1, 127.0.0.2",
"X-Forwarded-Port": "443",
"X-Forwarded-Proto": "https"
},
"multiValueHeaders": {
"Accept": [
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
],
"Accept-Encoding": [
"gzip, deflate, sdch"
],
"Accept-Language": [
"en-US,en;q=0.8"
],
"Cache-Control": [
"max-age=0"
],
"CloudFront-Forwarded-Proto": [
"https"
],
"CloudFront-Is-Desktop-Viewer": [
"true"
],
"CloudFront-Is-Mobile-Viewer": [
"false"
],
"CloudFront-Is-SmartTV-Viewer": [
"false"
],
"CloudFront-Is-Tablet-Viewer": [
"false"
],
"CloudFront-Viewer-Country": [
"US"
],
"Host": [
"0123456789.execute-api.us-east-1.amazonaws.com"
],
"Upgrade-Insecure-Requests": [
"1"
],
"User-Agent": [
"Custom User Agent String"
],
"Via": [
"1.1 08f323deadbeefa7af34d5feb414ce27.cloudfront.net (CloudFront)"
],
"X-Amz-Cf-Id": [
"cDehVQoZnx43VYQb9j2-nvCh-9z396Uhbp027Y2JvkCPNLmGJHqlaA=="
],
"X-Forwarded-For": [
"127.0.0.1, 127.0.0.2"
],
"X-Forwarded-Port": [
"443"
],
"X-Forwarded-Proto": [
"https"
]
},
"requestContext": {
"accountId": "123456789012",
"resourceId": "123456",
"stage": "prod",
"requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef",
"requestTime": "09/Apr/2015:12:34:56 +0000",
"requestTimeEpoch": 1428582896000,
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"accessKey": null,
"sourceIp": "127.0.0.1",
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "Custom User Agent String",
"user": null
},
"path": "/prod/path/to/resource",
"resourcePath": "/{proxy+}",
"httpMethod": "POST",
"apiId": "1234567890",
"protocol": "HTTP/1.1"
}
}
생성 후 Test
옆의 토글을 열어 방금 만든 add
를 확인할 수 있습니다.
이 add
는 lambda_handler
의 event
로 들어갑니다. 이 event
에 들어가는 걸 뽑아내서 코드로 작성하면 되겠지요!
import json
def lambda_handler(event, context):
# Key Check
param = event['queryStringParameters']
print(param)
if any([e not in param for e in ('operator', 'a', 'b')]):
return {
'statusCode': 400,
'body': json.dumps({"message": "key error"})
}
operator = param['operator']
try:
a = int(param['a'])
b = int(param['b'])
except Exception:
return {
'statusCode': 400,
'body': json.dumps({
"message": "value error"
})
}
if operator == '+' or operator == ' ': # query string에서 +는 띄어쓰기로 변환됨
return {
'statusCode': 200,
'body': json.dumps({
"result": a + b
})
}
elif operator == '-':
return {
'statusCode': 200,
'body': json.dumps({
"result": a - b
})
}
elif operator == '/':
return {
'statusCode': 200,
'body': json.dumps({
"result": a / b
})
}
elif operator == '*':
return {
'statusCode': 200,
'body': json.dumps({
"result": a * b
})
}
else:
return {
'statusCode': 400,
'body': json.dumps({"message": "operator error"})
}
위 코드를 lambda_function.py
에 복붙해서 넣고, Ctrl+S
를 눌러 저장하고, Deploy
를 누르고, Test
를 누르면 실행할 수 있습니다!
위 같이 Test
결과를 확인할 수 있습니다.
모니터링 > 로그
로 가면 로그 또한 확인할 수 있습니다. LogStream
에 적혀있는 링크를 누르면 각 로그를 타임스탬프 별로 볼 수 있습니다.
API Gateway 콘솔로 이동해서 REST API
카드를 찾아 구축을 누릅니다.
프로토콜
은 REST
, 새 API 생성
은 새 API
를 선택합니다. 그 외 설정은 원하는 대로 하면 됩니다. 그 후 API 생성을 누릅니다.
메인 콘솔로 넘어 오면 리소스 > 작업 > 리소스 생성
으로 하위 리소스를 설정해주러 가봅시다!
다음과 같이 입력해 주면 됩니다. API Gateway COR3 활성화
는 체크해주는 게 좋습니다. 우리가 서버를 만들 때 대부분 외부에서 사용할 수 있게 할 것이기 때문입니다. 리소스 생성을 눌러줍니다.
이제 메소드를 만들어 줍시다. 작업 > 메서드 생성
을 눌러서 만들고 ANY
로 한 다음 체크를 누르면 됩니다.
설정은 아래 나와있는대로 하면 됩니다. Lambda 함수
는 아까 했던 걸로 해야겠죠? 이렇게 하면 any
에 Lambda 함수
가 연결이 됩니다.
메서드가 연결이 된 것을 확인할 수 있습니다.
이제 실제로 배포를 해보겠습니다. 작업 > API 배포
를 클릭해 이동합니다.
다음과 같이 설정하고 배포를 누릅니다.
그렇게 되면 우리가 만든 API에 대해 URL 호출
, 즉 end point가 나오는데요. 이를 통해 접속할 수 있습니다.
해당 URL로 접속하면 {"message":"Missing Authentication Token"}
가 나오는데요!
저는 calc
이라는 하위메소드를 만들었으므로 그 링크로 들어가면, {"message": "Internal server error"}
라며 에러가 뜨지만, 내부 서버에 접속한 것을 알 수 있습니다.
현재 링크에 ?a=1&b=3&operator=/
를 추가하여 쿼리문을 작성해보면(postman으로도 가능합니다!) 이렇게 result값이 잘 나오는 것을 확인할 수 있습니다.😊