μ—¬λŸ¬ μ„œλΉ„μŠ€κ°€ ν•¨κ»˜ μ“°λŠ” λŒ€κΈ°μ—΄ ν΄λΌμ΄μ–ΈνŠΈ μ„€κ³„ν•˜κΈ°

sumin
  • #vwr
  • #react

λ“€μ–΄κ°€λ©°

μ•ˆλ…•ν•˜μ„Έμš”. λ…Έλ¨ΈμŠ€ ν”„λ‘ νŠΈμ—”λ“œ 개발자 μ§€μˆ˜λ―Όμž…λ‹ˆλ‹€.

fromm μŠ€ν† μ–΄λŠ” μƒν’ˆ μ˜€ν”ˆμ΄λ‚˜ 이벀트 μ‹ μ²­μ²˜λŸΌ νŠΉμ • μ‹œκ°„μ— μ‚¬μš©μžκ°€ λͺ°λ¦΄ λ•Œ, μ‚¬μš©μžλ₯Ό 쀄 μ„Έμ› λ‹€ μˆœμ„œλŒ€λ‘œ μž…μž₯μ‹œν‚€λŠ” λŒ€κΈ°μ—΄ μ‹œμŠ€ν…œ(Virtual Waiting Room, μ΄ν•˜ VWR)을 μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. λŒ€κΈ°μ—΄μ„ ν•„μš”λ‘œ ν•˜λŠ” μ„œλΉ„μŠ€κ°€ λŠ˜μ–΄λ‚˜λ©΄μ„œ, 사내 GitHub Packages(npm)에 νΌλΈ”λ¦¬μ‹œν•΄ 각 μ„œλΉ„μŠ€μ—μ„œ κ³΅ν†΅μœΌλ‘œ μ„€μΉ˜ν•΄ μ“°λŠ” νŒ¨ν‚€μ§€(vwr-client2)둜 κ°œλ°œν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

이 κΈ€μ—μ„œλŠ” 이 νŒ¨ν‚€μ§€λ₯Ό λ§Œλ“  μ΄μœ μ™€ 섀계 방식을 κ³΅μœ λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€.

VWR Client의 μ£Όμš” κΈ°λŠ₯

VWR ClientλŠ” λŒ€κΈ°μ—΄ 등둝뢀터 폴링(μƒνƒœ 확인), μž…μž₯, μ„Έμ…˜ μ’…λ£ŒκΉŒμ§€μ˜ 흐름을 λ§‘κ³  μžˆμŠ΅λ‹ˆλ‹€.

  1. λŒ€κΈ°μ—΄ 등둝: POST /registerλ₯Ό 톡해 λŒ€κΈ°μ—΄μ— μ§„μž…ν•©λ‹ˆλ‹€.
  2. μƒνƒœ 폴링: POST /polling/{visitorId}둜 μˆœλ²ˆμ„ ν™•μΈν•˜λ©° μƒνƒœλ₯Ό κ°±μ‹ ν•©λ‹ˆλ‹€ (pending β†’ waiting β†’ entered).
  3. μž…μž₯ 및 이용: μˆœμ„œκ°€ 되면 μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν•©λ‹ˆλ‹€.
  4. μ„Έμ…˜ μ’…λ£Œ: 이용이 λλ‚˜λ©΄ 폴링을 μ€‘λ‹¨ν•˜κ³  POST /endλ₯Ό ν˜ΈμΆœν•΄ 자리λ₯Ό λ°˜λ‚©ν•©λ‹ˆλ‹€ (ended).
register                POST /register
   β–Ό
폴링 루프                POST /polling/{visitorId}
   pending β†’ waiting β†’ entered
   β–Ό
end()                   폴링 쀑단 β†’ POST /end β†’ ended

λ°°κ²½

κΈ°μ‘΄ ν΄λΌμ΄μ–ΈνŠΈμ™€ ν•œκ³„

κΈ°μ‘΄ ν΄λΌμ΄μ–ΈνŠΈ(vwr-client)도 μ§€κΈˆκ³Ό 같은 core β†’ react κ΅¬μ‘°μ˜€μŠ΅λ‹ˆλ‹€. coreκ°€ λŒ€κΈ°μ—΄ λ‘œμ§μ„ λ‹΄λ‹Ήν•˜κ³ , react νŒ¨ν‚€μ§€κ°€ κ·Έ μœ„μ— 훅을 μ œκ³΅ν•˜λŠ” ν˜•νƒœμž…λ‹ˆλ‹€.

ν•˜μ§€λ§Œ VWR을 μ‚¬μš©ν•˜λŠ” 곳이 λŠ˜μ–΄λ‚˜λ©΄μ„œ ν•œκ³„κ°€ λ“œλŸ¬λ‚¬μŠ΅λ‹ˆλ‹€. ν˜ΈμΆœν•˜λŠ” ν™”λ©΄λ§ˆλ‹€ ν•„μš”ν•œ λ™μž‘μ΄ λ‹¬λžλŠ”λ°, κΈ°μ‘΄ react νŒ¨ν‚€μ§€μ˜ ν•¨μˆ˜λ§ŒμœΌλ‘œλŠ” 이λ₯Ό λŒ€μ‘ν•˜κΈ° μ–΄λ €μ› κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. κ²°κ΅­ 같은 React μ„œλΉ„μŠ€ μ•ˆμ—μ„œλ„ 각자 coreλ₯Ό 직접 μ΄μš©ν•΄ μ»€μŠ€ν…€ κ΅¬ν˜„μ„ ν•˜κ²Œ λ˜μ—ˆκ³ , coreλŠ” 둜직, reactλŠ” 곡톡 ν›… μ΄λΌλŠ” μ• μ΄ˆμ˜ 섀계 λͺ©μ λŒ€λ‘œ 쓰이지 λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.

μƒˆλ‘œ λ§Œλ“  이유

κ΅¬μ‘°λŠ” κΈ°μ‘΄κ³Ό λ™μΌν•˜κ²Œ core β†’ reactλ₯Ό μœ μ§€ν•˜λ˜, λ‚΄μš©μ„ μƒˆλ‘œ κ΅¬μ„±ν•œ μ΄μœ λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • λ°±μ—”λ“œ API 개편: VWR API λ²„μ „μ—…μœΌλ‘œ μ—”λ“œν¬μΈνŠΈ, μƒνƒœ, μž…μž₯ 방식이 달라져 ν”„λ‘ νŠΈμ—”λ“œλ„ λ™μ‹œ λŒ€μ‘μ΄ ν•„μš”ν–ˆμŠ΅λ‹ˆλ‹€. 이번 κΈ°νšŒμ— ν˜Έν™˜ λ ˆμ΄μ–΄ λŒ€μ‹  ν΄λΌμ΄μ–ΈνŠΈλ₯Ό μƒˆλ‘œ λ§Œλ“€κΈ°λ‘œ ν–ˆμŠ΅λ‹ˆλ‹€.
  • 곡톡 νŒ¨ν‚€μ§€ν™”: 프둬 μ„œλΉ„μŠ€μ˜ μŠ€ν† μ–΄μ™€ 채널이 λͺ¨λ‘ React둜 κ°œλ°œλ˜μ–΄ μžˆμ–΄, 두 μ„œλΉ„μŠ€κ°€ κ³΅ν†΅μœΌλ‘œ μ“Έ 수 μžˆλŠ” νŒ¨ν‚€μ§€κ°€ ν•„μš”ν–ˆμŠ΅λ‹ˆλ‹€.
  • μ‚¬μš©μ„± κ°œμ„ : ν›… ν•˜λ‚˜λ‘œ μ–΄λ””μ„œλ‚˜ μ‰½κ²Œ μ“Έ 수 μžˆλ„λ‘ λ§Œλ“€μ–΄, μ„œλΉ„μŠ€λ§ˆλ‹€ λŒ€κΈ°μ—΄ λ‘œμ§μ„ 쀑볡 κ΅¬ν˜„ν•˜λŠ” 일을 μ—†μ• κ³ μž ν–ˆμŠ΅λ‹ˆλ‹€.

ꡬ쑰

νŒ¨ν‚€μ§€λŠ” 역할에 따라 λ‘˜λ‘œ λΆ„λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.

νŒ¨ν‚€μ§€μ—­ν• 
coreμ„œλ²„ API 호좜 및 λŒ€κΈ°μ—΄ μƒνƒœ 관리 (ν”„λ ˆμž„μ›Œν¬Β·λΌμ΄λΈŒλŸ¬λ¦¬μ— μ˜μ‘΄ν•˜μ§€ μ•ŠμŒ, λŸ°νƒ€μž„ μ˜μ‘΄μ„± 0개)
reactcoreλ₯Ό Reactμ—μ„œ μ‰½κ²Œ μ“°κΈ° μœ„ν•œ μ–΄λŒ‘ν„° (VwrProvider, useVwr 제곡)

core: μ„œλ²„ API 호좜과 μƒνƒœ 관리

coreλŠ” register, polling, end μš”μ²­κ³Ό μƒνƒœ λ³€ν™”λ₯Ό λ§‘μŠ΅λ‹ˆλ‹€. Reactλ‚˜ DOM에 μ˜μ‘΄ν•˜μ§€ μ•Šμ•„, λ‚˜μ€‘μ— λ‹€λ₯Έ ν”„λ ˆμž„μ›Œν¬λ‚˜ 라이브러리둜 ν™•μž₯ν•˜λ”λΌλ„ coreλŠ” κ·ΈλŒ€λ‘œ μž¬μ‚¬μš©ν•˜κ³  μ–΄λŒ‘ν„°λ§Œ κ΅μ²΄ν•˜λ©΄ λ©λ‹ˆλ‹€.

// μ‚¬μš© μ˜ˆμ‹œ

const client = createVwrClient({baseUrl});
const session = client.register({roomId, view: 'counter'});
const unsubscribe = session.subscribe(state => render(state));
await session.end();

μ„Έμ…˜μ€ μƒνƒœ 쑰회, ꡬ독(subscribe), μ’…λ£Œ(end) μΈν„°νŽ˜μ΄μŠ€λ§Œ λ…ΈμΆœν•˜λ―€λ‘œ ν”„λ ˆμž„μ›Œν¬λ‚˜ 라이브러리의 μƒνƒœ 관리 방식에 맞좰 μ‰½κ²Œ μ—°κ²°ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μƒνƒœλŠ” μ„œλ²„κ°€ λ‚΄λ €μ£ΌλŠ” κ°’(pending, waiting, entered)에, ν΄λΌμ΄μ–ΈνŠΈκ°€ 화면을 그릴 λ•Œ ν•„μš”ν•œ μƒνƒœκ°’(disconnected, ended, failed)을 μΆ”κ°€ν•΄ μ •μ˜ν–ˆμŠ΅λ‹ˆλ‹€.

μƒνƒœμ˜λ―Έ
pending등둝이 λŒ€κΈ°μ—΄μ— 아직 λ°˜μ˜λ˜μ§€ μ•ŠμŒ
waitingλŒ€κΈ° 쀑 (순번 정보 포함)
enteredμž…μž₯ κ°€λŠ₯
disconnectedμΌμ‹œ μž₯μ• λ‘œ μž¬μ‹œλ„ 쀑
endedend()둜 정상 μ’…λ£Œ
failed였λ₯˜λ‘œ μ’…λ£Œ (errorType으둜 μ—λŸ¬μ²˜λ¦¬)

core의 4κ°€μ§€ λ™μž‘ 원칙:

  1. 폴링 μ£ΌκΈ°λŠ” μ„œλ²„κ°€ κ²°μ •: 폴링 간격은 μ„œλ²„ μ‘λ‹΅μ˜ pollInterval을 λ”°λ¦…λ‹ˆλ‹€.
  2. μž…μž₯ 후에도 폴링 μœ μ§€: 폴링이 μ„œλ²„μ˜ 만료 μ‹œκ°„(TTL)을 μ—°μž₯ν•˜λŠ” 역할도 ν•˜κΈ° λ•Œλ¬Έμ—, μž…μž₯ν•œ 뒀에도 폴링을 λ©ˆμΆ”μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ„Έμ…˜μ„ 끝낼 λ•ŒλŠ” 폴링을 λ¨Όμ € 끊고 /endλ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€.
  3. μΈμŠ€ν„΄μŠ€λ‹Ή 단일 μ„Έμ…˜: 같은 roomId둜 쀑볡 등둝을 λ°©μ§€ν•©λ‹ˆλ‹€. /registerλŠ” μž¬μ‹œλ„ν•˜μ§€ μ•Šλ„λ‘ μ„€κ³„ν–ˆμŠ΅λ‹ˆλ‹€.
  4. μ—λŸ¬ μΌ€μ΄μŠ€λ³„ 처리: μ„œλ²„μ—μ„œ λ‚΄λ €μ£ΌλŠ” μ—λŸ¬ μΌ€μ΄μŠ€λ³„λ‘œ μ²˜λ¦¬ν•©λ‹ˆλ‹€.

react: React μ„œλΉ„μŠ€ ν™˜κ²½μ„ μœ„ν•œ μ–΄λŒ‘ν„°

μ•± λ£¨νŠΈμ— VwrProviderλ₯Ό μ„ μ–Έν•˜κ³  ν•„μš”ν•œ κ³³μ—μ„œ useVwr 훅을 ν˜ΈμΆœν•˜λŠ” ꡬ쑰둜, μ–΄λ””μ„œλ“  같은 λ°©μ‹μœΌλ‘œ λŒ€κΈ°μ—΄μ„ μ œμ–΄ν•  수 μžˆλ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.

react μ‚¬μš© 방법

1. Provider 등둝

import {VwrProvider} from '@knowmerce/vwr-client2-react';

<VwrProvider baseUrl="https://...">{children}</VwrProvider>

baseUrl은 ν•„μˆ˜μ΄λ©° 첫 λ Œλ” κΈ°μ€€μœΌλ‘œ κ³ μ •λ©λ‹ˆλ‹€. ProviderλŠ” useState μ΄ˆκΈ°ν™”λ₯Ό 톡해 ν΄λΌμ΄μ–ΈνŠΈλ₯Ό ν•œ 번만 μƒμ„±ν•˜μ—¬ μ„Έμ…˜ 쀑볡 μ°Έμ—¬λ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€.

2. ν›… 호좜 (roomId, view)

const {state, register, end, clear} = useVwr(roomId, {view: 'counter'});
  • roomId: λŒ€κΈ°μ—΄ λ°© ID (λŒ€κΈ°μ—΄μ΄ μ—†λŠ” μƒν’ˆμ΄λ©΄ undefinedλ₯Ό κ·ΈλŒ€λ‘œ λ„˜κ²¨λ„ 되고, μ΄λ•Œ stateλŠ” null)
  • view: λŒ€κΈ° ν™”λ©΄ μœ ν˜• (counter λ˜λŠ” progress)
viewλŒ€κΈ° 쀑(waiting) μƒνƒœ λ°μ΄ν„°ν™œμš© μ˜ˆμ‹œ
counteraheadCount, totalWaiting”내 μ•žμ— Nλͺ…” λ“± λŒ€κΈ° 인원 ν‘œμ‹œ
progressloadingMetric (μ§„ν–‰λ₯ )인원 수 없이 λ‘œλ”© λ°” ν‘œμ‹œ

3. μƒνƒœμ— λ”°λ₯Έ ν™”λ©΄ λΆ„κΈ°

function PurchaseButton({roomId}: {roomId?: string}) {
    const {state, register, clear} = useVwr(roomId, {view: 'counter'});

    const onClick = () => register();

    useEffect(() => {
        if (state?.status === 'entered') goToOrder();
    }, [state?.status]);

    if (state?.status === 'waiting' && state.view === 'counter') {
        return <Waiting count={state.aheadCount} />;
    }
    if (state?.status === 'failed') {
        return <ErrorView onClose={clear} />;
    }
    return <button onClick={onClick}>κ΅¬λ§€ν•˜κΈ°</button>;
}

4. 이용 μ’…λ£Œ μ‹œ end() 호좜

end()λŠ” 폴링을 μ€‘λ‹¨ν•˜κ³  μ„œλ²„μ— 자리λ₯Ό λ°˜λ‚©ν•©λ‹ˆλ‹€. μ»΄ν¬λ„ŒνŠΈκ°€ μ–Έλ§ˆμš΄νŠΈλ˜μ–΄λ„ μ„Έμ…˜μ€ λλ‚˜μ§€ μ•ŠμœΌλ―€λ‘œ, μ£Όλ¬Έ μ™„λ£Œ μ‹œμ μ΄λ‚˜ ν™”λ©΄ μ΄νƒˆ μ‹œμ μ— 직접 ν˜ΈμΆœν•΄μ•Ό ν•©λ‹ˆλ‹€.

  • register(): κΈ°μ‘΄ ν™œμ„± μ„Έμ…˜ μž¬μ‚¬μš©
  • end(): μ°Έμ—¬ μ’…λ£Œ 및 μƒνƒœλ₯Ό null둜 μ΄ˆκΈ°ν™”
  • clear(): failed μƒνƒœλ₯Ό μ΄ˆκΈ°ν™”

마치며

coreλŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ—λ§Œ μ§‘μ€‘ν•˜κ³ , react νŒ¨ν‚€μ§€λŠ” React 연동 μ—­ν• λ§Œ ν•˜λ„λ‘ λΆ„λ¦¬ν•˜μ—¬ μœ μ§€λ³΄μˆ˜μ„±κ³Ό ν™•μž₯성을 λ†’μ˜€μŠ΅λ‹ˆλ‹€. 곡톡 훅을 μ œκ³΅ν•΄ μ„œλΉ„μŠ€λ§ˆλ‹€ λŒ€κΈ°μ—΄ λ‘œμ§μ„ 쀑볡 κ΅¬ν˜„ν•˜λ˜ 문제λ₯Ό 쀄이고, μΌκ΄€λœ λ°©μ‹μœΌλ‘œ 연동할 수 μžˆλ„λ‘ ν–ˆμŠ΅λ‹ˆλ‹€.

μ‹€μ œ μ„œλΉ„μŠ€ 적용 κ³Όμ •μ—μ„œ 얻은 μΈμ‚¬μ΄νŠΈλŠ” λ‹€μŒ κΈ€μ—μ„œ μ΄μ–΄μ„œ κ³΅μœ ν•˜κ² μŠ΅λ‹ˆλ‹€.

κ°μ‚¬ν•©λ‹ˆλ‹€.

← λͺ©λ‘μœΌλ‘œ λŒμ•„κ°€κΈ°

Art Changes Life

λ…Έλ¨ΈμŠ€μ™€ ν•¨κ»˜ μ—”ν„°ν…Œν¬ 산업을 ν˜μ‹ ν•΄λ‚˜κ°ˆ 멀버λ₯Ό μ°ΎμŠ΅λ‹ˆλ‹€.

μ±„μš© 쀑인 곡고 보기