---
id: web-web-locks-api
title: Web Locks API — tab 간 mutex
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [web, lock, vibe-coding]
tech_stack: { language: "TS", applicable_to: ["Frontend"] }
applied_in: []
aliases: [Web Locks, navigator.locks, tab synchronization, broadcast channel, mutex, exclusive lock]
---

# Web Locks API

> 같은 origin 의 여러 tab 가 race condition. **`navigator.locks` 가 mutex**. Sync between tabs / workers. Modern browsers.

## 📖 핵심 개념
- 같은 origin 의 다른 tab / worker / iframe 사이.
- Exclusive (1) 또는 shared (read).
- 매 lock 가 자동 release (tab close).
- `BroadcastChannel` 와 함께 사용.

## 💻 코드 패턴

### Exclusive lock
```ts
await navigator.locks.request('my-resource', async (lock) => {
  // 매 origin 에서 1 곳 만.
  await criticalSection();
});
// → Lock 자동 release.
```

### Shared lock (read)
```ts
await navigator.locks.request('config', { mode: 'shared' }, async (lock) => {
  // 여러 tab 가 동시 read.
});

await navigator.locks.request('config', { mode: 'exclusive' }, async (lock) => {
  // Write — shared 가 wait.
});
```

→ Reader / writer.

### Use case: leader election
```ts
// 1 tab 만 가 background work
let isLeader = false;

navigator.locks.request('leader', { mode: 'exclusive' }, async () => {
  isLeader = true;
  while (isLeader) {
    await pollServer();
    await new Promise(r => setTimeout(r, 5000));
  }
});

window.addEventListener('beforeunload', () => {
  isLeader = false;
});
```

→ 새 tab 가 lock = leader.

→ "Service Worker 가 leader" 의 alternative.

### TryLock (안 wait)
```ts
const result = await navigator.locks.request('resource', { ifAvailable: true }, async (lock) => {
  if (!lock) return 'busy';
  // ...
  return 'done';
});
```

→ `ifAvailable: true` = 잠겼으면 lock=null + callback 즉시.

### Timeout (own logic)
```ts
async function lockWithTimeout(name: string, ms: number, fn: () => Promise<void>) {
  const ac = new AbortController();
  setTimeout(() => ac.abort(), ms);
  
  await navigator.locks.request(name, { signal: ac.signal }, fn);
}
```

→ N ms 안 lock 못 = AbortError.

### Query state
```ts
const { held, pending } = await navigator.locks.query();
console.log('Held locks:', held.map(l => l.name));
console.log('Pending:', pending.map(l => l.name));
```

→ Debug.

### vs SharedWorker
```
SharedWorker: 같은 origin tab 가 공유 worker.
Web Locks: 작은 mutex.

→ 둘 다 사용 가능.
SharedWorker = 큰 logic 공유.
Web Locks = small lock.
```

### vs BroadcastChannel
```
BroadcastChannel: tab 간 message.
Web Locks: tab 간 lock.

→ 함께 사용 흔함.
```

```ts
const channel = new BroadcastChannel('updates');
channel.postMessage({ type: 'config-changed' });

// Other tab
channel.onmessage = (e) => {
  if (e.data.type === 'config-changed') {
    reloadConfig();
  }
};
```

### Use case: IndexedDB write coordinate
```ts
// 여러 tab 가 같은 data 변경 → conflict.
async function updateConfig(newValue) {
  await navigator.locks.request('config-write', async () => {
    const tx = db.transaction('config', 'readwrite');
    await tx.objectStore('config').put(newValue);
  });
}
```

### Service Worker + Web Locks
```ts
// Service worker
self.addEventListener('fetch', async (event) => {
  await navigator.locks.request('cache-write', async () => {
    const cache = await caches.open('v1');
    cache.put(event.request, response);
  });
});
```

→ Cache 가 tab 간 + worker 사이 안전.

### 함정
```
- Tab 간 만 (cross-origin X).
- Worker 도 같은 origin.
- Indexed DB transaction 가 별도 lock (혼동 X).
```

### Browser support
```
Chrome 69+, Edge 79+, FF 96+, Safari 15.4+.
→ 모든 modern.
```

### Polyfill 가 어려움
```
Cross-tab lock 가 native 만 신뢰.
Polyfill = localStorage 로 가능 (race condition 위험).
```

### Use case 모음
```
1. Leader election (single tab background work).
2. IndexedDB write serialize.
3. Cache 정리 (1 tab 만).
4. WebSocket 1 connection share.
5. Background sync coordinate.
```

### Single WebSocket pattern
```ts
async function connectIfLeader() {
  await navigator.locks.request('ws-connection', async () => {
    const ws = new WebSocket('wss://...');
    // ws → BroadcastChannel 가 다른 tab 에 전달
    ws.onmessage = (e) => {
      bc.postMessage({ type: 'message', data: e.data });
    };
    
    // Hold lock — tab close = release
    await new Promise(() => {});
  });
}
```

→ 1 WS, N tab 공유.

### Lock 의 ordering
```
First-come, first-served (queue).
- Tab A request → wait.
- Tab B request → wait B.
- Tab C request → wait C.
- A 끝 → B 시작 → 등.
```

### Re-entrancy 안 됨
```ts
await navigator.locks.request('x', async () => {
  // ❌ 재 lock — deadlock.
  await navigator.locks.request('x', async () => {});
});
```

→ Promise pending forever.

### 일반 mutex 와 차이
```
일반 mutex: 같은 process.
Web Locks: cross-tab (다른 process).

→ Browser 가 origin-scoped lock manager.
```

### Auto-release
```
Tab close, navigation, crash:
→ Lock 자동 release.

⚠️ 매 lock 가 finite life — 무한 hang X.
```

### Production tip
```
- Lock 안 work 가 빠름 (다른 tab block).
- Long-running = SharedWorker 가 더 좋음.
- Critical path 만 lock.
- Debug 로 query() 활용.
```

### LiveBlocks / PartyKit (alternative)
```
Cross-tab sync = Web Locks 의 idea + cross-device.

→ Different problem (tab 가 아닌 user).
```

### 함정: Worker 안 사용
```ts
// Worker 안
self.onmessage = async (e) => {
  await navigator.locks.request('x', async () => {
    // OK — worker 도 same-origin lock 참여
  });
};
```

### Same-site / origin / Tab 이해
```
example.com / about.html / 가 lock 공유.
example.com / sub.html / 도 공유.
sub.example.com 는 안 (다른 origin).
```

### Storage event (옛 방법)
```ts
// localStorage 변경 = 다른 tab 가 'storage' event.
// → Pseudo-lock (race condition 위험).

window.addEventListener('storage', (e) => {
  if (e.key === 'lock-X' && e.newValue === 'taken') {
    // ...
  }
});
```

→ Web Locks 가 modern + 안전.

## 🤔 의사결정 기준
| 작업 | 추천 |
|---|---|
| Tab 간 mutex | Web Locks |
| 1 tab 만 background | Leader election |
| Tab 간 message | BroadcastChannel |
| Logic 공유 | SharedWorker |
| Try lock | `ifAvailable: true` |
| Timeout | AbortController + signal |
| Read/write | shared / exclusive mode |

## ❌ 안티패턴
- **localStorage 로 fake lock**: race.
- **Cross-origin lock 가정**: 안 됨.
- **Re-entrant lock**: deadlock.
- **Long work in lock**: 다른 tab freeze.
- **Auto-release 무시**: leak 가정.
- **Web Locks 만 + sync 없음**: BroadcastChannel 함께.

## 🤖 LLM 활용 힌트
- Web Locks 가 cross-tab mutex 의 답.
- Leader election + BroadcastChannel 가 SharedWorker 의 작은 alternative.
- Auto-release 가 큰 강점.
- Browser support 가 좋음 (modern).

## 🔗 관련 문서
- [[Web_Service_Worker_Patterns]]
- [[Web_PWA_Service_Worker]]
