Member Schema¶
memberschema 는 길드 멤버 상태와 웹 조인 진행 기록을 저장한다.
Schema purpose¶
- PostgreSQL schema —
member - Corresponding domain —
engine/member/ - sqlc package —
db/queries/member/
대규모 길드의 멤버 조회와 Live Sync 배치 갱신을 수용해야 한다. 인덱스 설계가 중요.
Tables¶
member.members¶
길드 멤버 레코드.
CREATE TABLE member.members (
id UUID PRIMARY KEY,
guild_id UUID NOT NULL REFERENCES guild.guilds(id),
discord_user_id TEXT NOT NULL,
user_id UUID REFERENCES identity.users(id),
joined_via TEXT NOT NULL CHECK (joined_via IN ('web_join', 'direct', 'restored')),
joined_at TIMESTAMPTZ NOT NULL,
left_at TIMESTAMPTZ,
role_ids TEXT[] NOT NULL DEFAULT '{}',
nickname TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT members_guild_discord_unique UNIQUE (guild_id, discord_user_id)
);
Columns
| Column | Type | Constraints | Description |
|---|---|---|---|
id | UUID v7 | PK | Umbra 내부 Member ID |
guild_id | UUID | FK, NOT NULL | → guild.guilds.id |
discord_user_id | TEXT | NOT NULL | Discord snowflake |
user_id | UUID | FK nullable | → identity.users.id (OAuth2 완료 시) |
joined_via | TEXT | CHECK | 가입 경로 |
joined_at | TIMESTAMPTZ | NOT NULL | 가입 시각 |
left_at | TIMESTAMPTZ | — | 탈퇴 시각, NULL 이면 재적 |
role_ids | TEXT[] | NOT NULL | Discord role IDs (현재 상태) |
nickname | TEXT | — | 길드 내 닉네임 |
created_at | TIMESTAMPTZ | NOT NULL | Umbra 레코드 생성 |
updated_at | TIMESTAMPTZ | NOT NULL | 마지막 갱신 |
Indexes
| Index | Columns | Purpose |
|---|---|---|
members_pkey | (id) | PK |
members_guild_discord_unique | (guild_id, discord_user_id) | 조회 및 유일성 |
members_active_idx | (guild_id) WHERE left_at IS NULL | 활성 멤버 조회 |
members_user_idx | (user_id) WHERE user_id IS NOT NULL | User 의 가입 길드 목록 |
members_role_ids_gin | (role_ids) USING GIN | 역할별 멤버 쿼리 |
Foreign keys
| Column | References | On delete |
|---|---|---|
guild_id | guild.guilds(id) | RESTRICT |
user_id | identity.users(id) | SET NULL |
Invariants
(guild_id, discord_user_id)유일 — 같은 길드에 같은 Discord 유저 레코드 1개- 재가입 시 기존 row 의
left_atNULL 로 초기화,joined_at갱신 role_ids는 Discord role snowflake 배열
member.web_join_requests¶
웹 조인 진행 상태 추적.
CREATE TABLE member.web_join_requests (
id UUID PRIMARY KEY,
guild_id UUID NOT NULL REFERENCES guild.guilds(id),
discord_user_id TEXT NOT NULL,
state TEXT NOT NULL CHECK (state IN ('pending', 'completed', 'failed')),
encrypted_access_token BYTEA,
attempted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
completed_at TIMESTAMPTZ,
failure_reason TEXT
);
Columns
| Column | Type | Constraints | Description |
|---|---|---|---|
id | UUID v7 | PK | 요청 ID |
guild_id | UUID | FK, NOT NULL | → guild.guilds.id |
discord_user_id | TEXT | NOT NULL | 요청자 Discord snowflake |
state | TEXT | CHECK | pending / completed / failed |
encrypted_access_token | BYTEA | — | AES-256-GCM, completed 시 삭제 |
attempted_at | TIMESTAMPTZ | NOT NULL | 요청 시각 |
completed_at | TIMESTAMPTZ | — | 완료 시각 |
failure_reason | TEXT | — | 실패 상세 |
Indexes
| Index | Columns | Purpose |
|---|---|---|
web_join_requests_pkey | (id) | PK |
web_join_requests_pending_idx | (attempted_at) WHERE state = 'pending' | TTL 처리 cron |
web_join_requests_guild_idx | (guild_id, attempted_at DESC) | 길드별 최근 조회 |
Invariants
state = 'pending'이 1시간 초과면 cron 이failed로 전환 + token 삭제completed또는failed시encrypted_access_tokenNULL 로 설정 (보안)
Relationships¶
erDiagram
MEMBERS }o--|| GUILDS : "belongs to"
MEMBERS }o--o| USERS : "linked to (optional)"
WEB_JOIN_REQUESTS }o--|| GUILDS : "targets"
Cross-schema references¶
| From | To | Semantics |
|---|---|---|
member.members.guild_id | guild.guilds.id | 소속 길드 |
member.members.user_id | identity.users.id | Umbra 계정 연결 (OAuth2) |
member.web_join_requests.guild_id | guild.guilds.id | 요청 대상 |
Query patterns¶
UpsertMember— Live Sync 가 호출MarkMemberLeft— 탈퇴 이벤트GetMemberByGuildAndDiscordID— 특정 멤버 조회ListActiveMembers— 길드 멤버 목록 (페이지네이션)CountActiveMembers— 플랜 한도 체크용InsertWebJoinRequest— 웹 조인 시작CompleteWebJoinRequest— 완료 처리 (token 삭제 포함)ExpireStalePendingRequests— cron 용
Data retention¶
- Members — 영구 보관 (감사 목적), soft leave 로 유지
- Web join requests — 90일 후 완료/실패 기록 삭제 (cron)
- Access tokens — completed 또는 failed 시 즉시 NULL
Migration history¶
| Date | Change | Rationale |
|---|---|---|
| 2026-04-xx | 초기 스키마 | MVP |
See also¶
domain/member.md— Member 도메인data/guild-schema.md— 참조하는 Guilddata/identity-schema.md— 연결된 Userflows/web-join.md— 웹 조인 흐름adr/0020-postgres-schema-per-domain.md— schema 분리