push-notifications
Push notification patterns. Web Push, FCM, APNs, notification design, opt-in flows, delivery strategies.
소스 정보
- 저장소
- skeletorflet/opencode-kit
- 최근 소스 활동
- 2026년 4월 3일 15:32
- 감지된 SKILL.md 언어
- 영어
- 스타
- 10
- 포크
- 1
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
SKILL.md 표시 중
SKILL.md
소스 지침 · 읽기 전용 미리보기- name
- push-notifications
- description
- Push notification patterns. Web Push, FCM, APNs, notification design, opt-in flows, delivery strategies.
# Push Notifications
> Notifications that help users are features. Notifications that annoy are bugs.
---
## 1. Platform Overview
| Platform | Service | Library |
|----------|---------|---------|
| **Web** | Web Push API | `web-push` npm |
| **Android** | Firebase Cloud Messaging (FCM) | Firebase SDK |
| **iOS** | Apple Push Notification service (APNs) | Firebase SDK / APNs direct |
| **Unified** | FCM (handles both Android + web) | Firebase Admin SDK |
---
## 2. Web Push Implementation
```ts
// Server: generate VAPID keys (once)
import webpush from "web-push";
const vapidKeys = webpush.generateVAPIDKeys();
// Store PUBLIC_VAPID_KEY and PRIVATE_VAPID_KEY in env
webpush.setVapidDetails(
"mailto:admin@myapp.com",
process.env.PUBLIC_VAPID_KEY!,
process.env.PRIVATE_VAPID_KEY!,
);
// Send notification
async function sendWebPush(subscription: PushSubscription, payload: object) {
await webpush.sendNotification(
subscription,
JSON.stringify(payload),
{ TTL: 60 * 60 * 24 } // 24h TTL
);
}
```
```ts
// Client: Service Worker registration
const registration = await navigator.serviceWorker.register("/sw.js");
async function subscribeToPush() {
const sub = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(PUBLIC_VAPID_KEY),
});
// Send to server
await fetch("/api/push/subscribe", {
method: "POST",
body: JSON.stringify(sub),
headers: { "Content-Type": "application/json" },
});
}
```
```js
// Service Worker (sw.js)
self.addEventListener("push", (event) => {
const data = event.data?.json();
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: "/icon-192.png",
badge: "/badge-72.png",
data: { url: data.url },
actions: [
{ action: "view", title: "View" },
{ action: "dismiss", title: "Dismiss" },
],
})
);
});
self.addEventListener("notificationclick", (event) => {
event.notification.close();
if (event.action === "view") {
clients.openWindow(event.notification.data.url);
}
});
```
---
## 3. FCM (Android + Web)
```ts
import { initializeApp, cert } from "firebase-admin/app";
import { getMessaging } from "firebase-admin/messaging";
initializeApp({ credential: cert(serviceAccount) });
const messaging = getMessaging();
// Single device
await messaging.send({
token: deviceFcmToken,
notification: {
title: "New message",
body: "You have 3 unread messages",
},
data: { url: "/messages", type: "chat" },
android: {
priority: "high",
notification: { channelId: "messages", sound: "default" },
},
apns: {
payload: { aps: { sound: "default", badge: 3 } },
},
});
// Multiple devices (up to 500)
await messaging.sendEachForMulticast({
tokens: deviceTokens,
notification: { title: "...", body: "..." },
});
```
---
## 4. Opt-in Flow Design
```
Best practices:
├── Don\'t request permission immediately on load
├── Show value first: "Get notified when your order ships"
├── Use custom in-app prompt before browser prompt
├── Store preference in DB even if denied
└── Provide easy opt-out in profile/settings
Timing:
├── After user completes meaningful action
├── When notification value is clear
└── NEVER before any engagement
```
---
## 5. Notification Types & Priority
| Type | Priority | When |
|------|---------|------|
| **Transactional** | High | Order shipped, payment failed |
| **Alert** | High | Security event, system down |
| **Social** | Medium | Comment, like, follow |
| **Promotional** | Low | Sale, newsletter |
| **Digest** | Low | Weekly summary |
---
## 6. Delivery Strategy
```
Smart delivery:
├── Quiet hours (don\'t send 2am local time)
├── Frequency cap (max 3/day per user)
├── Dedup (don\'t send same notification twice)
├── Batch similar (1 notification for 10 likes)
└── Respect user preferences per type
Retry on failure:
├── 429 (rate limited) → exponential backoff
├── 410 (token expired) → remove subscription from DB
└── 5xx → retry with backoff, max 3 attempts
```
---
## 7. Database Schema
```prisma
model PushSubscription {
id String @id @default(cuid())
userId String
endpoint String @unique
p256dh String
auth String
platform String // web | android | ios
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id])
}
model NotificationPreference {
userId String @id
orderUpdates Boolean @default(true)
messages Boolean @default(true)
marketing Boolean @default(false)
quietStart Int @default(22) // 10pm
quietEnd Int @default(8) // 8am
}
```
GitHub에서 보기