라이브러리를 사용하여 푸시 메시지를 트리거하는 방법을 알아봤습니다. 하지만 이러한 라이브러리는 정확히 무엇을 하고 있나요?
이러한 요청이 올바른 형식인지 확인하면서 네트워크 요청을 하고 있습니다. 이 네트워크 요청을 정의하는 사양은 웹 푸시 프로토콜입니다.
이 섹션에서는 서버가 애플리케이션 서버 키로 자체를 식별하는 방법과 암호화된 페이로드 및 연결된 데이터가 전송되는 방법을 설명합니다.
웹 푸시의 예쁜 부분은 아니며 암호화 전문가도 아니지만, 이러한 라이브러리가 내부적으로 무엇을 하는지 알아두면 유용하므로 각 부분을 살펴보겠습니다.
애플리케이션 서버 키
사용자를 구독할 때 applicationServerKey를 전달합니다. 이 키는 푸시 서비스에 전달되며 사용자를 구독한 애플리케이션이 푸시 메시지를 트리거하는 애플리케이션인지 확인하는 데 사용됩니다.
푸시 메시지를 트리거하면 푸시 서비스가 애플리케이션을 인증할 수 있는 헤더 집합이 전송됩니다. (이는 VAPID 사양에 의해 정의됩니다.)
이 모든 것이 실제로 무엇을 의미하며 정확히 어떤 일이 일어날까요? 다음은 애플리케이션 서버 인증을 위해 취해진 단계입니다.
- 애플리케이션 서버는 비공개 애플리케이션 키로 일부 JSON 정보에 서명합니다.
- 이 서명된 정보는 POST 요청의 헤더로 푸시 서비스에 전송됩니다.
- 푸시 서비스는
pushManager.subscribe()에서 수신한 저장된 공개 키를 사용하여 수신된 정보가 공개 키와 관련된 비공개 키로 서명되었는지 확인합니다. 참고: 공개 키는 구독 호출에 전달된applicationServerKey입니다. - 서명된 정보가 유효하면 푸시 서비스가 사용자에게 푸시 메시지를 전송합니다.
정보 흐름의 예는 아래와 같습니다. (왼쪽 하단의 범례를 참고하여 공개 키와 비공개 키를 확인하세요.)
요청의 헤더에 추가된 '서명된 정보'는 JSON 웹 토큰입니다.
JSON 웹 토큰
JSON 웹 토큰 (JWT)은 수신자가 보낸 사람을 확인할 수 있도록 서드 파티에 메시지를 전송하는 방법입니다.
서드 파티가 메시지를 수신하면 발신자의 공개 키를 가져와 이를 사용하여 JWT의 서명을 검증해야 합니다. 서명이 유효하면 JWT가 일치하는 비공개 키로 서명되었으므로 예상 발신자로부터 온 것이어야 합니다.
jwt.io/에는 서명을 수행할 수 있는 다양한 라이브러리가 있으며, 가능한 경우 이를 사용하는 것이 좋습니다. 완전성을 위해 서명된 JWT를 수동으로 만드는 방법을 살펴보겠습니다.
웹 푸시 및 서명된 JWT
서명된 JWT는 문자열일 뿐이지만 점으로 연결된 세 개의 문자열로 생각할 수 있습니다.
첫 번째와 두 번째 문자열 (JWT 정보 및 JWT 데이터)은 base64로 인코딩된 JSON 조각으로, 공개적으로 읽을 수 있습니다.
첫 번째 문자열은 JWT 자체에 관한 정보로, 서명을 만드는 데 사용된 알고리즘을 나타냅니다.
웹 푸시의 JWT 정보에는 다음 정보가 포함되어야 합니다.
{
"typ": "JWT",
"alg": "ES256"
}
두 번째 문자열은 JWT 데이터입니다. JWT의 발신자, JWT의 대상, JWT의 유효 기간에 관한 정보를 제공합니다.
웹 푸시의 경우 데이터 형식은 다음과 같습니다.
{
"aud": "https://some-push-service.org",
"exp": "1469618703",
"sub": "mailto:example@web-push-book.org"
}
aud 값은 '대상', 즉 JWT가 누구를 위한 것인지 나타냅니다. 웹 푸시의 경우 잠재고객은 푸시 서비스이므로 푸시 서비스의 출처로 설정합니다.
exp 값은 JWT의 만료입니다. 이를 통해 스누퍼가 JWT를 가로채는 경우 재사용할 수 없습니다. 만료는 초 단위의 타임스탬프이며 24시간을 초과해서는 안 됩니다.
Node.js에서는 다음을 사용하여 만료를 설정합니다.
Math.floor(Date.now() / 1000) + 12 * 60 * 60;
전송 애플리케이션과 푸시 서비스 간의 시계 차이로 인한 문제를 방지하기 위해 24시간이 아닌 12시간입니다.
마지막으로 sub 값은 URL 또는 mailto 이메일 주소여야 합니다.
이는 푸시 서비스가 발신자에게 연락해야 하는 경우 JWT에서 연락처 정보를 찾을 수 있도록 하기 위함입니다. (웹 푸시 라이브러리에 이메일 주소가 필요한 이유입니다.)
JWT 정보와 마찬가지로 JWT 데이터는 URL 안전 base64 문자열로 인코딩됩니다.
세 번째 문자열인 서명은 처음 두 문자열(JWT 정보 및 JWT 데이터)을 가져와 점 문자로 연결한 결과입니다. 이를 '서명되지 않은 토큰'이라고 하며 서명됩니다.
서명 프로세스에서는 ES256을 사용하여 '서명되지 않은 토큰'을 암호화해야 합니다. JWT 사양에 따르면 ES256은 'P-256 곡선과 SHA-256 해시 알고리즘을 사용하는 ECDSA'의 약자입니다. 웹 암호화를 사용하면 다음과 같이 서명을 만들 수 있습니다.
// Utility function for UTF-8 encoding a string to an ArrayBuffer.
const utf8Encoder = new TextEncoder('utf-8');
// The unsigned token is the concatenation of the URL-safe base64 encoded
// header and body.
const unsignedToken = .....;
// Sign the |unsignedToken| using ES256 (SHA-256 over ECDSA).
const key = {
kty: 'EC',
crv: 'P-256',
x: window.uint8ArrayToBase64Url(
applicationServerKeys.publicKey.subarray(1, 33)),
y: window.uint8ArrayToBase64Url(
applicationServerKeys.publicKey.subarray(33, 65)),
d: window.uint8ArrayToBase64Url(applicationServerKeys.privateKey),
};
// Sign the |unsignedToken| with the server's private key to generate
// the signature.
return crypto.subtle.importKey('jwk', key, {
name: 'ECDSA', namedCurve: 'P-256',
}, true, ['sign'])
.then((key) => {
return crypto.subtle.sign({
name: 'ECDSA',
hash: {
name: 'SHA-256',
},
}, key, utf8Encoder.encode(unsignedToken));
})
.then((signature) => {
console.log('Signature: ', signature);
});
푸시 서비스는 공개 애플리케이션 서버 키를 사용하여 JWT를 검증하여 서명을 복호화하고 복호화된 문자열이 '서명되지 않은 토큰' (즉, JWT의 처음 두 문자열)과 동일한지 확인할 수 있습니다.
서명된 JWT (즉, 점으로 연결된 세 문자열 모두)는 다음과 같이 WebPush이 앞에 추가된 Authorization 헤더로 웹 푸시 서비스에 전송됩니다.
Authorization: 'WebPush [JWT Info].[JWT Data].[Signature]';
웹 푸시 프로토콜에서는 공개 애플리케이션 서버 키를 Crypto-Key 헤더에 p256ecdsa=이 앞에 붙은 URL 안전 base64 인코딩 문자열로 전송해야 한다고도 명시합니다.
Crypto-Key: p256ecdsa=[URL Safe Base64 Public Application Server Key]
페이로드 암호화
다음으로 웹 앱이 푸시 메시지를 수신할 때 수신된 데이터에 액세스할 수 있도록 푸시 메시지와 함께 페이로드를 전송하는 방법을 살펴보겠습니다.
다른 푸시 서비스를 사용해 본 적이 있는 사용자가 흔히 묻는 질문은 웹 푸시 페이로드를 암호화해야 하는 이유입니다. 네이티브 앱의 경우 푸시 메시지가 데이터를 일반 텍스트로 전송할 수 있습니다.
웹 푸시의 장점 중 하나는 모든 푸시 서비스가 동일한 API (웹 푸시 프로토콜)를 사용하므로 개발자가 푸시 서비스가 누구인지 신경 쓰지 않아도 된다는 것입니다. 올바른 형식으로 요청하면 푸시 메시지가 전송될 것으로 예상할 수 있습니다. 이 방법의 단점은 개발자가 신뢰할 수 없는 푸시 서비스에 메시지를 보낼 수 있다는 것입니다. 페이로드를 암호화하면 푸시 서비스가 전송된 데이터를 읽을 수 없습니다. 브라우저만 정보를 복호화할 수 있습니다. 이렇게 하면 사용자의 데이터가 보호됩니다.
페이로드의 암호화는 메시지 암호화 사양에 정의되어 있습니다.
푸시 메시지 페이로드를 암호화하는 구체적인 단계를 살펴보기 전에 암호화 과정에서 사용되는 몇 가지 기법을 다뤄야 합니다. (푸시 암호화에 관한 훌륭한 기사를 작성해 주신 Mat Scales님께 감사드립니다.)
ECDH 및 HKDF
ECDH와 HKDF는 모두 암호화 프로세스 전반에 사용되며 정보 암호화라는 목적에 이점을 제공합니다.
ECDH: 타원 곡선 디피-헬만 키 교환
정보를 공유하려는 두 사람, 앨리스와 밥이 있다고 가정해 보겠습니다. 앨리스와 밥은 각각 공개 키와 비공개 키를 가지고 있습니다. 앨리스와 밥은 서로의 공개 키를 공유합니다.
ECDH로 생성된 키의 유용한 속성은 Alice가 자신의 비공개 키와 Bob의 공개 키를 사용하여 비밀 값 'X'를 만들 수 있다는 것입니다. 밥도 마찬가지로 비공개 키와 앨리스의 공개 키를 사용하여 동일한 값 'X'를 독립적으로 만들 수 있습니다. 이렇게 하면 'X'가 공유 비밀이 되고 앨리스와 밥은 공개 키만 공유하면 됩니다. 이제 밥과 앨리스는 'X'를 사용하여 서로 간에 메시지를 암호화하고 복호화할 수 있습니다.
제가 알기로는 ECDH는 공유 보안 비밀 'X'를 만드는 이 '기능'을 허용하는 곡선의 속성을 정의합니다.
이는 ECDH에 관한 대략적인 설명입니다. 자세히 알아보려면 더 자세한 ECDH 개요 동영상을 참고하세요.
코드 측면에서 대부분의 언어 / 플랫폼에는 이러한 키를 쉽게 생성할 수 있는 라이브러리가 함께 제공됩니다.
노드에서는 다음을 수행합니다.
const keyCurve = crypto.createECDH('prime256v1');
keyCurve.generateKeys();
const publicKey = keyCurve.getPublicKey();
const privateKey = keyCurve.getPrivateKey();
HKDF: HMAC 기반 키 파생 함수
Wikipedia에는 HKDF에 대한 간결한 설명이 있습니다.
HKDF는 약한 키 자료를 암호화된 강력한 키 자료로 변환하는 HMAC 기반 키 파생 함수입니다. 예를 들어 디피-헬만 교환 공유 보안 비밀을 암호화, 무결성 확인 또는 인증에 사용하기에 적합한 키 자료로 변환하는 데 사용할 수 있습니다.
기본적으로 HKDF는 특별히 안전하지 않은 입력을 가져와 더 안전하게 만듭니다.
이 암호화를 정의하는 사양에서는 SHA-256을 해시 알고리즘으로 사용해야 하며 웹 푸시의 HKDF 결과 키는 256비트(32바이트)를 초과해서는 안 됩니다.
노드에서는 다음과 같이 구현할 수 있습니다.
// Simplified HKDF, returning keys up to 32 bytes long
function hkdf(salt, ikm, info, length) {
// Extract
const keyHmac = crypto.createHmac('sha256', salt);
keyHmac.update(ikm);
const key = keyHmac.digest();
// Expand
const infoHmac = crypto.createHmac('sha256', key);
infoHmac.update(info);
// A one byte long buffer containing only 0x01
const ONE_BUFFER = new Buffer(1).fill(1);
infoHmac.update(ONE_BUFFER);
return infoHmac.digest().slice(0, length);
}
이 예시 코드에 관한 Mat Scale의 도움말에 감사드립니다.
ECDH는 공개 키를 공유하고 공유 비밀번호를 생성하는 안전한 방법입니다. HKDF는 안전하지 않은 자료를 가져와 안전하게 만드는 방법입니다.
이는 페이로드 암호화 중에 사용됩니다. 다음으로 입력으로 사용하는 항목과 암호화 방법을 살펴보겠습니다.
입력
페이로드가 있는 푸시 메시지를 사용자에게 보내려면 다음 세 가지 입력이 필요합니다.
- 페이로드 자체입니다.
PushSubscription의auth보안 비밀입니다.PushSubscription의p256dh키
PushSubscription에서 auth 및 p256dh 값이 검색되는 것을 확인했지만, 구독을 고려할 때 다음 값이 필요합니다.
subscription.toJSON().keys.auth;
subscription.toJSON().keys.p256dh;
subscription.getKey('auth');
subscription.getKey('p256dh');
auth 값은 비밀로 취급되어야 하며 애플리케이션 외부로 공유해서는 안 됩니다.
p256dh 키는 공개 키이며, 클라이언트 공개 키라고도 합니다. 여기서는 p256dh를 정기 결제 공개 키라고 합니다. 정기 결제 공개 키는 브라우저에서 생성됩니다. 브라우저는 비공개 키를 비밀로 유지하고 페이로드를 복호화하는 데 사용합니다.
이 세 값(auth, p256dh, payload)은 입력으로 필요하며 암호화 프로세스의 결과는 암호화된 페이로드, 솔트 값, 데이터를 암호화하는 데만 사용되는 공개 키입니다.
Salt
솔트는 16바이트의 무작위 데이터여야 합니다. NodeJS에서는 다음과 같이 솔트를 만듭니다.
const salt = crypto.randomBytes(16);
공개 키 / 비공개 키
공개 키와 비공개 키는 P-256 타원 곡선을 사용하여 생성해야 합니다. Node에서는 다음과 같이 합니다.
const localKeysCurve = crypto.createECDH('prime256v1');
localKeysCurve.generateKeys();
const localPublicKey = localKeysCurve.getPublicKey();
const localPrivateKey = localKeysCurve.getPrivateKey();
이러한 키를 '로컬 키'라고 합니다. 암호화에만 사용되며 애플리케이션 서버 키와는 아무런 관련이 없습니다.
페이로드, 인증 비밀번호, 정기 결제 공개 키를 입력으로 사용하고 새로 생성된 솔트와 로컬 키 집합을 사용하여 실제로 암호화를 실행할 준비가 되었습니다.
공유된 보안 비밀
첫 번째 단계는 정기 결제 공개 키와 새로운 비공개 키를 사용하여 공유 비밀번호를 만드는 것입니다 (앨리스와 밥의 ECDH 설명을 기억하시나요?). 바로 그거예요.)
const sharedSecret = localKeysCurve.computeSecret(
subscription.keys.p256dh,
'base64',
);
이는 다음 단계에서 의사 난수 키 (PRK)를 계산하는 데 사용됩니다.
의사 난수 키
의사 난수 키 (PRK)는 푸시 구독의 인증 비밀번호와 방금 만든 공유 비밀번호의 조합입니다.
const authEncBuff = new Buffer('Content-Encoding: auth\0', 'utf8');
const prk = hkdf(subscription.keys.auth, sharedSecret, authEncBuff, 32);
Content-Encoding: auth\0 문자열이 무엇인지 궁금하실 수 있습니다.
간단히 말해 브라우저가 받은 메시지를 복호화하고 예상되는 콘텐츠 인코딩을 찾을 수 있지만 명확한 목적이 없습니다.
\0는 값이 0인 바이트를 버퍼 끝에 추가합니다. 이는 메시지를 복호화하는 브라우저에서 예상하는 것으로, 콘텐츠 인코딩에 대해 많은 바이트가 예상되고 그 뒤에 값 0이 있는 바이트가 오고 암호화된 데이터가 옵니다.
의사 난수 키는 인증, 공유 비밀번호, 인코딩 정보를 HKDF를 통해 실행하는 것 (즉, 암호화 강도를 높이는 것)입니다.
컨텍스트
'컨텍스트'는 나중에 암호화 브라우저에서 두 값을 계산하는 데 사용되는 바이트 집합입니다. 기본적으로 정기 결제 공개 키와 로컬 공개 키가 포함된 바이트 배열입니다.
const keyLabel = new Buffer('P-256\0', 'utf8');
// Convert subscription public key into a buffer.
const subscriptionPubKey = new Buffer(subscription.keys.p256dh, 'base64');
const subscriptionPubKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = subscriptionPubKey.length;
const localPublicKeyLength = new Uint8Array(2);
subscriptionPubKeyLength[0] = 0;
subscriptionPubKeyLength[1] = localPublicKey.length;
const contextBuffer = Buffer.concat([
keyLabel,
subscriptionPubKeyLength.buffer,
subscriptionPubKey,
localPublicKeyLength.buffer,
localPublicKey,
]);
최종 컨텍스트 버퍼는 라벨, 구독 공개 키의 바이트 수, 키 자체, 로컬 공개 키의 바이트 수, 키 자체입니다.
이 컨텍스트 값을 사용하면 nonce와 콘텐츠 암호화 키(CEK)를 만들 수 있습니다.
콘텐츠 암호화 키 및 nonce
nonce는 한 번만 사용해야 하므로 재생 공격을 방지하는 값입니다.
콘텐츠 암호화 키 (CEK)는 궁극적으로 페이로드를 암호화하는 데 사용되는 키입니다.
먼저 nonce와 CEK의 데이터 바이트를 만들어야 합니다. 이는 콘텐츠 인코딩 문자열과 방금 계산한 컨텍스트 버퍼로 구성됩니다.
const nonceEncBuffer = new Buffer('Content-Encoding: nonce\0', 'utf8');
const nonceInfo = Buffer.concat([nonceEncBuffer, contextBuffer]);
const cekEncBuffer = new Buffer('Content-Encoding: aesgcm\0');
const cekInfo = Buffer.concat([cekEncBuffer, contextBuffer]);
이 정보는 솔트와 PRK를 nonceInfo 및 cekInfo와 결합하는 HKDF를 통해 실행됩니다.
// The nonce should be 12 bytes long
const nonce = hkdf(salt, prk, nonceInfo, 12);
// The CEK should be 16 bytes long
const contentEncryptionKey = hkdf(salt, prk, cekInfo, 16);
이렇게 하면 nonce와 콘텐츠 암호화 키가 제공됩니다.
암호화 실행
이제 콘텐츠 암호화 키가 있으므로 페이로드를 암호화할 수 있습니다.
콘텐츠 암호화 키를 키로 사용하고 nonce를 초기화 벡터로 사용하여 AES128 암호를 만듭니다.
Node에서는 다음과 같이 실행됩니다.
const cipher = crypto.createCipheriv(
'id-aes128-GCM',
contentEncryptionKey,
nonce,
);
페이로드를 암호화하기 전에 페이로드 앞에 추가할 패딩의 양을 정의해야 합니다. 패딩을 추가하는 이유는 도청자가 페이로드 크기를 기반으로 메시지의 '유형'을 파악할 수 있는 위험을 방지하기 위해서입니다.
추가 패딩의 길이를 나타내기 위해 2바이트의 패딩을 추가해야 합니다.
예를 들어 패딩을 추가하지 않은 경우 값 0이 있는 바이트가 두 개 있습니다.즉, 패딩이 없습니다. 이 두 바이트 뒤에 페이로드를 읽게 됩니다. 패딩을 5바이트 추가한 경우 처음 두 바이트의 값은 5가 되므로 소비자는 추가로 5바이트를 읽은 다음 페이로드를 읽기 시작합니다.
const padding = new Buffer(2 + paddingLength);
// The buffer must be only zeros, except the length
padding.fill(0);
padding.writeUInt16BE(paddingLength, 0);
그런 다음 패딩과 페이로드를 이 암호화 알고리즘을 통해 실행합니다.
const result = cipher.update(Buffer.concat(padding, payload));
cipher.final();
// Append the auth tag to the result -
// https://nodejs.org/api/crypto.html#crypto_cipher_getauthtag
const encryptedPayload = Buffer.concat([result, cipher.getAuthTag()]);
이제 암호화된 페이로드가 있습니다. 와!
이제 이 페이로드를 푸시 서비스로 전송하는 방법을 결정해야 합니다.
암호화된 페이로드 헤더 및 본문
이 암호화된 페이로드를 푸시 서비스로 전송하려면 POST 요청에서 몇 가지 다른 헤더를 정의해야 합니다.
암호화 헤더
'Encryption' 헤더에는 페이로드를 암호화하는 데 사용된 솔트가 포함되어야 합니다.
16바이트 솔트는 base64 URL 안전 인코딩되어 다음과 같이 암호화 헤더에 추가되어야 합니다.
Encryption: salt=[URL Safe Base64 Encoded Salt]
암호화 키 헤더
'애플리케이션 서버 키' 섹션에서 Crypto-Key 헤더가 공개 애플리케이션 서버 키를 포함하는 데 사용되는 것을 확인했습니다.
이 헤더는 페이로드를 암호화하는 데 사용되는 로컬 공개 키를 공유하는 데도 사용됩니다.
결과 헤더는 다음과 같습니다.
Crypto-Key: dh=[URL Safe Base64 Encoded Local Public Key String]; p256ecdsa=[URL Safe Base64 Encoded Public Application Server Key]
콘텐츠 유형, 길이, 인코딩 헤더
Content-Length 헤더는 암호화된 페이로드의 바이트 수입니다. 'Content-Type' 및 'Content-Encoding' 헤더는 고정된 값입니다.
아래에 표시됩니다.
Content-Length: [Number of Bytes in Encrypted Payload]
Content-Type: 'application/octet-stream'
Content-Encoding: 'aesgcm'
이러한 헤더가 설정되면 암호화된 페이로드를 요청의 본문으로 전송해야 합니다. Content-Type가 application/octet-stream로 설정되어 있습니다. 암호화된 페이로드는 바이트 스트림으로 전송되어야 하기 때문입니다.
NodeJS에서는 다음과 같이 합니다.
const pushRequest = https.request(httpsOptions, function(pushResponse) {
pushRequest.write(encryptedPayload);
pushRequest.end();
헤더를 더 추가하시겠어요?
JWT / 애플리케이션 서버 키에 사용되는 헤더 (즉, 푸시 서비스로 애플리케이션을 식별하는 방법)와 암호화된 페이로드를 전송하는 데 사용되는 헤더를 다루었습니다.
푸시 서비스가 전송된 메시지의 동작을 변경하는 데 사용하는 추가 헤더가 있습니다. 이러한 헤더 중 일부는 필수이고 일부는 선택사항입니다.
TTL 헤더
필수
TTL(또는 수명)는 푸시 메시지가 전송되기 전에 푸시 서비스에서 유지되기를 원하는 시간(초)을 지정하는 정수입니다. TTL가 만료되면 메시지가 푸시 서비스 대기열에서 삭제되고 전송되지 않습니다.
TTL: [Time to live in seconds]
TTL을 0으로 설정하면 푸시 서비스가 메시지를 즉시 전송하려고 시도하지만 기기에 연결할 수 없는 경우 메시지가 푸시 서비스 대기열에서 즉시 삭제됩니다.
기술적으로 푸시 서비스는 원하는 경우 푸시 메시지의 TTL을 줄일 수 있습니다. 푸시 서비스의 응답에서 TTL 헤더를 검사하여 이러한 상황이 발생했는지 확인할 수 있습니다.
주제
선택사항
주제는 일치하는 주제 이름이 있는 경우 대기 중인 메시지를 새 메시지로 대체하는 데 사용할 수 있는 문자열입니다.
이는 기기가 오프라인 상태일 때 여러 메시지가 전송되고 기기가 켜졌을 때 사용자에게 최신 메시지만 표시되기를 원하는 시나리오에서 유용합니다.
긴급
선택사항
긴급성은 푸시 서비스에 메시지가 사용자에게 얼마나 중요한지 나타냅니다. 이는 푸시 서비스가 배터리가 부족할 때 중요한 메시지에 대해서만 절전 모드를 해제하여 사용자의 기기의 배터리 수명을 절약하는 데 사용할 수 있습니다.
헤더 값은 아래와 같이 정의됩니다. 기본값은 normal입니다.
Urgency: [very-low | low | normal | high]
모든 것을 한곳에서
이 모든 기능의 작동 방식에 관해 더 궁금한 점이 있으면 언제든지 web-push-libs org에서 라이브러리가 푸시 메시지를 트리거하는 방법을 확인하세요.
암호화된 페이로드와 위의 헤더가 있으면 PushSubscription의 endpoint에 POST 요청을 하면 됩니다.
그렇다면 이 POST 요청에 대한 응답은 어떻게 해야 할까요?
푸시 서비스의 응답
푸시 서비스에 요청을 보낸 후에는 응답의 상태 코드를 확인해야 합니다. 상태 코드를 통해 요청이 성공했는지 여부를 알 수 있습니다.
| 상태 코드 | 설명 |
|---|---|
| 201 | 생성됨 푸시 메시지 전송 요청이 수신되어 수락되었습니다. |
| 429 | 요청 횟수가 너무 많습니다. 애플리케이션 서버가 푸시 서비스의 비율 제한에 도달했음을 의미합니다. 푸시 서비스는 다른 요청을 할 수 있기까지의 시간을 나타내는 'Retry-After' 헤더를 포함해야 합니다. |
| 400 | 요청이 잘못되었습니다. 일반적으로 이는 헤더 중 하나가 유효하지 않거나 형식이 잘못되었음을 의미합니다. |
| 404 | 찾을 수 없음 이는 정기 결제가 만료되어 사용할 수 없음을 나타냅니다. 이 경우 `PushSubscription`을 삭제하고 클라이언트가 사용자를 다시 구독할 때까지 기다려야 합니다. |
| 410 | 사라짐 정기 결제가 더 이상 유효하지 않으므로 애플리케이션 서버에서 삭제해야 합니다. 이는 `PushSubscription`에서 `unsubscribe()`를 호출하여 재현할 수 있습니다. |
| 413 | 페이로드 크기가 너무 큽니다. 푸시 서비스가 지원해야 하는 최소 크기 페이로드는 4096바이트(또는 4KB)입니다. |
HTTP 상태 코드에 대한 자세한 내용은 웹 푸시 표준 (RFC8030)을 참고하세요.
다음에 수행할 작업
- 웹 푸시 알림 개요
- 푸시 작동 방식
- 사용자 구독
- 권한 UX
- 웹 푸시 라이브러리로 메시지 보내기
- 웹 푸시 프로토콜
- 푸시 이벤트 처리
- 알림 표시
- 알림 동작
- 일반적인 알림 패턴
- 푸시 알림 FAQ
- 일반적인 문제 및 버그 신고