Serverless로 챗봇을 만드는 과정에서 ReceiverMultipleAckError가 발생했습니다. 간단한 문제(?)였지만 자료가 많이 없다보니 생각보다 고치는 데 시간이 오래 걸리는 에러였습니다.
아래의 사진처럼 Slash command를 입력하면
slack창에 모달창이 뜨고, 블로그 이름과 URL을 입력한 뒤 확인 버튼을 누르면 해당 테크블로깅의 Notion Database에 작성자와 이름과 블로그 URL이 저장되는 기능입니다.
그런데 아래 사진을 보면 노션에 1번만 업로드가 되야하는 게 총 3번이 되었습니다. 다시 말하자면 지금 handler가 여러 번 실행이 되는 문제에 직면하게 되었습니다.
또한 Slash command를 입력했을 때도 중복으로 요청이 되는 문제를 확인할 수 있었습니다. 아래 Log를 보면 한 번 요청을 보냈는데 중복으로 요청이 들어간 걸 확인할 수 있습니다.
결론적으로 말하자면 현재 저는 Lambda에 2개의 handler를 등록해 놓았습니다.
1) Slash command를 입력하면 슬랙 모달창이 뜨는 submitNotionCommandHandler
2) 슬랙 모달창의 제출 버튼을 누르면 Notion DB에 정보 저장하는 uploadNotionBlogViewHandler
에러를 분석해보자면 Slack event가 발생할 때마다 handler가 여러번 호출되는 상황입니다.
왜 중복으로 호출된 것인가? 이 문제로 오랜 시간동안 고민했습니다. 이렇게 생각하던 와중에 Log에서 항상 거슬리는 부분을 유심히 생각하게 되었습니다.
이 Log는 submitNotionCommandHandler를 호출할 때 발생되는 에러입니다. 잘 보시면 아래 awsLambdaReceiver가 중복으로 호출이 된 것을 확인할 수 있습니다.
POST /slack/events (λ: slackBotHandler)
awsLambdaReceiver 실행 ->1번 호출(?)
(λ: slackBotHandler) RequestId: de9ccc84-f8a4-4a12-9f68-f9e8b3265ff0 Duration: 225.03 ms Billed Duration: 226 ms
POST /slack/command (λ: slackBotHandler)
awsLambdaReceiver 실행 -> 2번호출(??)
submitNotionCommandHandler 시작
submitHandler에서 ack 실행전
submitNotionCommandHandler 시작
submitHandler에서 ack 실행전
submitHandler에서 ack 실행후
submitNotionCommandHandler 종료
[ERROR] bolt-app ReceiverMultipleAckError: The receiver's `ack` function was called multiple times.
at ack (/Users/choehyeonjeong/Documents/Developer/Node/Nest/Notion_Bot/node_modules/@slack/bolt/dist/receivers/AwsLambdaReceiver.js:95:31)
그래서 저는 handler.ts(Lambda함수의 진입점)에 Log를 좀 더 추가해보았습니다.
[handler.ts]
export const handler = async(
event: AwsEvent,
context: any,
callback: AwsCallback
): Promise<AwsResponse> => {
submitNotionCommandHandler(app);
uploadNotionBlogViewHandler(app, notion);
console.log('Lambda에 handler 등록');
const slackHandler = await awsLambdaReceiver.start();
console.log('awsLambdaReceiver 실행');
return slackHandler(event, context, callback);
}
그리고 다시 Slash command를 실행해보았습니다.
이 Log를 보고 원인을 파악했습니다.
원래 정상적인 로직은 처음 앱이 실행되었을 때 최초로 Lambda에 handler들을 등록하고, Slack event가 발생했을 때 awsLambdaReceiver.start()만 호출되어 Api GateWay가 Lambda에 등록된 handler들로 라우팅을 해주며 event 종류에 따라 Lambda함수가 알맞은 handler를 실행하는 로직입니다.
그런데 위의 로그를 보면 앱이 실행되었을 때 딱 한 번만 실행되어야 하는 로직임에도 불구하고 handler들이 호출될 때마다 Lambda함수에 handler들을 등록하는 상황이 발생합니다.
즉, handler.ts
파일에서 submitNotionCommandHandler(app)와 uploadNotionBlogViewHandler(app, notion)
이 여러 번 호출 되는 문제였습니다.
문제였던 부분은 이 부분이 중복으로 초기화(Lambda 함수에 등록) 되는 부분입니다.
submitNotionCommandHandler(app);
uploadNotionBlogViewHandler(app, notion);
그래서 저는 이 부분을 앱이 처음 실행되었을 때 단 한번만 호출될 수 있도록 handlerRegistered 라는 boolean 변수를 만들어 코드를 수정했습니다.
[handler.ts]
let handlerRegistered = false; // Lambda에 한 번만 등록할 수 있도록 하는 변수 생성
export const handler = async(
event: AwsEvent,
context: any,
callback: AwsCallback
): Promise<AwsResponse> => {
if(!handlerRegistered){ // Lambda에 handler가 등록되지 않았다면 handler 등록
submitNotionCommandHandler(app);
uploadNotionBlogViewHandler(app, notion);
handlerRegistered = true;
console.log('lambda에 handler 등록');
}
const slackHandler = await awsLambdaReceiver.start();
console.log('awsLambdaReceiver 실행');
return slackHandler(event, context, callback);
}
이렇게 코드를 수정하고 다시 실행해 보았습니다.
그 결과 Lambda에 handler를 등록하는 로직은 단 한번만 나오는 것을 확인할 수 있습니다.
이 에러를 해결하기 위해서는 Lambda에 대해서 잘 알고 있어야 합니다. 특히 handler.ts의 코드가 어떻게 돌아가는지? 와 Lambda가 언제 handler들을 등록하는 지 등.. 알고 있어야지만 해결할 수 있는 에러였다고 생각합니다.
슬랙봇을 만드실 때 정확한 로직이 이해가 안가시면 제 전블로그 https://velog.io/@ppinkypeach/SlackBot-개발-Aws-Lambda-CloudWatch-Error-No-request-handler-matched-the-request-undefined-해결
의 정리 부분을 보시면 도움이 될 거라 생각합니다.