이근λ‘₯
κ·Όλ‘₯이의 λΈ”λ‘œκ·Έ
이근λ‘₯
전체 방문자
776,740
였늘
409
μ–΄μ œ
460

곡지사항

  • 전체보기 (107)
    • μ›Ή (9)
    • μ–Έμ–΄ & ν”„λ ˆμž„μ›Œν¬ (53)
      • JavaScript (7)
      • TypeScript (0)
      • Node.js (11)
      • Vue.js (3)
      • React (0)
      • C & C++ (19)
      • Java & JSP (9)
      • Python (4)
    • 컴퓨터 κ³Όν•™ (3)
      • μ•Œκ³ λ¦¬μ¦˜ (0)
      • 자료ꡬ쑰 (3)
    • 기타 (9)
      • Linux (1)
      • Git (2)
      • DialogFlow (4)
    • 일상 (13)
      • κ²Œμž„ (13)
    • 칼럼 (9)
      • 회고 (0)
      • λ‚˜λ§Œμ˜ κΈ€ (0)
      • μ œν’ˆ 리뷰 (9)
    • __Dev__ (9)
      • Release (9)
λ°˜μ‘ν˜•

인기 κΈ€

  • [Tomcat] μ•„νŒŒμΉ˜ ν†°μΊ£ μ„œλ²„ 포⋯
    2018.08.24
    [Tomcat] μ•„νŒŒμΉ˜ ν†°μΊ£ μ„œλ²„ 포⋯
  • [Node.js] μ‹€μ‹œκ°„ μ±„νŒ… μ„œλΉ„μŠ€β‹―
    2018.05.31
    [Node.js] μ‹€μ‹œκ°„ μ±„νŒ… μ„œλΉ„μŠ€β‹―
  • [PWA] ν”„λ‘œκ·Έλ ˆμ‹œλΈŒ μ›Ή μ•± μ‹œμž‘β‹―
    2019.01.09
    [PWA] ν”„λ‘œκ·Έλ ˆμ‹œλΈŒ μ›Ή μ•± μ‹œμž‘β‹―
  • [Linux] Ubuntu 18.04 ν…Œλ§ˆ μ„€β‹―
    2019.01.16
    [Linux] Ubuntu 18.04 ν…Œλ§ˆ μ„€β‹―
  • [DialogFlow] λ‹€μ΄μ–Όλ‘œκ·Έ ν”Œλ‘œβ‹―
    2018.07.13
    [DialogFlow] λ‹€μ΄μ–Όλ‘œκ·Έ ν”Œλ‘œβ‹―

νƒœκ·Έ

  • spread syntax
  • vue-next
  • WWDC
  • 이클립슀
  • vue.js
  • μ›Ή ν™•μž₯
  • ν”„λ‘œκ·Έλ ˆμ‹œλΈŒ μ›Ή μ•±
  • Vue 3
  • self
  • composition-api
  • vue3
  • Scanner
  • Hello World!
  • μ΄νŽ™νŠΈ
  • μ „κ°œ ꡬ문
  • javascript
  • Java FX
  • WWDC20
  • μžλ°”
  • Composition API
  • ES6
  • vue
  • vuex
  • AstroWar
  • μΆœκ°„
  • Deemo
  • μžλ°” ν”„λ‘œμ νŠΈ
  • java
  • pwa
  • νŒŒν‹°ν΄

졜근 λŒ“κΈ€

  • μ„œλ²„ 둜그 ν™•μΈν•΄λ³΄μ„Έμš”
    γ…Œγ…Œγ…Œ
  • /socket.io/socket.io.js μ—μ„œβ‹―
    γ…Œγ…Œγ…Œ
  • io.socket.on 말고 sockets 볡⋯
    γ…Œγ…Œ
  • get을 μˆ˜μ •ν•œ λ’€ ν„°λ―Έλ„λ‘œ λ¦¬λ‘œβ‹―
    junny
  • κ·Όλ‘₯λ‹˜... μ΄ˆλ©΄μ— 정말 μ£„μ†‘ν•˜β‹―
    μ½κ³ μ“°μž

졜근 κΈ€

  • μ›Ή ν‘Έμ‹œ μ•Œλ¦Ό(Web Push Notificβ‹―
    2022.06.13
    μ›Ή ν‘Έμ‹œ μ•Œλ¦Ό(Web Push Notificβ‹―
  • μŠ€νƒ(Stack)
    2022.05.26
    μŠ€νƒ(Stack)
  • λ°°μ—΄(Array)
    2022.05.25
    λ°°μ—΄(Array)
  • Cμ–Έμ–΄λ‘œ λ°°μš°λŠ” 자료ꡬ쑰
    2022.05.24
    Cμ–Έμ–΄λ‘œ λ°°μš°λŠ” 자료ꡬ쑰
  • [Vue 3] Composition API와 ν…œβ‹―
    2020.10.02
    [Vue 3] Composition API와 ν…œβ‹―

λΈ”λ‘œκ·Έ 메뉴

  • ν™ˆ
  • λ―Έλ””μ–΄λ‘œκ·Έ
  • λ°©λͺ…둝
hELLO Β· Designed By μ •μƒμš°.
이근λ‘₯

κ·Όλ‘₯이의 λΈ”λ‘œκ·Έ

μ›Ή ν‘Έμ‹œ μ•Œλ¦Ό(Web Push Notification)
μ›Ή

μ›Ή ν‘Έμ‹œ μ•Œλ¦Ό(Web Push Notification)

2022. 6. 13. 04:38
λ°˜μ‘ν˜•

πŸ‘‰ μ‹œμž‘ν•˜λ©΄μ„œ

μ›Ή ν‘Έμ‹œ μ•Œλ¦Ό(Web Push Notification)μ΄λž€, 말 κ·ΈλŒ€λ‘œ λΈŒλΌμš°μ € ν™˜κ²½μ—μ„œ ν‘Έμ‹œ μ•Œλ¦Όμ„ 받을 수 μžˆλŠ” κΈ°μˆ μ„ μ˜λ―Έν•œλ‹€.

ν‘Έμ‹œ μ•Œλ¦Όμ΄λΌκ³  ν•˜λ©΄ λ„€μ΄ν‹°λΈŒ μ•±μ˜ μ „μœ λ¬Όμ΄λΌκ³  λŠλ‚„ 수 μžˆμ§€λ§Œ, μ›Ή ν‘Έμ‹œ κΈ°μˆ μ„ 톡해 μ›Ήμ—μ„œλ„ ν‘Έμ‹œ μ•Œλ¦Ό κΈ°λŠ₯을 κ΅¬ν˜„ν•˜κ³  μ‚¬μš©ν•  수 μžˆλ‹€.

λΈŒλΌμš°μ €μ— κ΅¬ν˜„λ˜μ–΄μžˆλŠ” Push API와 μžμ„Έν•œ μŠ€νŽ™μ€ μ•„λž˜ λ¬Έμ„œμ—μ„œ 확인해볼 수 μžˆλ‹€.

  • MDN Web Docs
  • W3 Spec

λ³Έ ν¬μŠ€νŒ…μ—μ„œλŠ” μ›Ή ν‘Έμ‹œκ°€ λ™μž‘ν•˜λŠ” λ©”μ»€λ‹ˆμ¦˜μ— λŒ€ν•΄ 쑰금 μ„ΈλΆ€μ μœΌλ‘œ μ‚΄νŽ΄λ³΄κ³ , 직접 데λͺ¨ μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄λ©° μ›Ή ν‘Έμ‹œμ— λŒ€ν•œ μ „λ°˜μ μΈ 이해λ₯Ό ν•  수 μžˆλ„λ‘ μ§„ν–‰ν•  μ˜ˆμ •μ΄λ‹€.
λΆ„λŸ‰μ„ μ‘°μ ˆν•˜κΈ° μœ„ν•΄ λͺ¨λ“  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 에도 체크 μ•„μ΄μ½˜μ΄ μƒκΈ°κ²Œ 될 κ²ƒμœΌλ‘œ 보인닀.

아직 μ—…λ°μ΄νŠΈ λ˜μ§€ μ•Šμ€ Push API ν˜Έν™˜μ„± ν‘œ

μ‘°λ§Œκ°„ μœ„ ν˜Έν™˜μ„± ν‘œ μ†μ˜ Safari X μ•„μ΄μ½˜μ€ 역사 μ†μœΌλ‘œ μ‚¬λΌμ§ˆ 것이닀.

μ–΄μ¨Œλ“ , μ• ν”Œμ΄ ν¬κΈ°ν•œ μ€„λ§Œ μ•Œμ•˜λ˜ μ›Ή ν‘Έμ‹œλ₯Ό 지원해쀀닀고 ν•˜λ‹ˆ μ•žμœΌλ‘œμ˜ μ›Ή 기술이 μ–΄λ–»κ²Œ λ³€ν™”ν• μ§€ κΈ°λŒ€κ°€ λœλ‹€.

πŸ‘€ μ›Ή ν‘Έμ‹œ λ™μž‘ μ‚΄νŽ΄λ³΄κΈ°

μ›Ή ν‘Έμ‹œκ°€ μ „λ°˜μ μœΌλ‘œ μ–΄λ–»κ²Œ λ™μž‘ν•˜λŠ”μ§€ λ©”μ»€λ‹ˆμ¦˜μ— λŒ€ν•΄ μ‚΄νŽ΄λ³΄λ„λ‘ ν•˜μž.

πŸš€ ν’€(Pull)κ³Ό ν‘Έμ‹œ(Push)

λ¨Όμ € ν’€(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의 λΉ„κ³΅κ°œ ν‚€λ‘œ μ„œλͺ…(μ•”ν˜Έν™”)ν•œλ‹€.

μ„œλͺ…λœ 토큰은 λ°˜λŒ€λ‘œ ν‘Έμ‹œ μ„œλΉ„μŠ€μ—μ„œ 곡개 ν‚€λ‘œ λ³΅ν˜Έν™”ν•˜μ—¬ μœ νš¨μ„±μ„ κ²€μ¦ν•˜κ²Œ λœλ‹€.

μ΄λŸ¬ν•œ 절차λ₯Ό 톡해 ν‘Έμ‹œ μ„œλΉ„μŠ€μ—μ„œ μ–΄λ–€ μ„œλ²„λ‘œλΆ€ν„° μˆ˜μ‹ ν•œ λ©”μ‹œμ§€μΈμ§€, μœ νš¨ν•œ λ©”μ‹œμ§€μΈμ§€ 검증할 수 있게 λœλ‹€.

μ§€κΈˆκΉŒμ§€ μ‚΄νŽ΄λ³Έ λ‚΄μš©μ„ λͺ¨μ•„ 보면 μ•„λž˜μ™€ 같은 νλ¦„μœΌλ‘œ λ‚˜νƒ€λ‚Ό 수 μžˆλ‹€.

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
λ°˜μ‘ν˜•
μ €μž‘μžν‘œμ‹œλΉ„μ˜λ¦¬λ™μΌμ‘°κ±΄
    'μ›Ή' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
    • [PWA] SNS μ•± 예제둜 λ°°μš°λŠ” ν”„λ‘œκ·Έλ ˆμ‹œλΈŒ μ›Ή μ•± μΆœκ°„!
    • [WWDC20] Safari Web Extension/μ‚¬νŒŒλ¦¬ μ›Ή ν™•μž₯ μ‚΄νŽ΄λ³΄κΈ°
    • [PWA] ν”„λ‘œκ·Έλ ˆμ‹œλΈŒ μ›Ή μ•± μ‹œμž‘ν•˜κΈ° (6) - Push, ν‘Έμ‹œ μ•Œλ¦Ό
    • [PWA] ν”„λ‘œκ·Έλ ˆμ‹œλΈŒ μ›Ή μ•± μ‹œμž‘ν•˜κΈ° (5) - Push, ν‘Έμ‹œ μ•Œλ¦Ό
    이근λ‘₯
    이근λ‘₯
    μƒˆλ‘œμš΄ 것을 μ’‹μ•„ν•˜λŠ” ν”„λ‘ νŠΈμ—”λ“œ 개발자 ✨
    λŒ“κΈ€μ“°κΈ°
    • 이전
    • 1
    • 2
    • 3
    • 4
    • 5
    • ···
    • 107
    • λ‹€μŒ

    ν‹°μŠ€ν† λ¦¬νˆ΄λ°”