본문으로 건너뛰기
취준생을 위한 n8n 실전 워크플로우 레시피 · 7/44시리즈 보기 →

결제가 일어나면 바로 디스코드 알림 — n8n 결제 웹훅으로 외부 서비스 이벤트 받기

· 약 9분
Datapopcorn CEO / AI automation educator
이 글과 연결된 글 보기
7편 · 20개 연결
100%
n8n Data Table 처음 써보기 — 트렌드 3개를 합쳐 매일 아침 분석 주제 뽑는 워크플로우n8n으로 정적 웹서버 운영하기 — 수료증 발급부터 진위 확인까지, 멱등 처리로 안전하게벡터DB 없이 실시간으로 사이트를 읽는 챗봇 — 경복대학교 안내 챗봇 해부n8n으로 반복업무 자동화 포트폴리오 만들기결제가 일어나면 바로 디스코드 알림 — n8n 결제 웹훅으로 외부 서비스 이벤트 받기구글시트에 쌓인 데이터를 인스타그램 카드뉴스로 — n8n 시트 인스타그램 자동발행서브워크플로우를 Tool로 감싸 AI Agent에게 서버 점검시키기 — 그리고 시스템 프롬프트만으로는 부족한 이유

드래그로 이동 · 휠/두 손가락으로 확대 · 방향키로 글 선택 · 0으로 맞춤. 실선: 본문 링크, 점선: 시리즈·주제의 대표 연결.

표시된 글 7편 목록
전체 글 연결 탐색

결제나 주문이 일어날 때 이벤트를 보내주는 서비스를 쓰고 있다면, n8n Webhook 노드 하나로 그 이벤트를 받아 곧바로 알림으로 바꿀 수 있습니다. 이 글의 워크플로우는 래피드에서 스터디 상품이 팔리는 순간 구매자를 디스코드 스터디 역할에 넣고, 채널에 "OO님이 구매를 완료했습니다"를 띄웁니다. 앞선 편들이 정해진 시간에 읽으러 가는 폴링이었다면 이 편은 사건이 생길 때 상대가 알려주는 푸시 방식이라, 자동화의 두 번째 기본 패턴이 손에 남습니다.

이 워크플로우 하나로 보여줄 수 있는 것​

재고 알림 편처럼 앞선 편들은 n8n이 정해진 시간에 시트나 API를 읽으러 가는 구조였습니다. 이 편은 방향이 반대입니다. 외부 서비스가 "방금 결제가 됐어요"라고 n8n에 찾아오고, n8n은 그 요청을 받아서 처리합니다. 노드는 4개뿐이지만 면접에서 설명할 거리는 세 가지가 들어 있습니다.

  • 웹훅 수신 — 외부 서비스의 outgoing webhook을 받을 URL을 열고, 테스트 URL과 프로덕션 URL을 구분해 등록해본 경험
  • 모르는 페이로드 다루기 — 내가 만들지 않은 JSON이 들어올 때 구조를 확인하고 필요한 필드만 뽑아 쓴 경험
  • 성공·실패 분기 — 외부 API 호출이 실패했을 때 워크플로우를 멈추지 않고 실패용 메시지로 갈라 보낸 경험

이 세 가지는 폼 제출, 신규 가입, 고객 문의 접수처럼 "서비스에서 사건이 생기면 우리 쪽에서 뭔가 하기" 유형의 자동화 전부에 그대로 쓰입니다. NFC 웹훅 편과 사진 습관 트래커 편에서도 Webhook 노드를 썼지만, 그때는 보내는 쪽도 내가 만든 것이었습니다. 이번에는 남의 서비스가 보내기 때문에 페이로드 형식을 내가 정할 수 없다는 게 이 편의 핵심입니다.

배경 — 결제 확인하고 초대하는 일을 사람이 하던 때​

데이터팝콘은 스터디 상품을 크리에이터 커머스 플랫폼 래피드에서 팔고, 스터디는 디스코드 서버에서 진행합니다. 구매가 들어오면 누군가 판매 내역을 확인하고, 구매 폼에 적힌 디스코드 아이디에 스터디 역할을 붙여주고, 채널에 새 멤버를 알려야 합니다. 결제는 새벽에도 일어나는데 사람은 아침에야 확인하니, 구매자는 몇 시간을 아무 안내 없이 기다리게 됩니다.

원본 캔버스의 스티키 노트에는 이 워크플로우의 목적이 이렇게 적혀 있습니다. "판매 내역을 외부에 자동으로 보내기. 누가 상품을 사면 웹훅이 바로 '00이 결제했어요!'라고 알려주고, 자동으로 감사 메일을 보내거나 구매자 정보가 노션에 저장되거나 다음 상품 추천 또는 쿠폰이 날아가게 할 수 있음." 이 워크플로우는 그중 디스코드 초대와 알림만 구현한 최소 버전입니다. 스티키 노트에는 래피드 웹훅 가이드 링크와 요청 형식·페이로드 스키마도 옮겨져 있어서, 이 글의 페이로드 설명은 거기에 근거합니다. 설치나 기본 노드 설명은 n8n으로 반복업무 자동화 포트폴리오 만들기에 있으니 이 글에서는 워크플로우 자체만 다룹니다.

구조 — Trigger / Task / Target​

  • Trigger — 래피드가 결제 이벤트를 POST로 보내는 Webhook 노드
  • Task — 페이로드에서 구매자 이름과 폼 답변(디스코드 아이디)을 꺼내 디스코드 봇으로 스터디 역할 부여
  • Target — 역할 부여 성공 시 채널에 구매 축하와 초대 안내, 실패 시 아이디를 다시 확인해달라는 메시지

Task와 Target이 모두 디스코드인데 인증이 다릅니다. 역할 부여는 봇 권한이 필요해서 Discord Bot credential을, 메시지를 올리는 두 노드는 채널 웹훅 URL 하나면 되는 Discord Webhook credential을 씁니다. 메시지만 보낼 거라면 봇 없이 웹훅 쪽이 훨씬 간단합니다.

만들기​

1. Webhook 노드 — URL 두 개의 차이부터​

Webhook 노드를 놓고 HTTP Method를 POST, Path를 latpeed처럼 서비스 이름으로 둡니다. 노드를 열면 URL이 두 개 보입니다. Test URL(/webhook-test/latpeed)은 Listen for test event를 누르고 있는 동안만 열리고 들어온 요청이 출력 패널에 바로 표시됩니다. Production URL(/webhook/latpeed)은 Active를 켠 뒤부터 항상 열리며 결과는 Executions 탭에 쌓입니다.

외부 서비스에 테스트 URL을 등록해두고 Listen을 끄면 요청이 전부 실패하고, 프로덕션 URL을 등록했는데 Active를 안 켜면 아무 일도 안 일어납니다. 페이로드를 확인하는 동안은 테스트 URL, 확인이 끝나면 프로덕션 URL 등록 후 Active라고 순서를 정해두면 됩니다. Respond는 기본값(Immediately)으로 둡니다. 받자마자 응답을 돌려주고 나머지 노드는 뒤에서 실행되므로 보내는 쪽이 응답을 오래 기다리지 않습니다.

2. 페이로드 구조 잡기 — 테스트 결제 한 번​

래피드 쪽 설정에서 웹훅 URL에 위의 테스트 URL을 넣고, Listen for test event 상태에서 결제를 한 번 일으킵니다. 원본 캔버스에 핀으로 고정된 데이터를 보면 amount가 0이고 option이 test로 되어 있어서, 실제 결제 대신 0원 테스트 상품으로 페이로드를 잡아둔 것으로 보입니다. 이렇게 받은 요청을 출력 패널의 핀 아이콘으로 고정해두면, 결제를 다시 일으키지 않아도 뒤 노드를 그 데이터로 계속 실행해볼 수 있습니다.

스티키 노트에 옮겨진 래피드 웹훅의 요청 형식과 페이로드입니다. 이름과 이메일은 가이드의 예시값 그대로입니다.

POST /[webhook_url] HTTP/1.1
User-Agent: Latpeed Webhook
Content-Type: application/json
{
"type": "NORMAL_PAYMENT",
"payment": {
"name": "래피드",
"email": "[email protected]",
"phoneNumber": "01012341234",
"amount": 1000,
"status": "SUCCESS",
"date": "2025-04-23T00:00:00.000+09:00",
"method": "CARD",
"option": "옵션 1",
"forms": [
{ "question": "질문 1", "answer": "응답 1" }
],
"agreements": [
{ "question": "동의사항 1", "answer": true }
]
}
}

type은 NORMAL_PAYMENT 또는 MEMBERSHIP_PAYMENT, status는 SUCCESS 또는 CANCEL이고 취소일 때는 canceledReason이 붙습니다. forms는 판매자가 구매 폼에 넣어둔 질문과 구매자의 답변 배열입니다. 데이터팝콘 스터디 상품은 첫 질문이 "디스코드 서버에서 쓰는 아이디를 알려주세요"라서, 이 워크플로우가 뽑아 쓰는 값은 forms[0].answer 하나입니다.

Webhook 노드는 들어온 요청을 headers, params, query, body로 나눠서 넘깁니다. 위 JSON은 전부 body 아래에 있으므로 구매자 이름은 {{ $json.body.payment.name }}, 디스코드 아이디는 {{ $json.body.payment.forms[0].answer }}로 꺼냅니다. 필드가 많이 들어오지만 이 워크플로우가 실제로 쓰는 건 이 두 개뿐입니다.

3. Discord 봇으로 역할 부여 — 실패해도 멈추지 않게​

Discord 노드를 놓고 Connection Type을 Bot Token, Resource를 Member, Operation을 Role Add로 둡니다. 봇은 Discord Developer Portal에서 만들어 서버에 초대하고 토큰을 Discord Bot credential에 넣습니다. 봇의 역할이 부여하려는 역할보다 서버 역할 목록에서 위에 있어야 하는데, 이건 디스코드 자체 규칙입니다.

Server는 데이터팝콘 서버를, Role에는 스터디 역할을, User ID에는 {{ $json.body.payment.forms[0].answer }}를 넣습니다. 디스코드의 역할 부여 API는 사용자 이름이 아니라 숫자로 된 사용자 ID를 요구해서, 구매자가 폼에 닉네임을 적으면 이 노드가 실패합니다. 원본은 그 경우를 에러로 끝내지 않고 노드 Settings의 On Error를 Continue (using error output)로 두어, 성공 아이템은 위 출력으로 실패 아이템은 아래 출력으로 갈라 보냅니다. Retry On Fail도 켜 두어서 일시적인 네트워크 오류는 재시도 뒤에야 실패 분기로 넘어갑니다.

4. 성공·실패 메시지 두 통​

성공 출력에는 Discord 노드(Connection Type: Webhook, Operation: Send a Message)를 붙이고 Message에 다음을 넣습니다.

🎉 **{{ $('Webhook').item.json.body.payment.name }}**님이 래피드에서 구매를 완료해주셨습니다!
💬 Discord ID {{ $('Webhook').item.json.body.payment.forms[0].answer }} 로 스터디방에 초대해드렸습니다.

실패 출력에는 같은 credential의 Discord 노드를 하나 더 붙입니다.

🎉 **{{ $json.body.payment.name }}**님이 래피드에서 구매를 완료해주셨습니다!
⚠️ 입력하신 Discord ID {{ $('Webhook').item.json.body.payment.forms[0].answer }} 가 올바르지 않은 것 같아요. 다시 한 번 확인해 주세요!

표현식이 조금 다른 이유가 있습니다. 성공 쪽은 앞 노드가 역할 부여 결과를 돌려주므로 $json에 결제 정보가 없고, 그래서 $('Webhook').item으로 Webhook 노드의 아이템을 직접 가리킵니다. 실패 쪽은 에러 출력으로 원래 입력 아이템이 넘어오기 때문에 $json.body로도 읽힙니다. 어느 쪽이든 $('Webhook').item으로 통일해두면 중간에 노드를 끼워 넣어도 깨지지 않습니다.

5. 프로덕션 URL 등록하고 Active 켜기​

핀 데이터로 세 노드가 다 동작하면 래피드 설정의 웹훅 URL을 프로덕션 URL로 바꾸고 워크플로우를 Active로 켭니다. 이때부터 결제마다 Executions 탭에 실행이 쌓입니다. 처음 며칠은 실패 분기로 간 건이 얼마나 되는지 보면, 구매자가 숫자 ID를 제대로 적는지 바로 알 수 있습니다.

원본 캔버스에 남아 있는 흔적 — 외부 서비스 없이 웹훅 테스트하기​

원본에는 본 흐름과 연결되지 않은 HTTP Request 노드가 하나 떠 있습니다. 이 워크플로우 자신의 프로덕션 웹훅 주소로 POST를 보내되 헤더에 User-Agent: Latpeed Webhook, Body에 example: test를 넣어 둔 노드로, 래피드를 거치지 않고 n8n이 스스로에게 래피드인 척 요청을 보내 웹훅이 열려 있는지 확인한 흔적입니다.

외부 서비스에서 이벤트를 매번 일으키기 번거로울 때 따라 할 만한 방법입니다. IDE 터미널에서 테스트 URL로 직접 요청을 쏘면 됩니다.

curl -X POST "https://<내 n8n 주소>/webhook-test/latpeed" \
-H "User-Agent: Latpeed Webhook" \
-H "Content-Type: application/json" \
-d '{"type":"NORMAL_PAYMENT","payment":{"name":"홍길동","email":"[email protected]","amount":0,"status":"SUCCESS","forms":[{"question":"디스코드 아이디","answer":"123456789012345678"}]}}'

Listen for test event를 켜둔 상태에서 실행하면 출력 패널에 요청이 잡힙니다. 다만 이 페이로드는 스키마를 보고 직접 만든 것이라 실제 요청과 다를 수 있으니, 실제 요청을 한 번은 받아서 핀해두고 그다음부터 가짜 요청으로 변형을 실험하는 순서가 안전합니다.

한계 — 원본에 없는 네 가지​

그대로 옮기면 동작은 하지만, 운영에 쓰려면 원본에 빠진 것들을 알아야 합니다.

  1. 요청 검증이 없습니다 — 프로덕션 URL을 아는 사람은 누구나 같은 형식으로 POST를 보내 역할을 받아갈 수 있고, User-Agent 헤더는 흉내 내기 쉬워 검증이 아닙니다. 보내는 쪽이 커스텀 헤더나 서명을 지원하면 Webhook 노드의 Authentication(Header Auth)을 켜고, 아니면 최소한 Path를 추측하기 어려운 긴 문자열로 바꿔야 합니다.
  2. 결제 상태를 보지 않습니다 — 스키마에 status가 CANCEL인 경우가 명시돼 있는데 원본에는 이를 거르는 노드가 없어서, 취소 이벤트에도 역할이 부여되고 축하 메시지가 나갑니다. Webhook 뒤에 IF 노드를 두고 SUCCESS일 때만 진행하며, CANCEL이면 Member: Role Remove로 역할을 회수하는 분기를 추가하면 완성됩니다.
  3. 구매자가 숫자 ID를 알아야 합니다 — 디스코드 사용자 ID는 설정에서 개발자 모드를 켜야 복사할 수 있어서 구매자가 닉네임을 적기 쉽습니다. 폼 질문에 ID 복사 방법을 적어두는 게 실패 분기를 줄이는 가장 싼 방법입니다.
  4. 개인정보가 실행 로그에 남습니다 — 이름·이메일·전화번호가 통째로 들어와 Executions에 저장됩니다. 워크플로우 Settings에서 성공 실행의 데이터 저장을 끄거나 보관 기간을 짧게 잡고, 실명이 올라갈 채널이 공개 채널인지도 확인해야 합니다.

포트폴리오·면접 포인트​

이 패턴은 래피드에만 묶여 있지 않습니다. Stripe, 토스페이먼츠, 스마트스토어, 폼 도구처럼 outgoing webhook을 제공하는 서비스라면 페이로드 형식만 각자 문서에서 확인하면 되고, Webhook으로 받기 → 테스트 이벤트로 구조 확인 → 필드 추출 → 후속 동작이라는 뼈대는 같습니다. 포트폴리오에는 자기가 실제로 쓰는 서비스에 이 뼈대를 붙이고, 위 한계 중 2번(상태 분기)을 직접 채운 버전이 좋습니다. 취소 처리까지 넣은 사람과 성공 케이스만 만든 사람은 면접에서 확실히 구분됩니다.

면접에서는 "판매 내역을 스케줄로 조회하면 되는데 왜 웹훅을 썼나요?" 같은 질문이 올 수 있습니다. 폴링은 주기만큼 지연이 생기고 결제가 없는 시간에도 빈 조회를 반복하는 반면, 푸시는 사건이 생긴 순간 한 번만 실행되고 URL 하나만 열어두면 된다고 답하면 됩니다. 여기에 "대신 우리 쪽이 항상 켜져 있어야 하고 요청을 검증할 책임이 생긴다"는 반대급부까지 말하면 두 방식을 이해하고 골랐다는 인상을 줍니다.

다음 액션​

여기까지가 트리거와 알림의 기본 패턴입니다. 다음 편부터는 데이터를 모으는 쪽으로 넘어가서, 공공데이터 API를 매시간 호출해 구글시트에 쌓는 워크플로우를 다룹니다. 트리거는 다시 스케줄이지만 목적이 알림이 아니라 적재라서 API 응답을 행 단위로 쪼개 넣는 단계가 새로 등장합니다. 이 편의 웹훅과 다음 편의 스케줄 수집을 둘 다 만들어두면 포트폴리오에 "이벤트 기반"과 "주기 기반" 자동화를 나란히 놓을 수 있습니다.