간단하게(?) WebSocket API, Lambda, DynamoDB를 사용해 서버리스 채팅 앱을 구축해보자..
해당 링크에서 cloudformation 템플릿으로 작업 후에 하나씩 살펴보자..
- CloudFormation 생성
- WebSocket API 생성
라우팅 선택 표현식 값으로 표현된 속성은 클라이언트가 API 호출할 때마다 전송되는 모든 메시지에 포함되어야 한다.
$request.body.action 의 경우, 클라이언트 요청의 body 내에 "action" 키에 mapping 된 값으로 route 한다는 의미!
다음으로 넘어가 $connect / $disconnect / $default 다 추가하기.
(요청과 일치하는 다른 경로가 없을 때 $default 경로를 호출)
통합 연결에서 Lambda 선택 후 각 각 맞는 함수를 넣어준다.
- Test the chat API
wscat 유틸을 사용해 테스트 해보자
터미널 환경을 위해 cloud9을 열어주자... 2개를 만들 것!(메시지를 던지는 서버 1 / 메시지를 받는 서버 2)
wscat을 사용하기 위해선 npm이 필요!
npm -v
npm install -g wscat
publish 된 API endpoint에 연결
wscat -c wss://{YOUR-API-ID}.execute-api.{YOUR-REGION}.amazonaws.com/{STAGE}
api gateway의 대시보드를 보면 websocket url 알 수 있음..
위의 명령어 입력 시 connected 가 뜨면 연결 된 것..
DynamoDB에 connect Id 가 들어간 것을 볼 수 있음
sendMessage 함수 테스트를 위해 아래와 같은 JSON 메시지 전달 (cloud9 -1)
던진 메시지를 보기 위한 것..(cloud9 -2)
두 개의 터미널(cloud9 -1,2)에서 연결을 종료 시킨 뒤 다시 dynamoDB를 확인해보면...
이제 하나씩 다시 살펴보자!
일단 Lambda 함수들 먼저 보면
Connect / Disconnect / Default / sendMessage 이렇게 4개의 함수가 생성되어 있다.
(물론 모든 함수들은 트리거로 위에서 생성했던 API Gateway 잡혀있음!!)
- ConnectHandler
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = async function (event, context) {
try {
await ddb
.put({
TableName: process.env.table,
Item: {
connectionId: event.requestContext.connectionId,
},
})
.promise();
} catch (err) {
return {
statusCode: 500,
};
}
return {
statusCode: 200,
};
};
DynamoDB 테이블에 connectId 값을 넣는 작업
- DisconnectHandler
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = async function (event, context) {
await ddb
.delete({
TableName: process.env.table,
Key: {
connectionId: event.requestContext.connectionId,
},
})
.promise();
return {
statusCode: 200,
};
};
DynamoDB 테이블에 connectId 값에 해당되는 record 삭제
(따라서 위 두 함수의 역할에는 기본적인 Lambda + DynamoDB 에 접근할 수 있는 권한이 필요!)
- DefaultHandler
const AWS = require('aws-sdk');
exports.handler = async function (event, context) {
let connectionInfo;
let connectionId = event.requestContext.connectionId;
const callbackAPI = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint:
event.requestContext.domainName + '/' + event.requestContext.stage,
});
try {
connectionInfo = await callbackAPI
.getConnection({ ConnectionId: event.requestContext.connectionId })
.promise();
} catch (e) {
console.log(e);
}
connectionInfo.connectionID = connectionId;
await callbackAPI
.postToConnection({
ConnectionId: event.requestContext.connectionId,
Data:
'Use the sendmessage route to send a message. Your info:' +
JSON.stringify(connectionInfo),
})
.promise();
return {
statusCode: 200,
};
};
잘못된 경로(?)로 들어오면... 들어오는 방법 설명!
- sendMessageHandler
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = async function (event, context) {
let connections;
try {
connections = await ddb.scan({ TableName: process.env.table }).promise();
} catch (err) {
return {
statusCode: 500,
};
}
const callbackAPI = new AWS.ApiGatewayManagementApi({
apiVersion: '2018-11-29',
endpoint:
event.requestContext.domainName + '/' + event.requestContext.stage,
});
const message = JSON.parse(event.body).message;
const sendMessages = connections.Items.map(async ({ connectionId }) => {
if (connectionId !== event.requestContext.connectionId) {
try {
await callbackAPI
.postToConnection({ ConnectionId: connectionId, Data: message })
.promise();
} catch (e) {
console.log(e);
}
}
});
try {
await Promise.all(sendMessages);
} catch (e) {
console.log(e);
return {
statusCode: 500,
};
}
return { statusCode: 200 };
};
메시지 주고 받게...
(위 두 함수들은 연결된 클라이언트들 호출을 위한 "execute-api:ManageConnections" 권한이 필요하다.)
❗ 근데.. 위는 정말 간단하게 메시지 주고 받는 거고... 실제로 사용할 땐 어떤 식으로 사용하는지 궁금하다... ❗
그리고 웹소켓 연결 후 주고받는 메시지들은 어떻게 관리가 되는지, 유실은 없는지 등의 궁금증이 생기네...?
'Cloud > AWS' 카테고리의 다른 글
[AWS] Serverless Service - Lambda 편 (2) (0) | 2022.11.05 |
---|---|
[AWS] Serverless Service - Lambda 편 (1) (0) | 2022.11.05 |
[AWS] Lambda Warm Start / Cold Start (0) | 2022.10.28 |
[AWS] Fan-Out 시나리오 -1(easy) (0) | 2022.10.11 |
[AWS] Event-Driven Architecture (0) | 2022.10.11 |