들어가며
사용자가 언제 불편함을 느낄까? 사용자의 의견이 너무나도 궁금했다. 그래서 피드백 모달을 만들었다.
위와 같이 사용자가 피드백을 입력하면 Supabase DB에 피드백 데이터가 저장되는 방식이다. Supabase에 매일같이 접속을 해서 ‘사용자가 피드백을 추가했나, 안했나?’ 확인할 수는 없는 격이다. 그럼에도 사용자가 피드백을 해주면 빠르게 이에 대응하고 싶었다.
어떻게 하면 사용자가 피드백을 추가하면 즉시 나에게 알림을 구현할 수 있을까?에 대한 여정을 이 글에서 이야기하고자 한다.
개념 설명
Webhook
웹훅은 서버와 클라이언트 간의 실시간 통신을 가능하게 하는 메커니즘이다. 전통적인 폴링(polling) 방식과 달리, 웹훅은 이벤트가 발생했을 때만 통신이 이루어지므로 서버 리소스를 절약할 수 있다. 택배 알림이랑 비슷하다.
보통 웹훅을 활용하여 결제 시스템, 알림 시스템, 소셜 미디어 통합 등의 기능을 구현한다. 예를 들어, 결제 시스템에서는 결제가 완료되었을 때 웹훅을 통해 서버에 알림을 보내고, 이를 통해 주문 처리나 배송 프로세스를 자동으로 시작할 수 있다. 또한 소셜 미디어 플랫폼에서는 새로운 게시물이나 댓글이 작성되었을 때 웹훅을 통해 관련 시스템에 알림을 보낼 수 있다.
Webhook을 제공하는 서비스에는 다음과 같은 서비스가 있다.
- 데이터베이스/백엔드 서비스
- Supabase: 데이터베이스 변경 사항에 대한 실시간 웹훅
- Firebase: Realtime Database와 Firestore의 데이터 변경 웹훅
- MongoDB Atlas: 데이터베이스 변경 사항 트리거 웹훅
- PostgreSQL: pg_notify와 Listen/Notify 기능
- 결제/금융 서비스
- Stripe: 결제, 환불, 구독 상태 변경 등의 웹훅
- PayPal: 결제 상태 및 거래 관련 웹훅
- Toss Payments: 결제 진행 상태 웹훅
- 이메일/메시징 서비스
- SendGrid: 이메일 전송, 열람, 클릭 등의 이벤트 웹훅
- Mailgun: 이메일 상태 및 활동 관련 웹훅
- Slack: 메시지, 채널 활동 등의 웹훅
- Discord: 서버 활동 및 메시지 관련 웹훅
- 저장소/버전 관리
- GitHub: 코드 푸시, PR, 이슈 등의 웹훅
- GitLab: 저장소 활동 및 CI/CD 이벤트 웹훅
- Bitbucket: 코드 변경 및 저장소 활동 웹훅
- CI/CD 서비스
- Jenkins: 빌드 결과 및 파이프라인 상태 웹훅
- CircleCI: 빌드 및 배포 상태 웹훅
- Travis CI: 빌드 결과 알림 웹훅
AWS Lambda
AWS Labmda는 서버리스 컴퓨팅 서비스다. 서버를 직접 관리하지 않고도 코드를 실행할 수 있다. 이벤트에 반응하여 자동으로 코드를 실행하며, 사용한 컴퓨팅 시간과 호출한 횟수에 대해서만 비용을 지불한다.
보통 주기적인 배치 작업, 이미지 처리, 데이터베이스 업데이트와 같은 백엔드 작업을 수행할 때 사용한다. 특히 이벤트 기반으로 뭔가 만들 때 편했다.
서버리스 컴퓨팅 서비스
AWS Labmda외의 서버리스 컴퓨팅 서비스에는 다음과 같은 서비스가 있다.
- AWS Lambda : AWS 생태계와의 뛰어난 통합, 다양한 런타임 환경 지원 (Node.js, Python, Java 등), Cold Start 이슈가 있지만 Provisioned Concurrency로 완화 가능
- Google Cloud Functions : Firebase와의 원활한 통합, 자동 스케일링, 이벤트 기반 트리거링 지원
- Azure Functions : .NET 생태계와의 강력한 통합, 다양한 바인딩과 트리거 지원, 하이브리드 클라우드 환경에 적합
AWS Api Gateway
AWS API Gateway는 API를 생성, 배포 및 관리할 수 있게 해주는 완전관리형 서비스이다. REST API나 WebSocket API를 쉽게 생성할 수 있으며, 인증, 액세스 제어, 모니터링 등의 기능을 제공한다. 특히 Lambda 함수와 연동하여 서버리스 API를 구축하는 데 많이 활용되며, 트래픽에 따라 자동으로 확장되는 특징이 있다.
AWS IAM
AWS IAM(Identity and Access Management)은 AWS 리소스에 대한 접근을 안전하게 제어할 수 있게 해주는 서비스이다. 사용자, 그룹, 역할을 생성하고 관리하며, 각각에 대한 권한을 세밀하게 설정할 수 있다. 이를 통해 보안 모범 사례인 최소 권한 원칙을 실현하고, AWS 리소스에 대한 무단 접근을 효과적으로 차단할 수 있다.
IAM 정책
IAM 정책(Policy)은 AWS 리소스에 대한 접근 권한을 정의하는 JSON 문서이다. 정책은 특정 AWS 서비스나 리소스에 대해 어떤 작업을 수행할 수 있는지를 상세하게 명시하며, 'Allow'나 'Deny' 문을 통해 권한을 명확히 제어할 수 있다. 예를 들어, Lambda 함수가 SNS로 메시지를 발행하기 위해서는 'sns:Publish' 권한이 포함된 정책이 필요하다.
IAM 역할
IAM 역할(Role)은 AWS 서비스나 리소스가 다른 AWS 서비스에 접근할 때 필요한 임시 보안 자격 증명을 제공한다. 예를 들어, Lambda 함수가 다른 AWS 서비스를 호출할 때는 해당 서비스에 접근할 수 있는 적절한 권한을 가진 IAM 역할이 필요하다. 이러한 역할은 정책을 통해 권한을 정의하며, 필요한 경우 여러 정책을 하나의 역할에 연결할 수 있다.
실전
TL;DR
사용자가 피드백을 작성하면 자동으로 휴대폰으로 알림이 오도록 구현하는 방법을 설명한다.
Supabase Webhook으로 피드백 이벤트를 감지하고, AWS Lambda로 메시지 전송 로직을 구현하며, API Gateway를 통해 웹훅 엔드포인트를 제공한다. 이를 통해 실시간으로 사용자 피드백에 대응할 수 있는 시스템을 구축할 수 있다.
SNS는 AWS SNS 서비스를 사용했다. 처음에는 Twilio라는 SNS 서비스를 사용을 고려했으나 caller id가 등록 안되는 이슈가 있기도 했고, AWS SNS가 AWS의 Lambda, CloudWatch 등 다른 서비스와 원활한 통합이 더 유리해보여 AWS SNS를 선택했다.
1. AWS Lambda 생성
AWS Labmda는 나에게 메시지를 전달할 핵심적인 역할을 수행한다. 이를 위해서는 IAM에서 정책과 역할을 생성하고 이를 Lambda에 설정해야한다.
1) IAM - 정책 생성
이를 위해 가장 먼저 IAM에 접속해보자.
- IAM에서 정책 탭에 들어가면 이미 1300여개에 달하는 정책들이 있는 것을 확인할 수 있다.
위 정책들은 AWS에서 기본적으로 제공하는 정책들로 AWS 서비스들에 대한 다양한 접근 권한을 정의해놓은 것이다. 이러한 정책들은 필요에 따라 선택하여 사용할 수 있으며, 직접 커스텀 정책을 만들 수도 있다.
Lambda 함수가 SMS 메시지를 보낼 수 있도록 SNS 서비스를 사용할 권한이 필요하다. 이를 위해 먼저 SNS 서비스 사용을 위한 IAM 정책을 생성해야 한다.
- 우측 상단의 정책 생성을 누른다.
- 서비스 선택에서 SNS를 선택한다.
- 작업에서 쓰기에서 publish를 선택한다.
우리가 사용할 기능은 SNS 서비스의 Publish이다.
- 리소스는 모두를 선택한다.
이렇게 리소스를 모두 선택하면, SNS의 모든 주제(topic)에 대해 메시지를 발행할 수 있는 권한이 부여된다. 실제 프로덕션 환경에서는 특정 SNS 주제의 ARN만을 지정하여 더욱 세분화된 권한 관리를 할 수 있지만, 이 예제에서는 간단히 모든 리소스에 대한 권한을 부여하도록 하겠다.
- 마지막으로 정책 이름을 설정해 정책을 생성한다.
적절한 정책 이름을 입력하고, 생선 버튼을 누른다.
- 생성이 잘 되었는지 확인한다.
2) IAM - 역할 생성
이제 Lambda 함수가 SNS를 사용할 수 있도록 하는 역할을 생성해보자.
- IAM 콘솔에서 '역할' 탭으로 이동한 후, '역할 생성'을 클릭한다.
- 신뢰할 수 있는 엔티티 유형은 AWS서비스, 서비스에서는 Lambda를 선택한다.
- 권한 정책은 앞서 만든 SNSPublishPolicy를 선택한다.
3) AWS Lambda 생성
이제 Labmda를 만들기 위한 과정은 모두 마쳤다. 이제 Lamdda를 만들어보자.
- Labmda에 접속해 우측 상단의 함수 생성을 누른다.
- 함수 이름과 런타임을 설정한다. 함수 이름은 'sendSMSNotification'과 같이 기능을 명확히 알 수 있게 지정하고, 런타임은 JavaScript환경을 사용할 것이기에 Node.js를 선택한다.
- 실행 역할에서는 앞서 생성한 LambdaSNSRole을 선택한다. 이를 통해 Lambda 함수가 SNS 서비스를 사용할 수 있는 권한을 가지게 된다.
- 고급 설정은 기본값을 유지하고 '함수 생성'을 클릭하여 Lambda 함수를 생성한다.
- Lambda 생성 완료
이제, 내 마음대로 JavaScript 코드를 작성하고, 실행할 수 있는 가상 환경이 만들어졌다.
좌측의 Deploy를 통해 저장된 파일을 배포할 수 있고, Test를 통해 테스트도 가능하다.
4) AWS Labmda 코드 작성
Labmda코드를 작성하기에 앞서, 환경 변수를 아래와 같이 설정할 수 있다.
아래 코드에서 process.env를 통해 위 환경 변수에 접근할 것이다.
import { SNSClient, PublishCommand } from "@aws-sdk/client-sns";
const snsClient = new SNSClient({ region: "ap-northeast-1" });
const SNS_MESSAGE = process.env.SNS_MESSAGE;
const ADMIN_PHONE_NUMBER = process.env.ADMIN_PHONE_NUMBER;
export const handler = async (event) => {
try {
const command = new PublishCommand({
Message: SNS_MESSAGE,
PhoneNumber: ADMIN_PHONE_NUMBER
});
const data = await snsClient.send(command);
return {
statusCode: 200,
body: JSON.stringify({
message: "SMS sent successfully",
messageId: data.MessageId
})
};
} catch (error) {
console.error("Error:", error);
return {
statusCode: 500,
body: JSON.stringify({
error: error.message
})
};
}
};
위 코드에 대해 간략하게 설명하면,
- SNSClient객체와 PublishCommad객체를 생성한다.
const snsClient = new SNSClient({ region: "ap-northeast-1" });
// ..
const command = new PublishCommand({
Message: SNS_MESSAGE,
PhoneNumber: ADMIN_PHONE_NUMBER
});
SNSClient는 AWS SNS(Simple Notification Service)와 통신하기 위한 클라이언트이다. region 파라미터를 통해 SNS 서비스를 사용할 AWS 리전을 지정할 수 있다. 이 때 왜 리전을 서울인 ap-southeast-2가 아니라 도쿄인 ap-northeast-1를 선택했냐면, SMS서비스는 서울 리전에서는 제공하지 않는다. SMS서비스를 지원하는 도쿄, 오하이오 등의 리전을 선택해야한다.
PublishCommand는 SNS 서비스에서 메시지를 발행(publish)하기 위한 명령 객체이다. Message와 PhoneNumber와 같은 파라미터를 포함하여 실제 SMS 메시지를 전송하는데 필요한 정보를 담고 있다.
- SNSClient인스턴스의 send메시지의 인자에 PublishCommand 인스턴스를 전달한다.
await snsClient.send(command);
2. AWS SNS 생성
이제 우리는 Lambda 함수를 만들었으니, SNS 서비스를 설정하여 실제로 SMS를 보낼 수 있도록 해보자.
AWS SNS 중 SMS(문자 메시지)는 서울 리전에서 지원하지 않는다. 그렇기에 우리는 도쿄 등 SMS서비스를 지원하는 리전을 선택해야한다. 이는 Lambda의 리전과 일치하지 않아도 된다.


- Amazon SNS > 문자 메시지(SMS) 접속한다.
- 샌드박스 대상 전화번호 추가
이 때 주의할 점은 확인 메시지 언어를 대한민국으로 선택하면 400에러가 발생한다. 그러니 선택을 하지 않고 ‘전화번호 추가’ 버튼을 눌러야한다.
- 확인 코드 입력
- 추가된 전화번호 확인
3. AWS Api Gateway 생성
물론 AWS Lambda에서도 함수 URL을 만들 수 있다.
위와 같은 방식으로 퍼블릭 URL을 만들 수 있다. 하지만 이번에는 또 다른 방법인 Api Gateway를 활용해보자. 추후 데이터를 담아 Lambda를 동작시키기 위해서는 API Gateway가 더 확장성 있을 것이라 판단했다.
- API Gateway > API 생성
- HTTP API 선택
단방향 API가 필요하니 간단하게 사용 가능한 HTTP API를 선택하자.
- 이후에는 이름을 입력하고 다음 > 다음 > 생성을 누르면 된다.
- 생성한 API Gateway 확인
엔드포인트가 만들어졌다.
이 엔드포인트가 바로 우리가 만든 AWS Lambda를 호출할 수 있는 URL이다. 이 URL은 외부에서 접근 가능하며, HTTP 요청을 통해 Lambda 함수를 트리거할 수 있게 해준다.
- 이제 경로를 만들어보자.
원하는 경로로 만들면 된다. 이 경로는 url의 pathname이 될 것이다.
- Integrations에 접속해 위에서 만든 Labmda를 연결해보자. 위 경로로 API호출을 하면 Lambda를 실행하는 것이다.
- 메서드를 Any로 설정했기 떄문에, 위 API Gateway API의 URL + pathname페이지에 접속하면 해당 Labmda가 실행될 것이다.
위와같이 성공 메시지가 JSON형태로 보인다면 성공한 것이다 🎉
4. Supabase Webhook으로 연결
Supabase에서 Webhook설정하는 건 아주 간단하다.
- Supabase > Dashboard > Database > Webhooks 접속
- Create a new webhook에서, HTTP Request에서 API Gateway에서 만든 url을 작성한다.
이제 선택한 DB에 변경사항이 생기면 해당 API Gateway로 HTTP 요청이 전송될 것이다. 이를 통해 사용자가 피드백을 작성할 때마다 자동으로 Lambda 함수가 실행되어 SMS 알림을 받을 수 있게 된다. 이렇게 Supabase의 Webhook, AWS Lambda, 그리고 SNS를 연동하여 실시간 알림 시스템을 구축할 수 있다.
결과
피드백을 등록하면, 문자로 즉시 알림이 오는 것을 확인할 수 있다.
트러블 슈팅
200응답은 오는데, 문자 메시지가 안온다면?
Lambda에서 만든 SNSClient객체의 region을 체크해보자.
const snsClient = new SNSClient({ region: "ap-northeast-1" });
이 region은 위에서 만든 AWS SNS의 region과 일치해야한다.
AWS SNS에서 텍스트 메시지 실패가 뜬다면?
Labmda에서 작성한 휴대폰 형식이 아래와 같은지 확인해보자.
const command = new PublishCommand({
Message: "..",
PhoneNumber: "+8201012345678"
});
AWS 공식문서에서는 E.164 형식을 사용할 것을 권장하지만, 해당 포멧(+821012345678)으로 작성하면 위와 같이 에러가 발생한다. 이러한 경우, +8201012345678와 같이 앞에 0을 붙여주면 해결이 된다.
맺으며
Supabase Webhook, AWS Lambda, API Gateway, SNS를 활용해 사용자의 피드백이 즉시 나에게 전달되는 시스템을 구축했다. 이제 Supabase를 매일 들여다볼 필요도 없고, 웹훅 덕에 서버 리소스도 아끼고, Lambda라 서버 관리 부담도 없다. API Gateway로 짜둔 덕에 나중에 알림 종류나 기능을 붙이기도 쉽다.
다음에는 CloudWatch를 이용해 Lambda 함수의 로그와 성능을 모니터링하고, 에러 발생 시 즉각적인 알림을 받을 수 있도록 구성해보고 싶다. 이를 통해 시스템의 안정성을 더욱 높이고 문제 상황에 더욱 빠르게 대응하는 시스템을 만들어보고 싶다.