YWS 문서
YWS 콘솔로 이동
앱 연동/알림 서버 (앱 알림 API)

알림 서버 (앱 알림 API)

내 앱(YWS로 로그인하는 앱)에서 사용자의 폰으로 푸시 알림을 보낼 수 있습니다. 알림은 별도의 알림 서버(alarm)가 보냅니다.

주소 누가
https://alarm.jimmy-home.com/v1/notify 어디서든 (앱 서버)
http://alarm/v1/notify YWS 안의 모든 EC2·Lambda (설정 없음, 내부 주소)

아무 사용자에게나 보낼 수 없습니다. 사용자가 직접 허용해야 그 사용자의 notify_token이 생기고, 보낼 때는 그 토큰과 **앱 자신의 인증 정보(client_id / client_secret)**가 둘 다 필요합니다.

흐름

  1. 앱 등록 시 "사용자에게 알림 보내기: 사용" 선택 (OAuth 앱) → 관리자 승인. 이미 등록한 앱은 "수정"으로 바꾸고 관리자가 변경을 승인
  2. 사용자가 그 앱으로 처음 로그인할 때 "알림 허용" 화면이 한 번 나옴 — 허용 / 허용 안 함 (허용 안 해도 로그인은 됨)
  3. 허용한 사용자에 한해, 토큰 교환 응답(POST /oauth/token)과 GET /oauth/userinfo 응답에 "notify_token": "ntf_..."이 들어옴 → 사용자별로 저장
  4. 알림을 보낼 때 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, Deployment yws-alarm(jimmy-master), 자체 DB와 VAPID 키(PVC alarm-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(Secret alarm-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 키를 그대로 옮겨서 이미 알림을 켠 폰은 다시 등록할 필요 없음