사용자가 보낸 요청과 게임 내 모든 이벤트를 수동으로 모니터링하는 대신, 웹후크를 설정하여 HTTP 요청을 수신할 수 있는 타사 메시징 도구 또는 사용자 지정 엔드포인트에서 실시간 알림을 받을 수 있습니다. 이를 통해 알림 관리 워크플로를 자동화하여 알림 처리에 대한 수동 노력을 줄일 수 있습니다.
웹후크 워크플로
웹후크는 Roblox와 타사 메시징 도구와 같은 두 개의 서로 다른 애플리케이션 또는 서비스 간에 실시간 알림이나 데이터를 전송합니다. 데이터 수신을 위해 서버에 요청을 보내는 클라이언트 애플리케이션을 설정해야 하는 전통적인 API와 달리, 웹후크는 이벤트가 발생하는 즉시 클라이언트 엔드포인트로 데이터를 전송합니다. 이는 팀과 협업하는 데 사용하는 타사 애플리케이션과 Roblox 간의 워크플로를 자동화하는 데 유용하며, 실시간 데이터 공유 및 처리를 가능하게 합니다.
웹후크를 설정하면, 대상 이벤트가 발생할 때마다 Roblox는 제공한 웹후크 URL로 요청을 보냅니다. 웹후크 URL은 요청을 수신하는 애플리케이션이나 사용자 지정 엔드포인트로 리디렉션되며, 웹후크 페이로드에 포함된 데이터를 기반으로 작업을 수행할 수 있습니다. 여기에는 RTBF 준수를 위한 데이터 삭제, 사용자에게 확인 메시지 전송, 또는 다른 이벤트 트리거가 포함될 수 있습니다.
지원되는 트리거
Roblox는 현재 다음 이벤트 트리거를 지원합니다.
구독
- 구독 재구독 - 사용자가 구독을 재구독할 때, 구독 및 구독자 정보를 포함하는 메시지가 전송됩니다.
- 구독 갱신 - 사용자가 구독을 갱신할 때, 구독 및 구독자 정보를 포함하는 메시지가 전송됩니다.
- 구독 환불 - 사용자가 구독에 대한 환불을 받을 때, 구독 및 구독자 정보를 포함하는 메시지가 전송됩니다.
- 구독 구매 - 사용자가 구독을 구매할 때, 구독 및 구독자 정보를 포함하는 메시지가 전송됩니다.
- 구독 취소 - 사용자가 구독을 취소할 때, 구독 및 구독자 정보와 함께 취소 사유가 포함된 메시지가 전송됩니다.
구독 이벤트 및 해당 필드에 대한 자세한 내용은 구독 참조를 참조하세요.
준수
- 삭제 권리 / 삭제 요청 - 사용자가 적용 가능한 글로벌 데이터 보호 및 개인 정보 보호 규정에 따라 개인 정보를 영구적으로 삭제할 권리를 행사할 때 발생합니다. 자세한 내용은 RTBF 및 제작자에서 확인할 수 있습니다.
상거래
- 상거래 제품 주문 환불 - 사용자가 상거래 제품 주문에 대한 환불을 받았거나 주문이 취소된 경우 발생합니다.
- 상거래 제품 주문 결제 완료 - 사용자가 상거래 제품 주문에 대한 결제를 완료했을 때 발생합니다. 중복 웹후크 이벤트가 발생할 수 있으므로 고유한 상거래 주문 ID를 사용하여 이벤트를 중복 제거해야 합니다.
Creator Dashboard에서 웹후크 구성
웹후크를 통해 알림을 받으려면 알림을 트리거하기 위해 특정 이벤트에 구독하는 웹후크를 구성해야 합니다. 그룹 소유 게임의 경우, 그룹 소유자만 웹후크 알림을 구성하고 받을 수 있습니다.
웹후크를 설정하려면:
Creator Hub에서 경험을 선택합니다.
구성 아래에서 웹후크를 선택하고 웹후크 추가를 클릭합니다.
웹후크 URL은 제공업체에서 가져옵니다. 예를 들어, Slack URL은 다음과 같을 수 있습니다:
https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX웹후크 URL과 이름을 입력합니다.
- 선택 사항비밀을 포함하여 Roblox에서 수신하는 알림이 실제로 Roblox에서 온 것임을 보장합니다. 자세한 내용은 웹후크 보안 확인을 참조하세요.
알림을 받고자 하는 이벤트의 지원되는 트리거 목록에서 하나 이상의 옵션을 선택합니다.
- 선택 사항테스트 응답 버튼을 사용하여 서비스가 샘플 요청을 수신할 수 있는지 확인합니다.
변경 사항 저장을 클릭합니다.
웹후크 URL 설정
웹후크 URL로 사용자 지정 HTTP 서비스 엔드포인트를 설정할 수 있으며, 다음 요구 사항을 충족해야 합니다:
- 요청을 처리할 수 있도록 공개적으로 접근 가능해야 합니다.
- POST 요청을 처리할 수 있어야 합니다.
- 요청에 대해 5초 이내에 2XX 응답을 반환할 수 있어야 합니다.
- HTTPS 요청을 처리할 수 있어야 합니다.
엔드포인트가 POST 요청을 수신하면 다음을 수행할 수 있어야 합니다:
- POST 메시지 본문에서 알림에 대한 세부 정보를 추출합니다.
- 알림에 대한 일반 세부 정보와 이벤트 유형과 관련된 특정 세부 정보를 포함한 POST 메시지 본문을 읽습니다.
POST 요청을 처리하기 위한 스키마에 대한 자세한 내용은 페이로드 스키마를 참조하세요.
전송 실패 재시도 정책
웹후크 알림이 엔드포인트의 사용 불가와 같은 오류로 인해 지정된 URL에 도달하지 못하면, Roblox는 고정된 시간 창을 사용하여 구성된 URL로 메시지를 5회 재전송합니다. 5회 시도 후에도 알림이 여전히 전달되지 않으면 Roblox는 알림 전송을 중단하고 URL이 더 이상 유효하지 않다고 가정합니다. 이 경우, 알림을 수신할 수 있는 새로운 URL로 웹후크 구성을 업데이트해야 합니다. 웹후크 URL이 성공적으로 알림을 수신할 수 있는지 확인하고 문제를 해결하려면 웹후크 테스트를 참조하세요.
타사 요구 사항
타사 도구는 웹후크를 설정할 때 따라야 할 자체 요구 사항이 있는 경우가 많습니다. 이러한 요구 사항은 대상 도구의 지원 또는 문서 사이트에서 "웹후크"라는 키워드를 검색하여 찾을 수 있습니다. 지원되는 타사 도구는 다음과 같습니다:
웹후크 테스트
구성한 웹후크가 Creator Dashboard에서 알림을 성공적으로 수신할 수 있는지 테스트할 수 있습니다:
- 웹후크 구성 페이지로 이동합니다.
- 구성된 웹후크 목록에서 테스트할 웹후크를 선택합니다.
- 대상 웹후크 옆의 연필 아이콘을 클릭합니다.
- 테스트 응답 버튼을 클릭합니다.
시스템은 SampleNotification 이벤트를 전송하며, 여기에는 알림을 트리거한 사용자의 사용자 ID가 포함됩니다. 예시는 다음과 같습니다:
{
"NotificationId": "string",
"EventType": "SampleNotification",
"EventTime": "2023-12-30T16:24:24.2118874Z",
"EventPayload": {
"UserId": 1
}
}타사 서비스와 웹후크를 통합하는 경우, 타사 URL을 사용하여 테스트하여 서비스가 웹후크에서 알림을 성공적으로 수신할 수 있는지 확인할 수 있습니다. 웹후크를 구성할 때 비밀을 제공하면, roblox-signature도 생성되어 roblox-signature 로직을 테스트하는 데 사용할 수 있습니다.
웹후크 보안 확인
페이로드를 수신하도록 서버를 구성한 후, 해당 엔드포인트로 전송된 모든 페이로드를 수신하기 시작합니다. 웹후크를 구성할 때 비밀을 설정한 경우, Roblox는 각 웹후크 알림에 roblox-signature를 전송하여 요청이 실제로 Roblox에서 온 것임을 보장합니다. 서명은 사용자 지정 엔드포인트의 페이로드 헤더에 있으며, 타사 서버의 바닥글에 있습니다.
t=<timestamp>,v1=<signature>웹후크에 대한 비밀을 설정하지 않은 경우, 서명은 알림이 전송된 시간의 타임스탬프만 포함합니다:
t=<timestamp>서명을 확인하려면:
타임스탬프 및 서명 값을 추출합니다. 비밀이 있는 웹후크의 모든 서명은 다음 두 값이 접두사와 함께 CSV 문자열 형식으로 공유됩니다:
- t: 알림이 전송된 시간의 타임스탬프.
- v1: Creator Dashboard 구성에서 제공한 비밀을 사용하여 생성된 서명 값.
roblox-signature의 기본 문자열을 재생성합니다:
- 타임스탬프를 문자열로.
- 마침표 문자 ..
- 요청 본문의 JSON 문자열.
구성 중에 정의한 비밀을 키로 사용하여 SHA256 해시 함수를 사용하여 해시 기반 메시지 인증 코드(HMAC)를 계산합니다. 2단계에서 생성한 기본 문자열을 메시지로 사용합니다. 결과를 Base64 형식으로 변환하여 예상 서명을 얻습니다.
추출한 서명 값을 예상 서명과 비교합니다. 서명을 올바르게 생성했다면 값이 동일해야 합니다.
- 선택 사항재전송 공격을 방지하기 위해, 공격자가 데이터를 가로채고 재전송하여 무단 액세스 또는 악의적인 작업을 수행하는 사이버 공격의 일종인 재전송 공격을 방지하기 위해, 추출한 타임스탬프 값을 현재 타임스탬프와 비교하고 합리적인 시간 제한 내에 있는지 확인하는 것이 좋습니다. 예를 들어, 10분의 시간 창이 일반적으로 합리적인 시간 제한입니다.
페이로드 스키마
웹후크의 대상 이벤트가 트리거되면, 요청이 웹후크 URL로 전송되며, 페이로드에 이벤트에 대한 정보가 포함됩니다. 모든 요청의 페이로드는 고정 및 가변 필드로 구성된 동일한 스키마를 공유합니다. 이는 페이로드에 전송된 데이터가 구조화되고 일관되게 유지되어 수신 애플리케이션이 데이터를 처리하고 사용할 수 있도록 합니다.
고정 페이로드 스키마 필드는 모든 웹후크 요청 간의 일관성을 유지하는 데 도움이 되며, 다음 필드가 제공됩니다:
- NotificationId (string): 전송된 각 알림에 대한 고유 식별자. 동일한 NotificationId가 두 번 수신되면 중복으로 간주됩니다.
- EventType (string): 알림이 트리거된 이벤트의 유형을 나타냅니다.
- EventTime (string): 이벤트가 트리거된 시간의 타임스탬프입니다.
가변 페이로드 스키마 필드는 웹후크가 다양한 유형의 이벤트를 수용할 수 있도록 유연성을 제공합니다. 여기에는:
- EventPayload (object): 웹후크를 트리거한 EventType에 대한 특정 정보를 포함합니다. EventPayload 스키마의 구조는 이벤트 유형에 따라 다릅니다.
다음 예시는 삭제 권리 요청 이벤트의 페이로드 스키마를 보여줍니다:
{
"NotificationId": "string",
"EventType": "RightToErasureRequest",
"EventTime": "2023-12-30T16:24:24.2118874Z",
"EventPayload": {
"UserId": 1,
"GameIds": [
1234, 2345
]
}
}알림 처리
사용자의 **개인 식별 정보(PII)**를 저장하는 경우, 예를 들어 사용자 ID와 같은 경우, 법적 의무에 비추어 요청을 평가해야 합니다. 자세한 내용은 RTBF 및 제작자에서 확인할 수 있습니다. 웹후크 알림을 처리하고 PII 삭제를 자동화하는 봇을 생성할 수 있으며, PII를 데이터 저장소에 저장하는 경우 가능합니다. 삭제 권리 요청 자동화에서 Discord 내에서 PII 데이터를 삭제하는 봇을 만드는 방법에 대한 예시를 확인할 수 있습니다. 이 예시는 구독 이벤트와 같은 다른 알림을 처리하는 데에도 적용할 수 있습니다.
타사 도구 대신 사용자 지정 엔드포인트를 웹후크 서버로 사용하는 경우, 웹후크 페이로드에서 삭제할 데이터 를 추출하고 자체 자동화 솔루션을 구축할 수 있습니다. 다음 코드 샘플은 타임스탬프를 확인하고 요청이 Roblox에서 오는지 확인하여 재전송 공격을 방지하는 서버의 예입니다:
const crypto = require('crypto');
const express = require('express');
const secret = '<your_secret>' // 이 값은 환경 변수로 설정할 수 있습니다.
let app = express();
app.use(express.json());
app.all('/*', function (req, res) {
console.log('새 요청 수신');
// 헤더에서 타임스탬프 및 서명 추출
const signatureHeader = req.headers['roblox-signature'].split(',');
const timestamp = signatureHeader.find(e => e.startsWith('t=')).substring(2);
const signature = signatureHeader.find(e => e.startsWith('v1=')).substring(3);
// 재전송 공격을 방지하기 위해 요청이 300초 이내에 수신되었는지 확인
const requestTimestampMs = timestamp * 1000;
const windowTimeMs = 300 * 1000;
const oldestTimestampAllowed = Date.now() - windowTimeMs;
if (requestTimestampMs < oldestTimestampAllowed) {
return res.status(403).send('만료된 요청');
}
// 서명 검증
const message = `${timestamp}.${JSON.stringify(req.body)}`;
const hmac = crypto.createHmac('sha256', secret);
const calculatedSignature = hmac.update(message).digest('base64');
if (signature !== calculatedSignature) {
return res.status(401).send('인증되지 않은 요청');
}
// 페이로드 처리 로직
const payloadBody = req.body;
const eventType = payloadBody['EventType'];
if (eventType === 'RightToErasureRequest'){
const userId = payloadBody['EventPayload']['UserId'];
const gameIds = payloadBody['EventPayload']['GameIds'];
console.log(`페이로드 데이터: UserId=${userId} 및 GameIds=${gameIds}`);
// 데이터 저장소에 PII를 저장하는 경우, UserId 및 GameIds를 사용하여 데이터 저장소에서 정보를 삭제합니다.
}
return res.json({ message: '메시지를 성공적으로 처리했습니다.' });
});
app.listen(8080, function () {
console.log('서버 시작됨');
});