# Clerk Setup — Naturalness Review Page

**페이지**: `https://face.sds.ggls.dev/tools/blendshape-player-natural.html`
**Clerk 인스턴스**: `strong-coyote-51.clerk.accounts.dev` (dev, key prefix `pk_test_`)
**Last verified**: 2026-05-26

---

## 인프라 (이미 구성됨, 변경 불필요)

| Layer | What | Where |
|---|---|---|
| 정적 서버 | `python3 -m http.server 15156 --bind 127.0.0.1` | cwd = `package/face/animasync-face-v3/`, PID 13853 (4일째 가동) |
| Cloudflare Tunnel | `face.sds.ggls.dev` → `localhost:15156` | systemd unit `cloudflared.service`, config `/etc/cloudflared/config.yml` |
| 공개 URL | `https://face.sds.ggls.dev/tools/blendshape-player-natural.html` | HTTP 200 확인됨 |

> 서버나 터널을 재시작할 일이 거의 없지만, 자산을 추가하면 `package/face/animasync-face-v3/` 안에 두기만 하면 `/tools/...` 또는 `/avatar/...` 식으로 즉시 서빙됨.

---

## Clerk 대시보드 — 리뷰 발송 전 점검 항목

대시보드: https://dashboard.clerk.com/ → 해당 인스턴스 선택

### 1. Domains / Allowed Origins
**Configure → Domains** (또는 **API Keys** 옆 Domains)

다음이 등록되어 있어야 함:
- `https://face.sds.ggls.dev`
- (옵션) `http://localhost:15156` — 본인 로컬 테스트용

> dev 인스턴스는 기본적으로 `localhost`만 허용. `face.sds.ggls.dev`가 빠져 있으면 페이지가 Clerk SDK는 로드해도 `mountSignIn`/`mountSignUp`가 origin mismatch로 멈춤 → **리뷰어가 봤다는 "로그인 화면에서 더 진행 안 됨" 증상의 1순위 원인.**

### 2. Paths
**Configure → Paths**

- Sign-in URL: `/tools/blendshape-player-natural.html`
- Sign-up URL: `/tools/blendshape-player-natural.html` (같은 경로 — 페이지가 ticket 유무로 둘을 자동 분기)
- After sign-in URL: 비워두거나 위와 동일
- After sign-up URL: 비워두거나 위와 동일

> 코드에서 `signInForceRedirectUrl` / `signUpForceRedirectUrl`을 직접 넘기므로 대시보드 값은 fallback 역할만 함.

### 3. Restrictions (allowlist)
**Configure → Restrictions** → **Allowlist** 활성화

초대된 이메일만 가입할 수 있도록 allowlist에 리뷰어 이메일을 추가. 활성화하지 않으면 누구나 이메일만으로 가입 가능 — 사내 검토 목적이라면 켜둘 것.

### 4. Invitations 발송
**User & Authentication → Invitations → Invite**

- Email: 리뷰어 이메일
- **Redirect URL**: `https://face.sds.ggls.dev/tools/blendshape-player-natural.html` ← 반드시 명시
- Public metadata 등은 비워도 됨

리뷰어가 받은 이메일의 "Accept invitation" 링크는 다음 형태:
```
https://face.sds.ggls.dev/tools/blendshape-player-natural.html?__clerk_ticket=<JWT>&__clerk_status=...
```

페이지 코드(`__startClerkGate`)가 이 `__clerk_ticket` 쿼리를 감지해 자동으로 `mountSignUp`을 띄우고 ticket을 consume함.

---

## 트러블슈팅 (리뷰어가 막혔다고 할 때)

리뷰어한테 **반드시 브라우저 콘솔 로그**를 받아야 함 (Chrome: F12 → Console). 다음 키워드로 원인 매칭:

| 콘솔에 보이는 메시지 | 원인 | 조치 |
|---|---|---|
| `Cross-Origin Request Blocked` / `unauthorized` | Allowed Origins에 도메인 없음 | 위 #1 |
| `Clerk: This domain is not allowed` | 같음 | 위 #1 |
| `Invalid ticket` / `Ticket has been used` | 초대 링크 만료 또는 재사용 | 새 invitation 발송 |
| `User not found` (mountSignIn 모드일 때) | ticket 없는 URL로 접근 + 미가입자 | 초대 링크로 다시 접근하라고 안내 |
| `[clerk] mount failed:` | SDK 로드 실패 또는 mount 함수 미존재 | SDK 버전 확인 (현재 v5) |

---

## 코드 참조

- 인증 게이트: `tools/blendshape-player-natural.html` Line 778~ (`window.__startClerkGate`)
- ticket 분기: `mountSignUp` vs `mountSignIn` 선택 로직 — 같은 함수 안

코드 수정 시 핵심 invariant:
1. `__clerk_ticket` 쿼리가 있으면 **반드시** `mountSignUp` (SignIn은 신규 사용자를 못 받음)
2. `signInForceRedirectUrl` / `signUpForceRedirectUrl`은 `cleanReturnUrl()`로 Clerk 쿼리 제거된 URL 사용 (안 그러면 무한 ticket consume 루프 가능)
