개인 공부 목적으로 작성된 포스팅입니다. 왜곡된 내용이 포함되어 있습니다
HTTP API를 배포해 달라는 요청을 받았다. AWS 공부를 하고 있었던 터라 사용해보지 않는 AWS 서버리스 서비스를 통해 HTTP API 배포하였다. 사용한 AWS 서비스는 API Gateway, Lambda, DynamoDB로 모두 서버리스를 제공하는 서비스를 사용하였다. 각 서비스의 주요 특징을 살펴보고, 실습해보자
API Gateway
API Gateway는 규모와 관계없이 REST및 Websocket API를 생성, 게시 유지, 모니터링 및 보호하기 위한 AWS 서비스이다. API 개발자는 AWS 또는 다른 웹 서비스를 비롯해 AWS 클라우드에 저장된 데이터에 엑세스하는 API를 생성할 수 있다. API Gateway API 개발자는 자체 클라이언트 애플리케이션에서 사용할 API를 생성할 수 있고, 타사 앱 개발자가 API를 사용하도록 제공할 수도 있다.
API Gateway의 기능은 다음과 같다.
- Websocket, HTTP 및 REST API 지원
- 인증 매커니즘(AWS Identity and Access Management 정책, Lambda 권한 부여 함수, Amazon Cognito)
- 변경 사항을 안전하게 롤아웃하기 위한 Canary 릴리스 배포
- API 사용 및 API 변경에 대한 CloudTrail 로깅 및 모니터링
- 경보 설정 기능을 포함 CloudWatch 엑세스 로깅 및 실행 로깅
- AWS CloudFormation 템플릿을 사용하여 API 생성을 활성화할 수 있는 기능
- 사용자 지정 도메인 이름 지원
- 일반적인 웹 익스폴로잇으로부터 API를 보호하기 위해 AWS WAF 통합
- 성능 징연 시간 파악 및 학습을 위해 AWS X-Ray와 통합
이때 API 스테이지는 API의 수명 주기 상태에 대한 논리적 참조(예: dev, prod, beta, v2)이다. API 스테이지는 API ID 및 스테이지 이름으로 식별되며, API를 호출하는 데 사용하는 URL에 포함된다. 버전을 명세할 수 있고, 추후에 엔드포인트에 반영된다.
AWS Lambda
Lambd는 고가용성 컴퓨팅 인프라에서 코드를 실행하고 서비와 운영체제 유지 관리, 용량 프로비저닝 및 자동 조정, 코드 및 보안 패치 배포, 로깅 등 모든 커퓨팅 리소스 관리를 수행한다. Lambda를 사용하면 Lambda가 지원하는 언어 런타입 중 하나로 코드를 제공하기만 하면 된다.
Lambda 함수에 코드를 구성한다. Lambda 서비스는 필요할 때만 함수를 실행하고 자동으로 확장된다. 사용한 컴퓨팅 시간 만큼만 비용을 지불하고, 코드가 실행되지 않을 때는 요금이 부과되지 않는다.
Lambd는 빠르게 스케일 업해야 하고 수요가 없을 때는 0으로 스케일 다운해야 하는 애플리케이션 시나리오에 이상적인 컴퓨팅 서비스이다. 다음과 같은 상황에서 사용할 수 있다.
- 파일 처리: 업로드 후 Amazon Simple Storage Service(S3)를 사용하여 Lambda 데이터 처리를 실시간을 트리거한다.
- 스트림 처리: Lambda 및 Amazon Kinesis를 사용하여 애플리케이션 작업 추적, 거래 주문 처리, 클릭스트림 분석, 데이터 정리, 로그 필터링, 인덱싱, 소셜 미디어 분석, 사물 인터넷(IoT) 디바이스 데이터 텔레메트리 및 계측을 위한 실시간 스트리밍 데이터를 처리한다.
- 웹 애플리케이션: Lambd를 다른 AWS 서비스와 결합하여 여러 데이터 센터에서 고가용성 구성으로 자동으로 스케일 업/스케일 다운되고 실행되는 상력한 웹 애플리케이션을 빌드한다.
- IoT 백엔드: Lambda를 사용하여 서버리스 백엔드를 구축함으로써 웹, 모바일, IoT 및 서드 파티 API 요청을 처리한다.
- 모바일 백엔드: Lambda 및 Amazon API Gateway를 사용하여 백엔드를 구축함으로싸 API요청을 인증하고 처리한다. AWS Amplify를 사용하여 iOS, Android, 웹 및 React Native 프론트엔드와 손쉽게 통합한다.
Lambda를 사용하면 사용자는 자신의 코드에 대해서만 책임을 갖는다. Lambda는 메모리, CPU, 네트워크 및 기타 리소스의 균형을 제공하는 컴퓨팅 플릿을 관리하여 코드를 실행한다. Lambda가 이러한 리소스를 관리하므로 컴퓨팅 인스턴스에 로그인하거나 제공된 런타임에 운영체제를 사용자 지정할 수 없다. Lambda는 사용자를 대신하여 용량 관리, 모니터링 및 Lambda 함수 로링을 비롯한 운영 및 관리 활동을 수행한다.
Lambda에서 제공하는 주요 기능은 다음과 같다.
- 환경 변수: 환경 변수를 사용하여 코드를 업데이트하지 않고 함수의 동작을 조정한다.
- 버젼: 예를 틀어 안정적인 프로덕션 버전의 사용자에게 영향을 주지 않고 베타 테스트에 새 함수를 사용할 수 있도록 버전으로 함수 배포를 관리한다.
- 컨테이너 이미지: 기존 컨테이너 도구를 재사용하거나 기계 학습과 같은 상당한 종속 구성 요소에 의존하는 더 큰 워크로드를 배포할 수 있도록 AWS에서 제공하는 기본 이미지 또는 대체 기본 이미지를 사용하여 Lambda 함수에 대한 컨테이너 이미지를 생성한다.
- Lambda 계층: 라이브러리와 기타 종속 구성 용소를 패키징하여 배포 아카이브의 크기를 줄이고 코드를 더 빠르게 배포할 수 있도록 하낟.
- Lambda 확장: 모니터링, 관측성, 보안 및 거버넌스를 위한 도구로 Lambda 함수를 보강하낟,
- 함수 URL: Lambda 함수에 전용 HTTP(S) 엔드포인트를 추가한다.
- 응답 스트리밍: Node.js 함수에서 클라이언트로 응답 페이로드를 다시 스트리밍하여 첫 번째 바이트까지 시간(TTFB) 성능을 개선하거나 더 큰. 페이로드를 반환하도록 Lambda 함수 URL을 구성합니다.
- 동시성 및 크기 조정 컨트롤: 프로덕션 애플리케이션의 크기 조정 및 응답성에 대해 세밀한 제어를 적용한다.
- 코드 서명: 승인된 개발자만 변경됮 않은 신뢰할 수 있는 코드를 Lambda 함수에 게시하는지 확인한다.
- 프라이빗 네트워킹: 데이터베이스, 캐시 인스턴스, 내부 서비스 등의 리소스에 대해 프라이빗 네트워크를 생성한다.
- 파일 시스템: 함수 코드가 높은 동시성으로 안전하고 공유 리소스에 액세스하고 수정할 수 있게 Amazon Elastic File System(Amazon EFS)을 로컬 디렉터리에 탑재하도록 함수를 구성한다.
- Lambda SnapStart: Lambda SnapStart는 일반적으로 함수 코드를 변경하지 않고도 1초 미만의 시작 성능을 제공할 수 있다.
AWS DynamoDB
Amazon DynamoDB는 모든 규모에서 10 밀리초 미만의 성능을 제공하는 서버리스, NoSQL, 완전관리형 데이터베이스이다.
DynamoDB는 관계형 데이터베이스의 규모 조정 및 운영 복잡성을 극복하기 위한 요구 사항을 해결한다. DynamoDB는 규모와 관계없이 일관된 성능이 필요한 운영 워크로드에 맞게 특별히 구축되고 최적화되었다. 예를 들어, DynamoDB는 사용자가 10명이든 1억 명이든 상관없이 장바구니 사용 사례에 대해 일관된 한 자릿수 밀리초 성능을 제공한다.
DynamoDB 특징은 다음과 같다.
- Serverless
- NoSQL
- 완전관리형 데이터베이스
- 규모에 상관없이 한 자릿수 밀리초의 성능
테이블, 항목, 속성
DynamoDB는 테이블, 항목, 속성으로 구성된다. 테이블은 함목의 컬렉션이고, 각 항목은 속성의 컬렉션이다. DynamoDB는 프라이머리 키를 사용하여 테이블의 각 함목을 고유하게 식별한다.
기본키
테이블을 생성할 때는 테이블 이름 외에도 테이블의 기본 키를 지정해야 한다. 기본 키는 테이블의 각 항목을 나타내는 고유 식별자이다. 두항목이 동일한 키를 가질 수는 없다. DynamoDB는 두가지 기본 키를 지원한다.
- 파티션 키: DynamoDB는 내부 해시 함수에 대한 입력으로 파티션 키값을 사용한다. 해시 함수 출력에 따라 항목을 지정할 파티션(DynamoDB 내부의 물리적 스토리지)이 결정된다. 파티션 키로만 구성되어 있는 테이블에서는 어떤 두 개의 테이블 항목도 동일한 파티션 키 값을 가질 수 없다.
- 정렬 키: 파티션 키+ 정렬 키 조합으로 복합키를 구성 할 수 있다. DynamoDB는 내부 해시 함수에 대한 입력으로 파티션 키 값을 사용한다. 해시 함수 출력에 따라 함목을 저장할 파티션이 결정되고, 파티션 값이 동일한 모든 항목은 정렬 키 값을 기준을 정렬되어 함께 저장된다. 따라서 파티션 키와 정렬키로 구성되어 있는 테이블에서 여러 항목이 동일한 파티션 키 값을 가질 수 있다.
실습
위 그림은 최종적으로 배포할 아키텍처이다. 별도의 지불 없이 AWS free tier에서 제공하는 리소스로만 아키텍처를 배포한다.
AWS DynamoDB 실습
AWS Console을 통해 DynamoDB를 생성한다.
테이블 이름을 설정하고 파티션값은 숫자형으로 id를 설정하였다. 정렬 키는 따로 설정하지 않았다.
DynamoDB는 free tier 계정에게 25GB의 스토리지와 25개의 프로비저닝된 쓰기 및 25개의 프로비저닝된 읽기 용량 유닛(WCU, RCU)을 제공한다. 해당 스펙을 설정하기 위해서 테이블 설정 - 설정 사용자 지정 및 프로비저닝 모드를 설정해야한다. 다른 설정은 기본 값으로 두고 DynamoDB를 생성한다.
생성된 DynamoDB Console에서 Insert Item 을 통해 데이터를 삽입할 수 있다. 테스트 데이터를 추가하자
DynamoDB 항목 탐색 탭에서 해당 테이블에 대한 검색을 수행할 수 있다.
AWS Lambda 실습
lambda를 생성하여 DynamoDB와 연결하여 데이터 생성, 조회, 삭제 함수를 구현해보자
Lambda 함수에 사용할 함수 이름을 기입하고 런타임을 선택한다. 글쓴이 경우 파이썬을 선택하였다.
이때 기본 Lambda 권한을 가진 새 역활 생성 대신 AWS 정책 템플릿에서 새 역활 생성을 통해 새로운 역활을 생성한 후 정책 템플릿 중에 단순 마이크로서비스 권한을 추가한다. 해당 권한 DynamoDB으로의 접근 및 연산 제어 권한이다. 추가적으로 Lambda함수에 대한 CloudWatch 권한이 자동 추가되어 해당 Lambda 함수에 대한 로깅을 제공해준다.
생성한 Lambda 함수 Console로 이동하면 해당 페이지에서 AWS가 자체적으로 지원하는 code editor를 확인할 수 있다. 코드 수정후 Deploy를 통해 즉시 함수를 배포할 수 있다. 다음은 작성한 코드이다.
import json
import boto3
from decimal import Decimal
client = boto3.client('dynamodb')
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table('KUIT5-dynamoDB')
tableName = 'http-crud-tutorial-items'
class DecimalEncoder(json.JSONEncoder):
def default(self, o):
if isinstance(o, Decimal):
return int(o)
return super(DecimalEncoder, self).default(o)
def lambda_handler(event, context):
print(event)
body = {}
statusCode = 200
headers = {
"Content-Type": "application/json"
}
try:
if event['routeKey'] == "GET /animals":
body = table.scan()
body = body["Items"]
responseBody = []
sorted_items = sorted(body, key=lambda x: int(x['id']))
for item in sorted_items:
animal={'id': int(item['id']),'name': item['name'], 'url': item['url'], 'state': item['state'],
'breed':item['breed'], 'address':item['address']
}
#print(animal)
responseBody.append(animal)
#body = responseBody
body = {"data": responseBody}
elif event['routeKey'] == "GET /animals/{id}":
print("parameter",event['pathParameters']['id'])
body = table.get_item(
Key={'id': int(event['pathParameters']['id'])})
item = body["Item"]
animal={'id': int(item['id']),'name': item['name'], 'url': item['url'], 'state': item['state'],
'breed':item['breed'], 'address':item['address']
}
#body = animal
body = {"data": animal}
elif event['routeKey'] == "POST /animals":
#request_body = event['body']
request_body = json.loads(event['body'])
print("Received data:", request_body)
# 검증
required_fields = {'url', 'name', 'state', 'breed', 'address'}
if not required_fields.issubset(request_body.keys()):
raise ValueError(f"Missing required fields: {required_fields - set(request_body.keys())}")
allowed_states = {"PROTECT", "MISSING", "WITNESS"}
if request_body['state'] not in allowed_states:
raise ValueError(f"Invalid state value. Allowed values: {allowed_states}")
response = table.scan(ProjectionExpression="id")
items = response.get("Items", [])
if items:
max_id = max(int(item["id"]) for item in items) # id의 최댓값 찾기
else:
max_id = 0 # 데이터가 없으면 0부터 시작
new_id = max_id + 1
# DynamoDB에 넣을 데이터 구성
item = {
"id": new_id,
"url": request_body['url'],
"name": request_body['name'],
"state": request_body['state'],
"breed": request_body['breed'],
"address": request_body['address']
}
# 데이터 저장
table.put_item(Item=item)
body = {"data": item}
elif event['routeKey'] == "DELETE /animals/{id}":
animal_id = int(event['pathParameters']['id'])
if 1 <= animal_id <= 4:
raise ValueError(f"Animal with ID {animal_id} cannot be deleted.")
print(f"Deleting animal with ID: {animal_id}")
# 먼저 해당 ID가 존재하는지 확인
existing_item = table.get_item(Key={'id': animal_id})
if "Item" not in existing_item:
raise ValueError(f"Animal with ID {animal_id} not found.")
# 데이터 삭제
table.delete_item(Key={'id': animal_id})
body = {"message": f"Animal with ID {animal_id} deleted successfully", "data": {"id": animal_id}}
except KeyError:
statusCode = 400
body = {"error": f"Unsupported route: {event.get('routeKey', 'Unknown')}"}
except ValueError as ve:
statusCode = 400
body = {"error": str(ve)}
except Exception as e:
statusCode = 500
body = {"error": "Internal server error", "details": str(e)}
body = json.dumps(body, ensure_ascii=False)
res = {
"statusCode": statusCode,
"headers": {
"Content-Type": "application/json"
},
"body": body
}
return res
Python으로 작성한 HTTP Method를 제공하는 코드이다. 데이터 수정(PATCH)를 제외한 메서드를 제공한다.
생성한 함수를 테스트를 통해 테스트를 진행 할 수 있다. 이때 이벤트 JSON을 구성하여 Input Data를 지정 할 수 있다.
해당 데이터는 Lambda에서 event변수를 통해 확인할 수 있다.
if event['routeKey'] == "GET /animals":
body = table.scan()
body = body["Items"]
# 중략
elif event['routeKey'] == "GET /animals/{id}":
print("parameter",event['pathParameters']['id'])
body = table.get_item(
Key={'id': int(event['pathParameters']['id'])})
# 중략
elif event['routeKey'] == "POST /animals":
request_body = json.loads(event['body'])
print("Received data:", request_body)
# 중략
elif event['routeKey'] == "DELETE /animals/{id}":
animal_id = int(event['pathParameters']['id'])
# 중략
CloudWatch를 통해 Lambda 이벤트 마다 기록된 로그 스트림을 통해 디버깅을 진행하면 된다.
AWS Lambda의 경우, free tier에 대해 월 100만건의 무료 요청 및, 40만 GB-초의 컴퓨팅 시간이 포함되어 있으며 x86 및 Graviton2 프로세서로 구동되는 기능에 모두 사용할 수 있고, 요청당 첫 6MB를 초과하는 월별 100GiB의 HTTP 응답 스트리밍이 무료로 포함됩니다.
다만 Lambda를 통해 다른 AWS 서비스를 사용하는 경우 추가 비용을 부과하는 경우도 있다고 하니, 유의해야한다.
AWS API Gateway
마지막으로 API Gateway를 생성해보자
HTTP API를 연동하기 위해서 HTTP API, REST API 둘중 하나를 선택하면 된다(글쓴이의 경우 HTTP API를 선택)
사용할 API Gateway의 이름을 기입하고, Stage는 기본값($default)으로 설정한다.
경로 생성을 통해 사용한 API 엔드포인트를 추가한다
생성된 경로에 대해서 통합 연결을 통해 Lambda와 연결한다.
Lambda Console에서도 API Gateway와 연결된 것을 확인할 수 있다.
Postman에서 API를 정상적으로 호출할 수 있음을 확인할 수 있다. 추가적으로 API Gateway에서 CORS 구성을 통해 정책을 수정할 수 있다.
참조 자료
https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/welcome.html
Amazon API Gateway란 무엇입니까? - Amazon API Gateway
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
https://docs.aws.amazon.com/serverless/latest/devguide/welcome.html
What is serverless development? - Serverless
What is serverless development? The following topics will guide you through developing a better conceptual understanding of serverless application development, and how various AWS services fit into together to create application patterns that form the core
docs.aws.amazon.com
https://docs.aws.amazon.com/ko_kr/lambda/latest/dg/welcome.html
AWS Lambda란 무엇인가요? - AWS Lambda
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
https://docs.aws.amazon.com/ko_kr/amazondynamodb/latest/developerguide/Introduction.html
What is Amazon DynamoDB? - Amazon DynamoDB
이 페이지에 작업이 필요하다는 점을 알려 주셔서 감사합니다. 실망시켜 드려 죄송합니다. 잠깐 시간을 내어 설명서를 향상시킬 수 있는 방법에 대해 말씀해 주십시오.
docs.aws.amazon.com
Amazon DynamoDB의 핵심 구성 요소 - Amazon DynamoDB
Amazon DynamoDB의 핵심 구성 요소 DynamoDB에서 테이블, 항목 및 속성이 작업하는 핵심 구성 요소입니다. 테이블은 항목의 컬렉션이고 각 항목은 속성의 컬렉션입니다. DynamoDB는 프라이머리 키를 사용
docs.aws.amazon.com
https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/http-api-dynamo-db.html
자습서: Lambda 및 DynamoDB를 사용한 CRUD HTTP API 생성 - Amazon API Gateway
자습서: Lambda 및 DynamoDB를 사용한 CRUD HTTP API 생성 이 자습서에서는 DynamoDB 테이블에서 항목을 생성, 읽기, 업데이트 및 삭제하는 서버리스 API를 생성합니다. DynamoDB는 완전관리형 NoSQL 데이터베이
docs.aws.amazon.com
https://docs.aws.amazon.com/ko_kr/apigateway/latest/developerguide/http-api-stages.html
API Gateway의 HTTP API에 대한 스테이지 - Amazon API Gateway
API Gateway의 HTTP API에 대한 스테이지 API 스테이지는 API의 수명 주기 상태에 대한 논리적 참조(예: dev, prod, beta, v2)입니다. API 스테이지는 API ID 및 스테이지 이름으로 식별되며, API를 호출하는 데 사
docs.aws.amazon.com
'인프라 > AWS' 카테고리의 다른 글
[AWS] AWS 프리티어 마이그레이션-2 (S3 마이그레이션) (1) | 2025.05.16 |
---|---|
[AWS] AWS 프리티어 마이그레이션-1 (예약 인스턴스, RDS, Elasticache) (0) | 2025.05.14 |
[AWS] AWS SAA-C03 합격 후기 (1) | 2025.04.22 |