πŸ”” Notifiaction으둜 μ‚¬μš©μžμ—κ²Œ μ•Œλ¦Ό 전달

μ§„ν–‰ν–ˆλ˜ 개인 ν”„λ‘œμ νŠΈμΈ Loa-Hands, Web Chatλͺ¨λ‘ μ›Ή μ•±μ—μ„œ μ–΄λ– ν•œ λ°˜μ‘μ΄ 생기고 λ³€κ²½λ˜λŠ” 정보λ₯Ό μ‚¬μš©μžκ°€ μŠ΅λ“ν•˜λŠ”κ²ƒμ΄ μ£Ό 이닀.

λŒ€λΆ€λΆ„μ˜ 앱듀도 λ§ˆμ°¬κ°€μ§€κ² μ§€λ§Œ, μ›Ή 앱을 ν™œμ„±ν™” μ‹œμΌœλ†“κ³  계속 κ·Έ ν™”λ©΄λ§Œ λ³΄λŠ” μ‚¬μš©μžλ“€μ€ μ—†λ‹€.

λ‹€λ₯Έ ν™œλ™μ„ ν•œλ‹€κ±°λ‚˜, μž μ‹œ 자리λ₯Ό λΉ„μš΄λ‹€κ±°λ‚˜.

κ·Έλž˜μ„œ Loa-Handsμ—μ„œ νƒ€μ΄λ¨Έλ‘œ κ³„μ‚°λ˜κ³ μžˆλŠ” 컨텐츠듀이 νŠΉμ • μ‹œκ°„μ— λ„λ‹¬ν–ˆλ‹€κ±°λ‚˜ Web Chat에 μƒˆλ‘œμš΄ λ©”μ‹œμ§€κ°€ μ „λ‹¬λœλ‹€λ©΄ λ‹€λ₯Έν™”면을 λ³΄κ³ μžˆλŠ” μ‚¬μš©μžμ—κ²Œ μ•Œλ¦Όμ„ 전달할 수 μ—†μ„κΉŒ? λΌλŠ” 고민을 ν•˜μ˜€λ‹€.

사싀 λ‹€μ΄μ–Όλ‘œκ·Έκ°™μ€ μ»΄ν¬λ„ŒνŠΈκ°€ μƒμ„±λœλ‹€κ³  ν•˜λ”λΌλ„, ν™œμ„±ν™”λ˜μ–΄μžˆλŠ” λΈŒλΌμš°μ €κ°€ λ‹€λ₯΄λ‹€λ©΄ 확인할 수 μ—†κΈ° λ•Œλ¬Έμ— μ•± λ‚΄λΆ€μ—μ„œμ˜ κΈ°λŠ₯μœΌλ‘œλŠ” ν•œκ³„κ°€ μžˆμ—ˆλ‹€.

즉, λΈŒλΌμš°μ €μ—μ„œ μ œκ³΅ν•˜λŠ” Web APIμ—μ„œ μ•Œμ•„λ³Ό ν•„μš”κ°€ μžˆμ—ˆλ‹€.

Notification

λ‹Ήμ—°νžˆ Web API이닀. λŒ€λΆ€λΆ„μ˜ λΈŒλΌμš°μ €λ“€μ—μ„œ μ œκ³΅λ˜λŠ” κΈ°λŠ₯이고, chromeμ—μ„œλŠ” μŠ€νƒ€μΌλ§ 된 κΈ°λŠ₯도 λͺ¨λ“ˆμ²˜λŸΌ μ œκ³΅ν•˜λŠ”κ²ƒ κ°™μ•˜λ‹€.

Notification μƒμ„±μžλ₯Ό 톡해 μΈν„°νŽ˜μ΄μŠ€λ₯Ό 생성할 수 μžˆλ‹€.

κΆŒν•œ

μ‚¬μš©μžμ˜ λΈŒλΌμš°μ €λ₯Ό μ‘°μž‘ν•˜λŠ” API이기 λ•Œλ¬Έμ—, μ‚¬μš©μžμ˜ κΆŒν•œμ΄ λ¨Όμ € ν•„μš”ν•˜λ‹€.

this.requestPermission = async () => {
  const permission = await Notification.requestPermission(res => res)

  if (permission === 'granted')
    return new Notification('μ•Œλ¦Όμ΄ ν—ˆμš©λ˜μ—ˆμŠ΅λ‹ˆλ‹€.')
}

Notification.requestPermissionλ©”μ†Œλ“œλ₯Ό 톡해 μ‚¬μš©μžμ—κ²Œ κΆŒν•œμ„ μš”μ²­ν•˜λŠ” μ•Œλ¦Όμ΄ κ°„λ‹€.

prompt와 μœ μ‚¬ν•¨

  1. granted κΆŒν•œ 승인
  2. dennied κΆŒν•œ 미승인
  3. default κΆŒν•œμ„ μš”μ²­ν•  수 μžˆλŠ” μƒνƒœ

2번의 denniedμƒνƒœλŠ” κΆŒν•œμ„ μš”μ²­ν•  수 μ‘°μ°¨ μ—†λ‹€. λ”°λΌμ„œ, λ§Œμ•½ μ‚¬μš©μžμ˜ λΈŒλΌμš°μ €μ—μ„œ λͺ¨λ“  μ›Ή μ•±μ—μ„œμ˜ μš”μ²­μ„ denniedν•œ μƒνƒœλΌλ©΄, μ‚¬μš©μžκ°€ κΆŒν•œμ„ defaultμƒνƒœλ‘œ λ³€κ²½ν•˜μ—¬μ•Όλ§Œ μš”μ²­μ΄ μ „λ‹¬λ˜λŠ”κ²ƒ κ°™μ•˜λ‹€.

기본적으둜 https, μΈμ¦μ„œλ‘œ 인증된 μ‚¬μ΄νŠΈ μ—μ„œλŠ” μžλ¬Όμ‡ λ₯Ό ν΄λ¦­ν•˜μ—¬ μƒνƒœλ₯Ό 선택할 수 μžˆλ‹€.

λΉ„λ™κΈ°λ‘œ μž‘λ™μ΄ 되기 λ•Œλ¬Έμ—, μ‚¬μš©μžμ˜ 선택을 λ‹΄κ³ μžˆλŠ” promise객체λ₯Ό λ°˜ν™˜ν•œλ‹€.

.then으둜 응닡 데이터λ₯Ό λ°›μ•„μ„œ μ²˜λ¦¬ν•˜λ„λ‘ κ³΅λ¬Έμ—λŠ” λ‚˜μ™€μžˆμ§€λ§Œ, 비ꡐ적 μ΅œμ‹  방식인async await으둜 해도 μž‘λ™μ€ λ˜λ”λΌ.

κΆŒν•œ μƒνƒœ 확인

ν˜„μž¬ μ‚¬μš©μžμ—κ²Œ μ•Œλ¦Όμ„ 전달할 수 μžˆλŠ” κΆŒν•œμ΄ μ–΄λ–€ μƒνƒœμΈμ§€ 확인해볼 ν•„μš”κ°€ μžˆμ„ 것이닀.

this.checkPermission = () => {
  const permission = Notification.permission
  if (permission === 'granted') return true
  if (permission === 'default') return false
  if (permission === 'denied') return false
}

Notification.permission으둜 κΆŒν•œμ„ 확인해 λ³Ό 수 있으며, ν•΄λ‹Ή 속성은 μ˜€λ‘œμ§€ μ½κΈ°μ „μš©μ΄κΈ° λ•Œλ¬Έμ—, κ°’ 할당과같은 λ°©μ‹μœΌλ‘œλŠ” 값을 λ³€κ²½ν•  수 μ—†λ‹€.

μ‚¬μš©μžμ˜ κΆŒν•œ μ˜μ—­μ΄κΈ° λ•Œλ¬Έμ—, λ‹Ήμ—°ν•˜λ‹€κ³  λ³Έλ‹€.

μ•Œλ¦Ό 전달

λ§Œμ•½ κΆŒν•œμ΄ μŠΉμΈλ˜μ—ˆλ‹€λ©΄, μ•„λž˜μ™€ 같은 λ°©λ²•μœΌλ‘œ μ•Œλ¦Όμ„ 전달할 수 μžˆλ‹€.

new Notification('제λͺ©', { body: 'λ‚΄μš©' })

μ²«λ²ˆμ§Έλ‘œλŠ” 타이틀, λ‘λ²ˆμ§Έλ‘œλŠ” μ˜΅μ…˜κ°μ²΄λ₯Ό 전달할 수 μžˆλŠ”λ°, μ˜΅μ…˜ κ°μ²΄μ—λŠ” μ„ΈλΆ€ λ‚΄μš©, 이미지등을 μΆ”κ°€ν•  수 μžˆμ—ˆλ‹€.

Loa-Hands μ‚¬μš©

ν˜„μž¬, Loa-Handsμ—μ„œλŠ” μ•„λž˜μ™€ 같은 ν•˜λ‚˜μ˜ 이벀트 μƒμ„±μžλ‘œ κ΄€λ¦¬ν•΄μ„œ μ‚¬μš©ν•΄λ³΄μ•˜λ‹€.

μ•± νŠΉμ„±μƒ, λ™μΌν•œ μ‹œκ°„μ— μ—¬λŸ¬ μ•Œλ¦Όμ΄ 전달될 수 있기 λ•Œλ¬Έμ—, κ·ΈλŸ¬ν•œ 상황이라면 각각의 μ•Œλ¦Όλ“€μ„ que배열에 μ €μž₯ν•΄ λ‘μ—ˆλ‹€κ°€, νŠΉμ • μ‹œκ°„ 이내에 μƒˆλ‘œμš΄ μš”μ²­μ΄ μ—†λ‹€λ©΄ 일괄 μ²˜λ¦¬ν•΄μ£ΌλŠ” 방식을 μ‚¬μš©ν•˜μ˜€λ‹€.

export function NotificationHandler(createNotification) {
  let notificationWorks = []
  let timeout = null

  this.requestPermission = async () => {
    const permission = await Notification.requestPermission(res => res)

    if (permission === 'granted')
      return new Notification('μ•Œλ¦Όμ΄ ν—ˆμš©λ˜μ—ˆμŠ΅λ‹ˆλ‹€.')
  }

  this.checkPermission = () => {
    const permission = Notification.permission
    if (permission === 'granted') return true
    if (permission === 'default') return false
    if (permission === 'denied') return false
  }

  this.removeTimeout = () => clearTimeout(timeout)

  this.activeNotification = data => {
    // κΆŒν•œ μŠΉμΈμƒνƒœκ°€ μ•„λ‹ˆλ©΄ μ’…λ£Œ
    if (!this.checkPermission()) return

    this.removeTimeout()
    notificationWorks.push(data)

    timeout = setTimeout(() => {
      const { title, option } = createNotification(notificationWorks)
      notificationWorks = []
    }, 300)
  }
}

λ˜ν•œ μ „λ‹¬ν•˜κ²Œ 될 μ•Œλ¦Όμ— λŒ€ν•œ data듀을 κ³„μ‚°ν•˜λŠ” 방식은 λͺ¨λ‘ λ‹€λ₯Ό 수 있기 λ•Œλ¬Έμ—, ν•¨μˆ˜λ₯Ό μƒμ„±ν•˜κΈ° μ „ 전달해주도둝 ν•˜μ˜€λ‹€.

React의 νŠΉμ„±μƒ λ©”λͺ¨μ΄μ œμ΄μ…˜ λ˜μ–΄μžˆκΈ° λ•Œλ¬Έμ—, μ ˆλŒ€λ‘œ notification은 μž¬μƒμ„±λ˜μ§€ μ•ŠλŠ”λ‹€.

const notification = useMemo(
  () => new NotificationHandler(계산 ν•¨μˆ˜),
  [createNotification]
)

ν…ŒμŠ€νŠΈ

잘 μž‘λ™λ˜λŠ”μ§€ ν™•μΈν•΄λ³΄μž

1

μ‚¬μš©μžμ—κ²Œ λ¨Όμ € κΆŒν•œμ„ μš”μ²­ν•˜λŠ” μ•Œλ¦Όμ΄ κ°„λ‹€.

단, μ‚¬μš©μžμ˜ κΆŒν•œ μƒνƒœκ°€ dennied라면 μš”μ²­μ΄ μ „λ‹¬λ˜μ§€ μ•ŠλŠ”λ‹€.

2

μš”μ²­μ„ μŠΉμΈν•˜κ³ , μš”μ²­μ„ μŠΉμΈν–ˆλ‹€λŠ” 첫 μ•Œλ¦Όμ΄ μ „λ‹¬λœ λͺ¨μŠ΅μ΄λ‹€.

3

타이머가 적용된 μ»¨ν…μΈ μ˜ νŠΉμ • μ‹œκ°„μ— λ„λ‹¬ν•˜μž, μ—¬λŸ¬ μš”μ²­λ“€μ„ ν•˜λ‚˜λ‘œ λ³‘ν•©ν•˜μ—¬ μ•Œλ¦Όμ„ μ „λ‹¬ν•œ λͺ¨μŠ΅μ΄λ‹€.

κ³ λ―Ό

μœ„μ˜ μƒν™©μ—μ„œλŠ” μ—¬λŸ¬ μš”μ²­μ„ ν•˜λ‚˜λ‘œ 병합해주기 μœ„ν•΄ 0.3μ΄ˆλΌλŠ” μ‹œκ°„ 뒀에 μ•Œλ¦Όμ΄ μ „λ‹¬λ˜λ„λ‘ ν•˜μ˜€λ‹€.

λ§Œμ•½, Web Chat처럼 μ‹€μ‹œκ°„μ΄ μ€‘μš”ν•˜κ³ , λͺ¨λ“  μš”μ²­λ“€μ΄ κ°κ°μ „λ‹¬λ˜μ–΄μ•Ό ν•œλ‹€λ©΄?

μΉ΄μΉ΄μ˜€ν†‘μ²˜λŸΌ

μ•Œλ¦Ό.close.bind(μ•Œλ¦Ό)

기본적으둜 μ „λ‹¬λœ μ•Œλ¦Όμ€ λΈŒλΌμš°μ €μ—μ„œ 기본적으둜 μ„€μ •ν•œ μ‹œκ°„ 이후에 μžλ™μœΌλ‘œ 사라진닀.

λ§Œμ•½, μš”μ²­μ΄ μ€‘μ²©λ˜μ–΄μžˆλ‹€λ©΄ 사라진 λ‹€μŒμ— λ‹€μŒ μš”μ²­μ΄ μ•Œλ¦ΌμœΌλ‘œ μƒμ„±λœλ‹€.

μœ„μ˜ closeλ©”μ†Œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒμ„±λœ μ•Œλ¦Όμ„ μ¦‰μ‹œ 사라지도둝 ν•  수 μžˆλŠ”λ°, 이 방법을 μ‚¬μš©ν•˜μ—¬ 이전 μ•Œλ¦Όμ„ ν•˜λ‚˜μ˜ λ³€μˆ˜μ— 캐싱해두고, μƒˆλ‘œμš΄ μš”μ²­μ΄ λ“€μ–΄μ˜¬ λ•Œ 이전 μ•Œλ¦Όμ„ closeν•΄μ£ΌλŠ” λ°©μ‹μœΌλ‘œ μ²˜λ¦¬ν•΄μ£Όλ©΄ 될 것 κ°™λ‹€.

비ꡐ적 μ΅œκ·Όμ—λŠ” λΈŒλΌμš°μ €κ°€ ν™œμ„±ν™” λ˜μ–΄μžˆμ§€ μ•Šλ”λΌλ„, μ•Œλ¦Όμ„ 전달해 쀄 수 μžˆλŠ” Web Push APIκ°€ 생겼닀고 ν•œλ‹€.

마치 λͺ¨λ°”μΌμ˜ μ•Œλ¦Όμ²˜λŸΌ

ν•˜μ§€λ§Œ, ν•΄λ‹Ή APIλŠ” IOS의 κΈ°λ³Έ λΈŒλΌμš°μ €μΈ Safariμ—μ„œλŠ” μ œκ³΅ν•˜μ§€ μ•Šκ² λ‹€κ³  선을 그은 API라고 ν•œλ‹€.

λΈŒλΌμš°μ €μ—μ„œ nativeμ—μ„œλ§Œ κ΅¬ν˜„ν•  수 μžˆλŠ” 방법듀이 계속 μƒκ²¨λ‚˜κ²Œ 되면 nativeκ°œλ°œμžλ“€μ˜ μž…μ§€κ°€ 쀄어듀지 μ•Šμ„κΉŒ..? λΌλŠ” 점

μ•„.. μΆ©λΆ„νžˆ 이해가 κ°€λŠ” λ¬Έμ œμ΄λ‹€.


@SangMin
πŸ‘† H'e'story

πŸš€GitHub