콘텐츠로 이동

Member Schema

member schema 는 길드 멤버 상태와 웹 조인 진행 기록을 저장한다.

Schema purpose

  • PostgreSQL schemamember
  • Corresponding domainengine/member/
  • sqlc packagedb/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_at NULL 로 초기화, 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 또는 failedencrypted_access_token NULL 로 설정 (보안)

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 — 참조하는 Guild
  • data/identity-schema.md — 연결된 User
  • flows/web-join.md — 웹 조인 흐름
  • adr/0020-postgres-schema-per-domain.md — schema 분리