π μμνλ©΄μ
μΉ νΈμ μλ¦Ό(Web Push Notification)μ΄λ, λ§ κ·Έλλ‘ λΈλΌμ°μ νκ²½μμ νΈμ μλ¦Όμ λ°μ μ μλ κΈ°μ μ μλ―Ένλ€.
νΈμ μλ¦Όμ΄λΌκ³ νλ©΄ λ€μ΄ν°λΈ μ±μ μ μ λ¬Όμ΄λΌκ³ λλ μ μμ§λ§, μΉ νΈμ κΈ°μ μ ν΅ν΄ μΉμμλ νΈμ μλ¦Ό κΈ°λ₯μ ꡬννκ³ μ¬μ©ν μ μλ€.
λΈλΌμ°μ μ ꡬνλμ΄μλ Push APIμ μμΈν μ€νμ μλ λ¬Έμμμ νμΈν΄λ³Ό μ μλ€.
λ³Έ ν¬μ€ν
μμλ μΉ νΈμκ° λμνλ λ©μ»€λμ¦μ λν΄ μ‘°κΈ μΈλΆμ μΌλ‘ μ΄ν΄λ³΄κ³ , μ§μ λ°λͺ¨ μ½λλ₯Ό μ΄ν΄λ³΄λ©° μΉ νΈμμ λν μ λ°μ μΈ μ΄ν΄λ₯Ό ν μ μλλ‘ μ§νν μμ μ΄λ€.
λΆλμ μ‘°μ νκΈ° μν΄ λͺ¨λ API κΈ°λ₯μ λν΄ μ€λͺ
νμ§ μμΌλ μμΈν λ΄μ©μ κΈ μ€κ°μ€κ°μ μΆκ°ν΄λ μ°Έμ‘° λ§ν¬(λ¬Έμ)λ₯Ό μ°Έκ³ νκΈΈ λ°λλ€.
λ³Έ ν¬μ€ν
μμ λ€λ£¨λ λλΆλΆμ λ΄μ©μ κ³Όκ±°μ μΆκ°νλ μ μμ λ΄μ©κ³Ό λμΌνλ©°, PWAμ κ΄μ¬μ΄ μλ€λ©΄ ν λ² μ΄ν΄λ³΄λ©΄ μ’μ κ² κ°λ€.
[PWA] SNS μ± μμ λ‘ λ°°μ°λ νλ‘κ·Έλ μλΈ μΉ μ± μΆκ°!
μλ νμΈμ~!! μ΄λ²μ νλ‘κ·Έλ μλΈ μΉ μ±(PWA)μ μ£Όμ λ‘ ν "SNS μ± μμ λ‘ λ°°μ°λ νλ‘κ·Έλ μλΈ μΉ μ±" μ μΆκ°νκ² λμ΄ κ°λ΅ν μκ°νλ €κ³ ν©λλ€! π λ³Έ λΈλ‘κ·Έμλ νλ‘κ·Έλ μλΈ μΉ μ±μ λ
geundung.dev
π μ νκ³Ό μΉ νΈμ
μλλ‘μ΄λ λ° Chromium κΈ°λ° λΈλΌμ°μ λ±μμλ κ½€ μ€λμ λΆν° μΉ νΈμλ₯Ό μ§μν΄μκΈ° λλ¬Έμ μΉμμ νΈμ μλ¦Όμ μ 곡ν μ μμμΌλ, μ νμ Safari λΈλΌμ°μ λ μ΄λ₯Ό μ§μνμ§ μμλ€.
μ¬μ€ λ³ΈμΈμ μ νμ΄ PWAλ₯Ό λ΄λ€ λ²λ¦° μ€ μμλ€.
μ νμ νμ νμμ μΈ λͺ¨μ΅μ 보μ΄λλ°, μΉμκ² λ무 λ§μ κΈ°λ₯μ μ 곡νμ¬ λ€μ΄ν°λΈ μ±κ³Όμ κ²½κ³λ₯Ό ν물리 μλ€κ³ μκ°νλ€.
μμλ μ νμ ꡬκΈκ³Ό λ§μ΄ν¬λ‘μννΈ λ±μ μΉ μνκ³λ₯Ό μ£Όλμ μΌλ‘ μ΄λμ΄κ°λ μ‘°μ§κ³Ό λ€λ₯΄κ² PWA κ΄λ ¨ κΈ°μ (νΈμ μλ¦Ό ν¬ν¨)μ λν΄ μ κ·Ήμ μΌλ‘ λμνμ§ μμκ³ , κ·Έ κ²°κ³Όλ‘ Safari λΈλΌμ°μ μμλ§ μ§μνμ§ μλ APIκ° μ¬λΏ μκΈ°κ² λμλ€.
ν μν©μμ macOS κ·Έλ¦¬κ³ iOSμ iPadOS μ μ λ€μ΄ μ¬μ©νλ Safari λΈλΌμ°μ μ μ μ μ¨μ κ²°μ½ λ¬΄μν μ μλ μμ€μ΄κΈ°μ, μΉ(PWA)μ΄ νμ μ»κ³ μν₯λ ₯μ νΌμΉλ €λ©΄ Safari λΈλΌμ°μ μ μ κ·Ήμ μΈ PWA μ§μμ΄ νμν μν©μ΄μλ€.
PWAμ λν΄ κ΄μ¬μ΄ λ§μλ λ³ΈμΈμ, WWDC20λΆν° μΉ νΈμμ λν λ΄μ©μ κΈ°λ€λ €μμΌλ νμ κΈ°λνλ κ²κ³Όλ λ€λ₯Έ λ΄μ©λ§ μ»μκΈ°μ λ€μ μμ¬μμ΄ λ§μλ€. κ·Έλ¬λ μ€ μ΅κ·Ό μ§νλμλ μ ν κ°λ°μ νμ¬μΈ WWDC22μ Meet Web Push for Safari μΈμ
μμ λλμ΄ μΉ νΈμμ κ΄ν λ΄μ©μ΄ μΈκΈλμλ€.
Web Push is supported in Mac Safari beginning with macOS Ventura. And Web Push will be coming to iOS and iPadOS next year.
μ΄λ²μ λ°νν λ΄μ©μ λ°λ₯΄λ©΄ μΉ νΈμλ₯Ό μ§μνλ μ¬ν리 λ²μ μ μλμ κ°λ€.
- Safari 16 (macOS 13, Ventura λΆν°)
- iOS λ° iPadOSλ λ΄λ (2023λ )λΆν° μ§μ
λ€νμΈ μ μ μ ν λ
μμ μΈ μΉ νΈμκ° μλλΌ, λ€λ₯Έ λΈλΌμ°μ λ€μμ ꡬνλμ΄μλ μΉ νΈμ νμ€μ λ°λ₯Έλ€κ³ νλ€. (μ νμ 볡μ‘ν μΈμ¦μ μμ΄λ κ°λ¨ν νΈμ μλ¦Όμ μ 곡ν μ μλ€)
μμ§ MDN Web Docsμ μΉ νΈμ API νΈνμ± νκ° μ
λ°μ΄νΈλμ§ μμλλ°, κ³§ μ
λ°μ΄νΈλ κ² κ°κ³ λ΄λ
μλ iOS Safari μλ μ²΄ν¬ μμ΄μ½μ΄ μκΈ°κ² λ κ²μΌλ‘ 보μΈλ€.
μ‘°λ§κ° μ νΈνμ± ν μμ Safari X μμ΄μ½μ μμ¬ μμΌλ‘ μ¬λΌμ§ κ²μ΄λ€.
μ΄μ¨λ , μ νμ΄ ν¬κΈ°ν μ€λ§ μμλ μΉ νΈμλ₯Ό μ§μν΄μ€λ€κ³ νλ μμΌλ‘μ μΉ κΈ°μ μ΄ μ΄λ»κ² λ³νν μ§ κΈ°λκ° λλ€.
π μΉ νΈμ λμ μ΄ν΄λ³΄κΈ°
μΉ νΈμκ° μ λ°μ μΌλ‘ μ΄λ»κ² λμνλμ§ λ©μ»€λμ¦μ λν΄ μ΄ν΄λ³΄λλ‘ νμ.
π ν(Pull)κ³Ό νΈμ(Push)
λ¨Όμ ν(Pull)κ³Ό νΈμ(Push)μ μ°¨μ΄λ₯Ό μλ κ·Έλ¦Όμ 보며 μ΄ν΄λ³΄μ.
ν(Pull)μ λλΆλΆμ μ ν리μΌμ΄μ
μμ μ½κ² μ΄ν΄λ³Ό μ μλ λμ λ°©μμ΄λ€.
ν΄λΌμ΄μΈνΈ κΈ°μ€μΌλ‘ 보μμ λ, ν(Pull) λ¨μ΄μ μλ―Έμ λμΌνκ² λ°μ΄ν°λ₯Ό λΉκ²¨μ€λ λͺ¨μ΅μ λ³Ό μ μλ€.
μλ²μ μλ νΉμ λ°μ΄ν°λ₯Ό λ°κΈ° μν΄ λ¨Όμ μμ²νκ³ , κ·Έ κ²°κ³Όλ₯Ό λλ €λ°λ ννλ₯Ό μλ―Ένλ€.
νΈμ(Push)λ νκ³Ό λμνλ λͺ¨μ΅μ΄ μ‘°κΈ λ€λ₯΄λ€.
μλ²μμ ν΄λΌμ΄μΈνΈλ‘ λ°μ΄ν°λ₯Ό λ°μ΄ λ£λ(Push) λͺ¨μ΅μ λ³Ό μ μμΌλ©°, ν΄λΌμ΄μΈνΈκ° λ¨Όμ μμ²μ 보λ΄μ§ μλλΌλ μλ²κ° λ°μ΄ν°λ₯Ό λ°μ΄ λ£μ΄μ£Όλ ννμ΄λ€.
π¨π©π§π¦ μΉ νΈμμ κ΅¬μ± μμ
μΉ νΈμκ° λμνκΈ° μν΄μλ κΈ°λ³Έμ μΌλ‘ νμν κ΅¬μ± μμκ° μλ€.
- λΈλΌμ°μ (μ¬μ©μ)
- νΈμ μλ¦Όμ λ°μ‘ν μλ²
- νΈμ μλ¦Όμ μ¬μ©μμκ² μ λ¬ν νΈμ μλΉμ€
ν¬κ² 보면 μ΄ 3κ°μ§λ‘ μ΄λ£¨μ΄μ Έ μλ€.
μλ²μμ μ΄λ€ μ¬μ©μμκ², μ΄λ€ λ©μμ§λ₯Ό 보λΌμ§μ λν λ΄μ©μ΄ λ΄κΈ΄ λ©μμ§ λ°μ΄ν°λ₯Ό νΈμ μλΉμ€λ‘ μ λ¬νλ©΄, νΈμ μλΉμ€μμ μ¬μ©μ μ 보λ₯Ό μλ³ν ν λͺ©μ μ§λ‘ μ λ¬νλ€.
μΉ νΈμμ΄κΈ° λλ¬Έμ μ¬κΈ°μ μ΄μΌκΈ°νλ λͺ©μ μ§λ λΈλΌμ°μ κ° λλ€.
μ΄λ€ μμλ‘ μ΄λ£¨μ΄μ Έ μκ³ , λλ΅ μ΄λ€ νλ¦μΈμ§ μ κ²λ§ κ°μλ° λ°μ€λ‘ κ°μ‘°ν λΆλΆμ λν΄μ μλ¬Έμ μ΄ μκ²Όμ κ²μ΄λ€.
μ΄ λΆλΆμ μκΈ° μν΄μλ μΉ νΈμ κΈ°λ₯μ μ 곡νκΈ° μν΄ μ ν΄μ§ κ·μ½. μ¦, μΉ νΈμ νλ‘ν μ½(Web Push Protocol)μ λν΄ μμμΌ νλ€.
π€ μΉ νΈμ νλ‘ν μ½
μΉ νΈμ νλ‘ν μ½(Web Push Protocol)μ νΈμ μλ¦Όμ μμ νλ λΈλΌμ°μ μ λ°μ‘νλ μλ²κ° νΈμ μλΉμ€μ μνΈμμ©νκΈ° μν΄ μ ν΄λμ κ·μ½μ΄λ€.
ν΄λΌμ΄μΈνΈ(λΈλΌμ°μ ) μ 보λ₯Ό λ±λ‘νκ³ , μλ²μμ λ©μμ§λ₯Ό λ°μ‘νκ³ , μ΅μ’
μ μΌλ‘ μ¬μ©μμκ² λλ¬νκΈ°κΉμ§ μ¬λ¬ μ μ°¨λ₯Ό κ±°μΉκ² λλ€.
μ μ°¨μ λν λΆλΆμ μλ κ·Έλ¦Όμ ν΅ν΄ κ°λ΅ν μ΄ν΄λ³Ό μ μλ€.
λ¨Όμ νΈμλ₯Ό μμ νκ² λ ν΄λΌμ΄μΈνΈ(λΈλΌμ°μ )μμλ νΈμ μλΉμ€λ‘ λ΄κ° λκ΅°μ§ μλ¦°λ€.
μ΄ κ³Όμ μ ꡬλ
(Subscription)μ΄λΌκ³ νλ©° μ‘°κΈ λ μμΈν μ μ°¨λ μλ κ·Έλ¦Όκ³Ό κ°λ€.
νΈμ μλΉμ€λ‘ ꡬλ
μμ²μ 보λ΄κ³ , μ±κ³΅μ μΌλ‘ ꡬλ
λ κ²½μ° νΈμ μλΉμ€μμλ ꡬλ
μ 보λ₯Ό λΈλΌμ°μ μκ² λλ €μ€λ€.
ꡬλ
μ 보μλ λΈλΌμ°μ λ₯Ό μλ³νλ μ λ³΄κ° λ΄κ²¨μμΌλ©°, μλ²μμ νΉμ λΈλΌμ°μ (μ¬μ©μ)μκ² νΈμ λ©μμ§λ₯Ό λ°μ‘ν λ μ΄ μ λ³΄κ° νμνλ€.
ꡬλ
μ 보λ μλ²μ μ μ₯ν΄λμλ€κ°, νΈμ μλ¦Ό λ°μ‘μ΄ νμν λ κΊΌλ΄μ μ¬μ©νλ ννκ° λλ€.
μ¬κΈ°κΉμ§μ κ³Όμ μ΄ λ§λ¬΄λ¦¬λμλ€λ©΄, λΈλΌμ°μ κ° νΈμ μλ¦Όμ μμ νκΈ° μν κ³Όμ μ λͺ¨λ λλ¬λ€.
μ΄μ μλ²μμ νΈμ μλΉμ€λ‘ ꡬλ
μ 보μ λ©μμ§λ₯Ό μ λ¬νλ©΄ ν΄λΉνλ λΈλΌμ°μ λ‘ νΈμ μλ¦Όμ΄ μ λ¬λ κ²μ΄λ€.
π μμ ν λ©μμ§λ₯Ό μν VAPID
μ°λ¦¬κ° λμΉμ§ λ§μμΌ ν λΆλΆμ μλ²μμ νΈμ λ©μμ§λ₯Ό μ λ¬ν λ κ·Έλ₯ μ λ¬νμ§ μλλ€λ μ μ΄λ€. νΈμ λ©μμ§λ λ―Όκ°νκΈ° λλ¬Έμ μμ νκ² μνΈνλμ΄μΌ νκ³ , λ©μμ§κ° μ΄λ€ μλ²μμ λ°μ‘λμλμ§ μ μ μμ΄μΌ νλ€.
μΉ νΈμμμλ μ΄λ€ μλ²μμ λ©μμ§λ₯Ό λ°μ‘νλμ§ μλ³νκΈ° μν΄ VAPID(Voluntray Application Server Identification) μΈμ¦ λ°©μμ μ¬μ©νλ€.
VAPIDλ μλ°μ μΌλ‘ μλ²(νΈμ λ©μμ§λ₯Ό λ°μ‘νλ)λ₯Ό μλ³νλ μΈμ¦ λ°©μμ΄λ€.
VAPIDλ κ°λ¨νκ² λ³΄λ©΄ 곡κ°ν€ μνΈν λ°©μμ ν€ μμΌλ‘ κ²μ¦ μ μ°¨λ₯Ό κ±°μΉλλ°, λ¨Όμ 곡κ°ν€ μνΈν λ°©μμ λν΄ κ°λ΅ν μμ보μ.
곡κ°ν€ μνΈνλ λΉκ³΅κ° ν€(Private Key)μ κ³΅κ° ν€(Public Key) μμ κ°λλ€.
곡κ°ν€ μνΈνμ κ²½μ° λΉκ³΅κ° ν€μ κ³΅κ° ν€μ κ°μ΄ μΌμΉνμ§ μλ λΉλμΉ μνΈν λ°©μμ΄λ€.
μνΈν κ³Όμ μ΄ μλ€λ©΄ 볡νΈνλ νμν λ²μΈλ°, κ³΅κ° ν€λ‘ μνΈννλ€λ©΄ λΉκ³΅κ° ν€λ‘ 볡νΈνν΄μΌ νκ³ , λΉκ³΅κ° ν€λ‘ μνΈννλ€λ©΄ κ³΅κ° ν€λ‘ 볡νΈνν΄μΌ νλ€.
μ΄λ¬ν νΉμ±μΌλ‘ μΈν΄ μ μ μλͺ
μ΄λ μΈμ¦μ΄ νμν λΆλΆμμ λ리 μ¬μ©λκ³ μλ μνΈν κΈ°λ²μ΄λ€.
λ€μ λ³Έ λ΄μ©μΌλ‘ λμμμ, VAPIDλ 곡κ°ν€ μνΈν λ°©μμ ν€ μμΌλ‘ μ΄λ£¨μ΄μ Έ μλ€.
μλ²μμ νΈμ μλΉμ€λ‘ λ©μμ§λ₯Ό μ λ¬ν λ VAPID λͺ
μΈμ λ°λ₯Έ μ λ³΄κ° λ΄κΈ΄ JWT(JSON Web Token)μ ν¨κ» μ λ¬νκ² λλλ°, μ΄λ μ΄ ν ν°μ VAPIDμ λΉκ³΅κ° ν€λ‘ μλͺ
(μνΈν)νλ€.
μλͺ
λ ν ν°μ λ°λλ‘ νΈμ μλΉμ€μμ κ³΅κ° ν€λ‘ 볡νΈννμ¬ μ ν¨μ±μ κ²μ¦νκ² λλ€.
μ΄λ¬ν μ μ°¨λ₯Ό ν΅ν΄ νΈμ μλΉμ€μμ μ΄λ€ μλ²λ‘λΆν° μμ ν λ©μμ§μΈμ§, μ ν¨ν λ©μμ§μΈμ§ κ²μ¦ν μ μκ² λλ€.
μ§κΈκΉμ§ μ΄ν΄λ³Έ λ΄μ©μ λͺ¨μ 보면 μλμ κ°μ νλ¦μΌλ‘ λνλΌ μ μλ€.
- (1) λΈλΌμ°μ μμ νΈμ μλΉμ€ ꡬλ + VAPID κ³΅κ° ν€ μ λ¬
- (2) ꡬλ μ 보 μμ
- (3) ꡬλ μ 보 μλ²λ‘ μ λ¬
- (4) ꡬλ μ 보 + λ©μμ§ + VAPID λΉκ³΅κ° ν€λ‘ μνΈνλ JWT
- (5) VAPID κ³΅κ° ν€λ‘ μ ν¨μ± κ²μ¦ & κ²μ¦λ κ²½μ° κ΅¬λ μ 보μ ν΄λΉνλ λΈλΌμ°μ λ‘ νΈμ λ©μμ§ λ°μ‘
4, 5λ² κ³Όμ μμ λ°μ΄ν°κ° μμ‘°λκ±°λ, ν€κ° μΌμΉνμ§ μλ κ²½μ° νΈμ λ©μμ§λ μ ν¨νμ§ μμ μνκ° λμ΄ λΈλΌμ°μ κΉμ§ λλ¬νμ§ λͺ»νκ³ νΈμ μλΉμ€μμ νκΈ°λλ€.
VAPID μΈμ¦ κ³Όμ μ Node νκ²½μμ ꡬνν΄λ³Έ μ½λκ° μ‘΄μ¬νλλ° κ΄μ¬μ΄ μλ€λ©΄ ν λ² μ΄ν΄λ³΄λ©΄ μ’μ κ² κ°λ€.
μΉ νΈμμ μ 체μ μΈ νλ¦μ μ§κΈκΉμ§ μ΄ν΄λ³Έ κ²κ³Ό κ°λ€.
볡μ‘νκ² λκ»΄μ§ μλ μμ§λ§ μ½λλ₯Ό μμ±νλ€λ³΄λ©΄ ν¨μ¬ μ½κ² μλΏμ κ²μ΄λ€.
π₯ μΉ νΈμ ꡬννκΈ°
μνν μ§νμ μν΄ μΉ νΈμ λ°λͺ¨ μ½λλ₯Ό μ€λΉνμλ€.
μλ κΉνλΈ μ£Όμμμ μ½λλ₯Ό λ€μ΄λ‘λ λ°κ³ , μ½λμ λμμ νμΈν΄λ³΄μ.
https://github.com/leegeunhyeok/web-push
GitHub - leegeunhyeok/web-push: π Web Push | Getting Started
π Web Push | Getting Started. Contribute to leegeunhyeok/web-push development by creating an account on GitHub.
github.com
README νμΌμ μ 리λμ΄μλ λ΄μ©λλ‘ νκ²½μ μΈν
νλλ‘ νμ.
μ€λΉκ° λμλ€λ©΄ λ°λͺ¨ μλ²λ₯Ό μ€νμν¨ ν νμ΄μ§μ μ μνμ¬ λ€μ λ΄μ©λ€μ νλμ© μ΄ν΄λ³΄μ.
μΉ νΈμ κΈ°λ₯μ μΉ μ컀 μ€ ν μ’
λ₯μΈ μλΉμ€ μ컀(Servier Worker)λ₯Ό ν΅ν΄ ꡬνν μ μμΌλ©°, λΈλΌμ°μ μ λ°±κ·ΈλΌμ΄λ νκ²½μμ μ²λ¦¬λλ€.
(μλΉμ€ μ컀μ λν μμΈν λ΄μ©μ μΆν μμ±ν μμ μ΄λ€)
λͺ¨λ λΈλΌμ°μ (μ΄μ Safari ν¬ν¨)μλ μΉ νΈμ μλ¦Όμ μ 곡νκΈ° μν APIλ€μ΄ ꡬνλμ΄μλ€.
νΈμ μλΉμ€λ₯Ό ꡬλ
νκ³ , λ©μμ§λ₯Ό μμ νκΈ° μν Push API, κ·Έλ¦¬κ³ μλ¦Όμ μ¬μ©μμκ² λ
ΈμΆμν€κΈ° μν Notification APIκ° μλ€.
μ΄μ λν λΆλΆμ κ°λ΅ν μ΄ν΄λ³΄κ³ μ§λκ°λλ‘ νμ.
π Notification API
λΈλΌμ°μ μμ μμ€ν
μλ¦Όμ λμ°κΈ° μν κΈ°λ₯μ μ 곡νλ€. μ°λ¦¬λ μ΄λ₯Ό ν΅ν΄ μ¬λ¬ νλ«νΌ(μλμ°, λ§₯, 리λ
μ€, μλλ‘μ΄λ, iOS)μ λ§λ μμ€ν
μλ¦Όμ μ½κ² λμΈ μ μλ€.
Notification APIλ₯Ό ν΅ν΄ μν κ°λ₯ν λνμ μΈ κΈ°λ₯μ μλμ κ°λ€.
- μλ¦Ό κΆν μμ²νκΈ°
- μλ¦Ό κΆν μν
- μλ¦Ό λμ°κΈ°
(μμμΌλ‘ κ°μ‘°ν λΆλΆμ λν΄μλ§ κ°λ¨ν μ΄ν΄λ³Έλ€)
μλ¦Ό κΆνμ μλμ κ°μ΄ μ½κ² νμ
ν μ μλ€.
Notification.permission; // 'default', 'denied', 'granted'
κΈ°λ³Έ μνλ defaultμ΄λ©°, μμ§ λΈλΌμ°μ μμ μ¬μ©μμκ² κΆνμ λ¬Όμ΄λ³΄μ§ μμ μνλ₯Ό μλ―Ένλ€.
λΈλΌμ°μ μμ μ¬μ©μμκ² κΆνμ λ¬Όμ΄λ³Έ ν μ¬μ©μκ° μ νν λμμ λ°λΌ λ κ°μ§ μνλ‘ λλ μ§λ€.
μ¬μ©μκ° κ±°λΆν κ²½μ° denied, νκ°ν κ²½μ° grantedκ° λλ©°, μ΄ μνμμλ λ μ΄μ κΆν λ³κ²½μ΄ λΆκ°νλ€. (μ¬μ©μκ° μ§μ λΈλΌμ°μ μ€μ μ ν΅ν΄ λ³κ²½ν΄μΌ νλ€.)
κΆνμ΄ νκ° μνμΈ κ²½μ° μλ¦Όμ λμΈ μ μκ² λλ€. μλ¦Όμ λμ°λ λ°©λ²μ ν¬κ² λ κ°μ§κ° μλλ° μλ¦Ό κ°μ²΄λ₯Ό μμ±νλ κ²κ³Ό μλΉμ€ μ컀μ showNotification λ©μλλ₯Ό νΈμΆνλ κ²μ΄λ€. μ¬κΈ°μλ νμ λ°©μμ λν΄ μμλ³Έλ€.
λ¨Όμ μλ¦Όμ λμ°λ μ½λλ₯Ό μ΄ν΄λ³΄μ.
registration.showNotification(title, {
body: 'Some message',
});
μλΉμ€ μ컀 λ±λ‘ κ°μ²΄μλ μλ¦Όμ λμΈ μ μλ showNotification λ©μλκ° κ΅¬νλμ΄μλ€.
첫 λ²μ§Έ μΈμλ‘λ μλ¦Όμ μ λͺ©, λ λ²μ§Έ μΈμλ‘λ μλ¦Όμ λ³Έλ¬Έ κ·Έλ¦¬κ³ κΈ°ν μ΅μ
λ€μ΄ μ λ¬λλ€.
μλ¦Ό κΆνμ΄ λΆμ¬(granted)λμ΄μλ€λ©΄, μ μμ μΌλ‘ μμ€ν
μλ¦Όμ΄ νμλ κ²μ΄λ€.
π Push API
Push APIλ νΈμ μλΉμ€λ‘λΆν° μ μ‘λ λ©μμ§λ₯Ό μμ ν μ μλ κΈ°λ₯μ μ 곡νλ€.
μ΄λ₯Ό ν΅ν΄ μ¬μ©μκ° νΈμ μλ¦Όμ μμ νκ³ , μΉ μ ν리μΌμ΄μ
μ μ¬μ°Έμ¬ν μ μλλ‘ κΈ°λ° κΈ°λ₯μ μ 곡νλ€.
μΉ μ ν리μΌμ΄μ
μ΄ νΈμ λ©μμ§λ₯Ό μμ νκΈ° μν΄μλ νμ±ν λμ΄μλ μλΉμ€ μμ»€κ° νμνλ€.
μλΉμ€ μ컀λ λΈλΌμ°μ λ°±κ·ΈλΌμ΄λ νκ²½μμ λμνκ³ , νΈμ λ©μμ§λ₯Ό μμ νμ λ μ΄λ²€νΈ νΈλ€λ¬λ₯Ό ν΅ν΄ μμ ν λ©μμ§λ₯Ό μ²λ¦¬ν μ μλ€.
Push APIμ μ£Όμ κΈ°λ₯μ λμ΄ν΄λ³΄λ©΄ μλμ κ°λ€.
- νΈμ μλΉμ€ ꡬλ λ° ν΄μ§
- νΈμ λ©μμ§ μμ
βοΈ μλΉμ€ μ컀 λ±λ‘νκΈ°
νΈμλ₯Ό μμ νκΈ°μ μμ μλΉμ€ μ컀 λ±λ‘μ΄ νμνλ€. μλΉμ€ μ컀λ λ€λ₯Έ μΉ μ컀μ λ§μ°¬κ°μ§λ‘ μ컀 μ€ν¬λ¦½νΈλ₯Ό λ‘λνμ¬ μ€μΉ & νμ±νμν¬ μ μλ€.
async function registerServiceWorker () {
if (!('serviceWorker' in navigator)) return;
// μ΄λ―Έ λ±λ‘λμ΄μλ μ 보 κ°μ Έμ€κΈ°
let registration = await navigator.serviceWorker.getRegistration();
if (!registration) {
// μμΌλ©΄ μλΉμ€ μ컀 λ±λ‘
registration = await navigator.serviceWorker.register('/service-worker.js');
}
}
navigator κ°μ²΄μ serviceWorkerκ° μ‘΄μ¬νλμ§ νμΈνμ¬ νμ¬ λΈλΌμ°μ κ° μλΉμ€ μ컀λ₯Ό μ§μνλμ§ νμΈν μ μλ€.
μ‘΄μ¬νμ§ μμ κ²½μ° κΈ°λ₯ μ¬μ©μ΄ λΆκ°νλ λΆκΈ° μ²λ¦¬κ° λ°λμ νμνλ€.
navigator.serviceWorker.getRegistrationμ ν΅ν΄ μλΉμ€ μ컀μ λ±λ‘ κ°μ²΄λ₯Ό κ°μ Έμ¬ μ μλ€.
μμ§ μλΉμ€ μμ»€κ° λ±λ‘λμ΄μμ§ μλ€λ©΄ nullμ λ°ννκΈ° λλ¬Έμ κ°μ μ 무λ₯Ό ν΅ν΄ κΈ°μ‘΄ λ±λ‘ κ°μ²΄λ₯Ό μ¬μ©ν μ§, μλ‘ μλΉμ€ μ컀λ₯Ό λ±λ‘ν μ§ κ²°μ ν μ μλ€.
μλΉμ€ μ컀 λ±λ‘μ navigator.serviceWorker.registerλ₯Ό ν΅ν΄ μ§νν μ μλ€. 첫 λ²μ§Έ μΈμμλ μλΉμ€ μ컀 μ€ν¬λ¦½νΈκ° μμΉν κ²½λ‘λ₯Ό λ¬Έμμ΄λ‘ μ λ¬νλ©΄ λκ³ , λ€μ΄λ‘λμ μ€μΉ κ³Όμ μ λΈλΌμ°μ μμ μμμ μ²λ¦¬νλ€.
π νΈμ μλΉμ€ ꡬλ νκΈ°
μλΉμ€ μμ»€κ° λ±λ‘λμλ€λ©΄ λ±λ‘ κ°μ²΄λ₯Ό ν΅ν΄ νΈμ λ§€λμ (Push Manager)μ μ κ·Όν μ μλ€.
registration.pushManager;
μλΉμ€ μ컀 λ±λ‘ κ°μ²΄μ pushManagerκ° μλ€λ©΄, νΈμ μλ¦Όμ μ§μνμ§ μλ λΈλΌμ°μ μ΄λ μ΄ λν 쑰건μ ν΅ν΄ λΆκΈ° μ²λ¦¬κ° κ°λ₯νλ€.
μμ μ΄ν΄λ³΄μλ λ΄μ© μ€ νΈμ μλ¦Όμ΄ λμνλ νλ¦μ λμ§μ΄λ³΄λ©΄, 첫 λ²μ§Έλ‘ ν΄λΌμ΄μΈνΈ(λΈλΌμ°μ )μμ νΈμ μλΉμ€λ₯Ό ꡬλ
νλ κ²μ κΈ°μ΅ν κ²μ΄λ€.
νΈμ μλΉμ€ ꡬλ
μ νΈμ λ§€λμ λ₯Ό ν΅ν΄ μ²λ¦¬ κ°λ₯νκ³ , μ½λλ‘ κ΅¬ννμλ©΄ μλμ κ°λ€.
const subscription = await registration.pushManager.subscribe({
applicationServerKey: VAPID_PUBLIC_KEY,
userVisibleOnly: true,
});
νΈμ λ§€λμ μ ꡬνλμ΄μλ subscribe λ©μλλ₯Ό ν΅ν΄ νΈμ μλΉμ€μκ² κ΅¬λ
μμ²μ λ³΄λΌ μ μκ³ , μ΄ κ³Όμ μμ μλ¦Όμ μμ νκΈ° μν΄ λΈλΌμ°μ κ° μ¬μ©μμκ² κΆνμ νμΈνλ μ μ°¨λ₯Ό μννκ² λλ€.
ꡬλ
μμ²μ μν΄ subscribeλ₯Ό νΈμΆν κ²½μ° λ°λμ μ¬μ©μ μ μ€μ²κ° νμνλ°, μ΄λ κ°λ°μκ° μ
μμ μΌλ‘ νΈμ μλΉμ€μ ꡬλ
νλ κ²μ λ°©μ§νκΈ° μν¨μ΄λ€. μ¦, λ°λμ μ¬μ©μκ° μ΄λ ν νμλ₯Ό ν΄μΌ ꡬλ
μμ²μ΄ νμ©λλ€. (μ: λ²νΌ ν΄λ¦ λ±)
ꡬλ
μμ²μ 보λ΄κΈ° μν΄μλ μλ²λ₯Ό μλ³νκΈ° μν VAPID 곡κ°ν€(applicationServerKey)μ, νΈμ λ©μμ§κ° μ¬μ©μμκ² λ³΄μ΄λ μ©λλ‘ μ¬μ©λλ€λ κ²μ μλ―Ένλ νλκ·Έ κ°(userVisibleOnly)μ΄ νμνλ€.
λ°λͺ¨ μ½λ νκ²½μ μ€λΉνλ©΄μ VAPID ν€ μμ μμ±νμν
λ° default.jsonμ μ μμ μΌλ‘ λ£μ΄λμλ€λ©΄, APIλ₯Ό ν΅ν΄ 곡κ°ν€λ₯Ό λ°μμ¨ ν applicationServerKey νλ‘νΌν° κ°μΌλ‘ μ¬μ©λ κ²μ΄λ€.
userVisibleOnly κ°μ λ°λμ true μ΄μ΄μΌ νκ³ , μμ ν νΈμ λ©μμ§λ μλ¦Όμ ν΅ν΄ λ°λμ μ¬μ©μμκ² λ³΄μ¬μ£Όμ΄μΌ ν¨μ μλ―Ένλ€.
μ μμ μΌλ‘ ꡬλ
λμλ€λ©΄, ꡬλ
μ 보λ₯Ό λ°μλ³Ό μ μλ€.
ꡬλ
μ 보λ μλμ κ°μ ννλ‘ κ΅¬μ±λμ΄ μμΌλ©°, μλ²μμλ νΈμ λ©μμ§λ₯Ό λ°μ‘ν λ ν΄λΉ ꡬλ
μ 보λ₯Ό ν¨κ» μ λ¬νκ² λλ€.
{
"endpoint": "https://web.push.apple.com/QHnG_a...BVMI",
"keys": {
"p256dh": "BC56...kSB9-Vcq8",
"auth": "fz8...sA"
}
}
λ°λͺ¨ μ½λλ₯Ό μ΄ν΄λ³΄λ©΄ μλμ κ°μ΄ ꡬλ
μ 보λ₯Ό μλ²λ‘ μ λ¬νλ μ½λλ₯Ό νμΈν μ μλ€.
async function postSubscription (subscription?: PushSubscription) {
...
const response = await fetch('/subscription', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ userId, subscription }),
});
}
λ°λͺ¨ μ½λμμλ μ λ¬ν ꡬλ μ 보λ₯Ό μ¬μ©μ IDμ λ§€ννμ¬ μλ² μΈ‘μ μ μ₯ν΄λκ² λλ€.
π νΈμ μλΉμ€ ꡬλ μ·¨μνκΈ°
νΈμ μλΉμ€ ꡬλ
μ·¨μλ λ κ°λ¨νκ² κ΅¬νν μ μλ€.
νΈμ μλΉμ€ ꡬλ
ν μ λ¬λ°μ ꡬλ
μ 보μ ꡬνλμ΄μλ unsubscribe λ©μλλ₯Ό νΈμΆνκΈ°λ§ νλ©΄ λλ€.
const unsubscribed = await subscription.unsubscribe();
Promise κ²°κ³Ό κ°μΌλ‘λ μ·¨μ μ±κ³΅ μ¬λΆλ₯Ό boolean κ°μΌλ‘ λ°ννλ€.
λ°λͺ¨ μ½λμμλ ꡬλ
μ μ·¨μν κ²½μ° μλ²μλ μ΄ μ¬μ€μ μλ € μ μ₯ν΄λμλ μ¬μ©μμ ꡬλ
μ 보λ₯Ό ν¨κ» μ κ±°νλλ‘ κ΅¬νλμ΄μλ€.
βοΈ νΈμ λ©μμ§ λ³΄λ΄κ³ & λ°κΈ°
νΈμ λ©μμ§λ₯Ό 보λ΄κΈ° μν΄μλ, μλ²μμ μΉ νΈμ νλ‘ν μ½μ λ°λΌ νΈμ μλΉμ€λ‘ λ©μμ§λ₯Ό μ λ¬ν΄μΌ νλ€.
μ§μ ꡬννλ€λ©΄ μλΉν 볡μ‘ν ν
λ°, λ€νν μΉ νΈμ νλ‘ν μ½ νμ€μ λ§κ² ꡬνλμ΄μλ μ¬λ¬ λΌμ΄λΈλ¬λ¦¬λ€μ΄ μ‘΄μ¬νλ€.
λ°λͺ¨ μ½λμμλ web-push λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νμ¬ κ΅¬ννμΌλ μ°Έκ³ λ°λλ€.
μλ² μͺ½μμ νΈμ λ©μμ§λ₯Ό μ΄λ»κ² 보λ΄κ³ μλμ§ μ½λλ₯Ό νμΈν΄λ³΄μ.
import webpush from 'web-push';
webpush.setGCMAPIKey(GCM_KEY);
webpush.setVapidDetails(
SUBJECT,
VAPID_PUBLIC,
VAPID_PRIVATE
);
webpush.sendNotification(subscription, JSON.stringify({
title: 'Web Push | Getting Started',
body: message || '(Empty message)',
}));
μλ² μ½λλ₯Ό μ΄ν΄λ³΄λ©΄ μμ κ°μ μ½λκ° μ‘΄μ¬νλλ°, web-push λΌμ΄λΈλ¬λ¦¬λ₯Ό ν΅ν΄ μμ£Ό κ°λ¨ν λ°μ‘ μ½λλ₯Ό ꡬνν μ μλ€.
νΈμ μλ¦Όμ λ°μ‘νκΈ° μ μ κΈ°λ³Έμ μΈ κ΅¬μ±(GCM ν€ λ° VAPID ν€ λ±λ‘)μ μ§ννκ³ , λΌμ΄λΈλ¬λ¦¬ μμ€μ ꡬνλμ΄μλ sendNotification λ©μλλ₯Ό νΈμΆνμ¬ νΈμ λ©μμ§λ₯Ό λ°μ‘νλ€.
λ°μ‘ν λ, ꡬλ
μ 보μ λ©μμ§ λ°μ΄ν°λ₯Ό ν¨κ» μ λ¬νλ κ²μ νμΈν μ μλ€.
λ°μ‘ν νΈμ λ©μμ§λ νΈμ μλΉμ€λ₯Ό κ±°μ³ λͺ©μ μ§μΈ λΈλΌμ°μ μ λλ¬νκ² λλ€.
λΈλΌμ°μ μ νΈμ λ©μμ§κ° λλ¬ν κ²½μ° μλΉμ€ μ컀μ push μ΄λ²€νΈλ₯Ό ν΅ν΄ μ΄λ₯Ό μ²λ¦¬ν μ μλλ°, μλΉμ€ μ컀 μ½λλ₯Ό μ μ μ΄ν΄λ³΄λλ‘ νμ.
self.addEventListener('push', (event: PushEvent) => {
const message = event.data?.json() as PushMessage;
event.waitUntil(
self.registration.showNotification(message.title, {
body: message.body,
})
);
});
self.addEventListener('notificationclick', (event: NotificationEvent) => {
self.clients.openWindow('https://github.com/leegeunhyeok/web-push');
});
μλΉμ€ μ컀 μ½λμ μΌλΆλΆμ κ°μ Έμλ€.
μ¬κΈ°μ νμΈν λΆλΆμ push μ΄λ²€νΈ νΈλ€λ¬ λΆλΆμ΄λ€. push μ΄λ²€νΈλ νΈμ μλΉμ€λ‘λΆν° λ©μμ§λ₯Ό μμ νμ λ νΈλ¦¬κ±° λλ©°, λΈλΌμ°μ κ° λ«νμλλΌλ μλΉμ€ μ컀μμ μ΄λ₯Ό μμ νμ¬ μ²λ¦¬ν μ μλ€.
self.registration λΆλΆμ μλΉμ€ μ컀 λ±λ‘ κ°μ²΄λ₯Ό μλ―Ένλ©°, showNotificationμ μμ μ΄ν΄λ³΄μλ Notification APIμ κΈ°λ₯ μ€ νλμ΄λ€. μ¦, ν΄λΉ μ½λλ μ΄λ²€νΈ κ°μ²΄μμ μμ ν λ©μμ§ λ°μ΄ν°λ₯Ό κΊΌλ΄μ¨ ν μλ¦Όμ λμ°λ μ½λλΌκ³ λ³Ό μ μλ€.
μλμ notificationclick μ΄λ²€νΈλ μ΄λ²€νΈ λͺ
μμ μ μΆν μ μλ―μ΄ μ¬μ©μκ° μλ¦Όμ λλ μ λ νΈλ¦¬κ±°λλ μ΄λ²€νΈμ΄λ€.
μ¬μ©μκ° νΈμ μλ¦Όμ μμ νκ³ , μ΄λ₯Ό ν΄λ¦νμ λ μ΄λ ν λμμ μ²λ¦¬ν΄μΌ ν λ ν΄λΉ μ΄λ²€νΈ νΈλ€λ¬μμ λ‘μ§μ ꡬννλ©΄ λλ€.
(μ: νΈμ μλ¦Ό ν΄λ¦ μ νΉμ μΉ νμ΄μ§ μ΄μ΄μ£ΌκΈ°)
βοΈ νΈμ μλ¦Ό λμ νμΈν΄λ³΄κΈ°
μ§κΈκΉμ§ ν΅μ¬ κΈ°λ₯κ³Ό μ½λλ₯Ό μ΄ν΄λ³΄μλ€.
νΈμ μλΉμ€λ₯Ό ꡬλ
νκ³ , μ€μ λ‘ νΈμ λ©μμ§λ₯Ό λ°μ μμ€ν
μλ¦Όμ΄ λ¨λμ§ μ λ°μ μΌλ‘ νμΈν΄λ³΄μ.
λ°λͺ¨ μλ²λ₯Ό μ€ννκ³ νμ΄μ§μ μ μνλ©΄, λ‘κ·ΈμΈ νλ©΄μΌλ‘ μ΄λν κ²μ΄λ€. μ¬κΈ°μμ μ¬μ©μλ₯Ό μλ³νκΈ° μν΄ μμμ μμ΄λλ₯Ό μ
λ ₯νκ³ ν νλ©΄μΌλ‘ μ΄λνμ.
μ μ IDλ₯Ό safari_userλ‘ μ
λ ₯ν ν μ΄λν ν νλ©΄ λͺ¨μ΅μ΄λ€.
κ° μΉμ
μ λν΄ κ°λ΅ν μκ°νλλ‘ νκ² λ€.
- Status: μλΉμ€ μ컀 λ±λ‘ μ¬λΆ, νΈμ μλ¦Ό μ§μ μ¬λΆ, νΈμ μλ¦Ό κΆν 그리λ ꡬλ μ 보λ₯Ό νμΈν μ μλ€.
- Subscribe: νΈμ μλΉμ€ ꡬλ & ν΄μ§
- Send Push Notification: νΉμ μ¬μ©μ IDλ‘ νΈμ λ©μμ§ μ μ‘
- Logout: λ‘κ·ΈμΈ νλ©΄μΌλ‘ μ΄λ
λ¨Όμ νΈμ μλΉμ€ ꡬλ μ μν΄ Subscribe λ²νΌμ λλ¬λ³΄μ. κ·Έλ¬λ©΄ μλμ κ°μ΄ μλ¦Ό κΆνμ λ¬Όμ΄λ³Ό κ²μ΄λ€. λμμ νμΈν κ²μ΄κΈ° λλ¬Έμ νμ©ν΄μ£Όλλ‘ νμ.
(λ§μ½ κ±°λΆνκ±°λ μ·¨μν κ²½μ° κΆνμ κ±°λΆ(denied) μνκ° λλλ°, λΈλΌμ°μ μ€μ μΌλ‘ μ΄λνμ¬ κΆνμ μ¬μ€μ ν ν νμ΄μ§λ₯Ό μλ‘ κ³ μΉ¨ νλ©΄ λλ€)
μ μμ μΌλ‘ ꡬλ
λ κ²½μ° μλμ κ°μ΄ ꡬλ
μ 보λ₯Ό Status μΉμ
μμ νμΈν μ μλ€. νμ¬ λμμ Έ μλ λΈλΌμ°μ λ νΈμ μλ¦Όμ μμ ν μ μλ€.
Send Push Notification μΉμ
μμ Target User ID λΆλΆμ νμ¬ λ³ΈμΈμ μμ΄λλ₯Ό μ
λ ₯ν ν Send λ²νΌμ λλ¬λ³΄μ.
μλ²λ₯Ό ν΅ν΄ νΈμ μλΉμ€λ‘ λ©μμ§κ° μ λ¬λκ³ , νΈμ μλΉμ€μμ λ³ΈμΈ λΈλΌμ°μ λ‘ νΈμ λ©μμ§λ₯Ό μ λ¬νμ¬ μλ¦Όμ΄ λ
ΈμΆλ κ²μ΄λ€.
λ°λͺ¨λ₯Ό ν΅ν΄ λ³ΈμΈμκ² νΈμ λ©μμ§λ₯Ό μ μ‘νκ±°λ, λ€λ₯Έ μ¬μ©μμκ² λ©μμ§λ₯Ό μ μ‘ν μλ μλ€.
λ€λ₯Έ μ¬μ©μμκ² νΈμ λ©μμ§λ₯Ό μ μ‘νλ κ³Όμ μ μλ μμμ μ°Έκ³ νλ©΄ μ’μ κ² κ°λ€.
μ§κΈκΉμ§ μΉ νΈμμ λν΄ μ λ°μ μΌλ‘ μ΄ν΄λ³΄μλ€.
λ€μ΄ν°λΈμμλ§ κ΅¬ν κ°λ₯νλ νΈμ μλ¦Όμ μΉ νκ²½μμ ꡬννκ³ , μ¬μ©μμκ² κΈ°λ₯μ μ 곡ν μ μλ€λ μ μ μΉμ΄λΌλ νλ«νΌμ ν° λ¬΄κΈ°λ₯Ό μ₯μ΄μ€ κ²μ΄λΌκ³ μκ°νλ€.
μ΄λ²μ μμλ³Έ νΈμ μλ¦Ό λΏλ§ μλλΌ κ²°μ μμ², μ체 μΈμ¦, νμΌ μμ€ν
λ± μ¬λ¬ κ°μ§ API μ¬μλ€μ΄ νμνκ³ κ΅¬νλκ³ μλ κ°μ΄λ°, μμΌλ‘μ μΉμ΄ μ΄λ»κ² λ³νν μ§ λμ±λ κΆκΈν΄μ§λ€.
References
- https://developer.apple.com/documentation/usernotifications/sending_web_push_notifications_in_safari_and_other_browsers
- https://www.w3.org/TR/push-api
- https://notifications.spec.whatwg.org
- https://w3c.github.io/ServiceWorker
- https://www.rfc-editor.org/rfc/rfc8292