앱 연동/알림 서버 (앱 알림 API)
알림 서버 (앱 알림 API)
내 앱(YWS로 로그인하는 앱)에서 사용자의 폰으로 푸시 알림을 보낼 수 있습니다. 알림은 별도의 알림 서버(alarm)가 보냅니다.
| 주소 | 누가 |
|---|---|
https://alarm.jimmy-home.com/v1/notify |
어디서든 (앱 서버) |
http://alarm/v1/notify |
YWS 안의 모든 EC2·Lambda (설정 없음, 내부 주소) |
아무 사용자에게나 보낼 수 없습니다. 사용자가 직접 허용해야 그 사용자의 notify_token이 생기고, 보낼 때는 그 토큰과 **앱 자신의 인증 정보(client_id / client_secret)**가 둘 다 필요합니다.
흐름
- 앱 등록 시 "사용자에게 알림 보내기: 사용" 선택 (OAuth 앱) → 관리자 승인. 이미 등록한 앱은 "수정"으로 바꾸고 관리자가 변경을 승인
- 사용자가 그 앱으로 처음 로그인할 때 "알림 허용" 화면이 한 번 나옴 — 허용 / 허용 안 함 (허용 안 해도 로그인은 됨)
- 허용한 사용자에 한해, 토큰 교환 응답(
POST /oauth/token)과GET /oauth/userinfo응답에"notify_token": "ntf_..."이 들어옴 → 사용자별로 저장 - 알림을 보낼 때
POST /v1/notify
POST https://alarm.jimmy-home.com/v1/notify
Authorization: Basic base64(<client_id>:<client_secret>)
Content-Type: application/json
{"notify_token": "ntf_...", "title": "새 메시지", "body": "민수: 오늘 저녁 어때?", "url": "https://chat.jimmy-home.com/rooms/3"}
{"ok": true, "sent": true, "devices": 1, "id": 123}
- 인증 정보는
Authorization: Basic헤더 대신 본문의client_id,client_secret으로 보내도 됨 title필수(100자),body선택(1000자),url선택(https://만, 알림을 누르면 열림)- 사용자 폰에는 제목 앞에 **
[앱 이름]**이 붙어서 표시됨 (다른 앱이나 YWS 시스템 알림인 척할 수 없음) sent: false— 사용자가 알림 설정에서 "앱 알림"을 꺼 둠 (요청 자체는 정상)devices: 0— 사용자가 알림을 켠 기기가 아직 없음 (알림 목록에는 남음)
예제 (Python, 표준 라이브러리만)
import base64, json, urllib.request
def notify(notify_token, title, body="", url=""):
auth = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
req = urllib.request.Request(
"https://alarm.jimmy-home.com/v1/notify", # YWS EC2라면 http://alarm/v1/notify
data=json.dumps({"notify_token": notify_token, "title": title, "body": body, "url": url}).encode(),
headers={"Content-Type": "application/json", "Authorization": f"Basic {auth}"},
)
with urllib.request.urlopen(req, timeout=10) as r:
return json.loads(r.read())
오류
| 코드 | error | 뜻 |
|---|---|---|
| 400 | invalid_request |
JSON이 아니거나 title/body/url 형식 문제 |
| 401 | invalid_client |
client_id/secret이 틀림, 앱이 승인 안 됨, 또는 "알림 보내기"가 승인 안 됨 |
| 403 | invalid_notify_token |
토큰이 없거나 틀림, 다른 앱의 토큰, 또는 사용자가 권한을 해제함 — 저장해 둔 토큰을 지우고, 다음 로그인 때 새로 받기 |
| 429 | rate_limited |
한 사용자에게 1분 10개 / 하루 300개, 앱 전체 1분 300개 초과 |
| 503 | unavailable |
잠시 후 다시 |
사용자 쪽
- 콘솔 알림 → 알림을 허용한 앱: 허용/거부한 앱 목록, 보낸 알림 수. 권한 해제하면 그 토큰은 즉시 무효가 되고, 다음에 그 앱으로 로그인할 때 다시 묻습니다. 거부했던 앱도 여기서 허용으로 바꿀 수 있음
- 알림 설정 → 앱 알림을 끄면 모든 앱의 알림을 한 번에 끔
- 앱을 삭제하거나 관리자가 승인을 회수하거나 "알림 보내기"를 끄면, 모든 사용자의 토큰이 무효가 됨
구조
- 알림 서버는 포털과 별도 서버: 네임스페이스
alarm, Deploymentyws-alarm(jimmy-master), 자체 DB와 VAPID 키(PVCalarm-data). 소스는 저장소의alarm/, 매니페스트는alarm/k8s/alarm.yaml - 포털은 알림 서버의
/internal/*API를 쓰는 클라이언트 (app/notifications.py) — EC2·메일·Lambda·관리자 알림도 전부 알림 서버를 거쳐서 나감. 기기 등록·알림 목록·설정도 알림 서버에 저장되고, 포털 화면은 그걸 보여줄 뿐 - 앱 인증은 포털이 판단 (OAuth의 주인): 알림 서버가 포털의
/internal/alarm/verify-client에 client_id/secret을 물어봄 (결과 5분 캐시) - 서로 간 호출은 공유 비밀
ALARM_INTERNAL_TOKEN(Secretalarm-internal/yws-secrets)으로 인증 - 네트워크: 알림 서버로 들어오는 건 포털(내부 API), Traefik(공개 주소),
alarm에 연결한 EC2만.alarm.jimmy-home.com은/와/v1/*만 라우팅 —/internal/*은 외부에서 아예 안 보임 - Cloudflare:
alarm.jimmy-home.com의 API 경로는 브라우저 무결성 검사에서 제외 (파이썬 기본urllib처럼 서버에서 부르는 요청이 막히지 않게 — OAuth의/oauth/token,/oauth/userinfo와 같은 처리) - 2026-09-29 포털에서 분리: 기존 기기 등록, 알림 기록, 설정과 VAPID 키를 그대로 옮겨서 이미 알림을 켠 폰은 다시 등록할 필요 없음