Files
2nd/10_Wiki/Topics/Architecture/Alliance_(동맹).md
T
Antigravity Agent f8b21af4be Wiki cleanup: error-doc removal, dedup merge, link normalization
10_Wiki/Topics 대규모 정리:
- 오류 캡처/미완성 stub 문서 227개 제거
- 교차폴더 중복 43클러스터 병합 (63파일 → redirect)
- 링크명 정규화: 깨진 링크 수정·redirect 직결·개념 매핑 ~2,400건
- 카테고리 MOC 6개 신규 생성
- Graph 섹션 미해결 related-keyword 링크 10,058건 제거

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 23:52:15 +09:00

6.5 KiB
Raw Blame History

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, tags, raw_sources, last_reinforced, github_commit, tech_stack
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status tags raw_sources last_reinforced github_commit tech_stack
wiki-2026-0508-alliance-동맹 Alliance (동맹) 10_Wiki/Topics verified self
Guild
Clan
Faction
동맹
none A 0.9 applied
game-design
social-systems
mmo
architecture
2026-05-10 pending
language framework
typescript Game backend (Colyseus, Nakama, custom)

Alliance (동맹)

매 한 줄

"매 player-formed group — shared goals, shared resources, shared identity". 매 MMO/SLG 의 retention 핵심 system. 매 EverQuest guild (1999) → World of Warcraft guild (2004) → Lords Mobile/Rise of Kingdoms 동맹 (2017+). 매 2026 modern SLG (4X/RTS hybrid) 의 core loop driver — solo player retention < 7 days, alliance member retention > 90 days 의 typical metric.

매 핵심

매 구조

  • Membership tier: Leader / Officers (R4/R5) / Members / Recruits.
  • State: roster, treasury, buff inventory, war declarations, territory.
  • Permissions: hierarchical RBAC — invite/kick/promote/demote/disband.
  • Lifecycle: create → recruit → grow → war → decline → disband.

매 server-authoritative invariants

  • 매 single alliance per player 의 enforcement (atomic).
  • Member cap (typical 50100) — atomic check-and-insert.
  • Treasury balance — race-free debit/credit (transactional).
  • War state machine — pending/active/peace transitions.

매 응용

  1. SLG 4X game (Lords Mobile pattern) — alliance buffs, rallies, KvK.
  2. MMO guild (WoW pattern) — guild bank, calendar, perk levels.
  3. Mobile RPG clan (Clash of Clans pattern) — clan wars, donations.
  4. Social fitness app (Strava clubs) — challenges, leaderboards.

💻 패턴

Schema (Postgres)

CREATE TABLE alliances (
  id BIGSERIAL PRIMARY KEY,
  tag VARCHAR(5) UNIQUE NOT NULL,
  name VARCHAR(40) NOT NULL,
  leader_id BIGINT NOT NULL REFERENCES players(id),
  member_cap SMALLINT NOT NULL DEFAULT 50,
  treasury_gold BIGINT NOT NULL DEFAULT 0,
  created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE TABLE alliance_members (
  player_id BIGINT PRIMARY KEY REFERENCES players(id),
  alliance_id BIGINT NOT NULL REFERENCES alliances(id) ON DELETE CASCADE,
  rank SMALLINT NOT NULL,  -- 1=member..5=leader
  joined_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX ON alliance_members (alliance_id);

Atomic join (server)

async function joinAlliance(playerId: bigint, allianceId: bigint) {
  return await db.tx(async (t) => {
    // 1. Player must not be in any alliance
    const existing = await t.oneOrNone(
      "SELECT 1 FROM alliance_members WHERE player_id=$1 FOR UPDATE", [playerId]);
    if (existing) throw new Error("ALREADY_IN_ALLIANCE");

    // 2. Member cap check (lock alliance row)
    const a = await t.one(
      "SELECT member_cap, (SELECT COUNT(*) FROM alliance_members WHERE alliance_id=$1)::int AS n " +
      "FROM alliances WHERE id=$1 FOR UPDATE", [allianceId]);
    if (a.n >= a.member_cap) throw new Error("ALLIANCE_FULL");

    await t.none(
      "INSERT INTO alliance_members(player_id, alliance_id, rank) VALUES($1,$2,1)",
      [playerId, allianceId]);
  });
}

Permission check

const PERMS = {
  invite: 2,    // R2+
  kick:   3,    // R3+
  promote:4,    // R4+
  disband:5,    // leader only
} as const;

function can(memberRank: number, action: keyof typeof PERMS): boolean {
  return memberRank >= PERMS[action];
}

Alliance chat (Redis pub/sub)

// Publish
await redis.publish(`alliance:${allianceId}:chat`,
  JSON.stringify({ from: playerId, msg, ts: Date.now() }));

// Subscribe (per connected client)
const sub = redis.duplicate();
await sub.subscribe(`alliance:${allianceId}:chat`, (raw) => {
  ws.send(raw);
});

War declaration state machine

type WarState = "PEACE" | "PENDING" | "ACTIVE" | "COOLDOWN";

const transitions: Record<WarState, WarState[]> = {
  PEACE:    ["PENDING"],
  PENDING:  ["ACTIVE", "PEACE"],
  ACTIVE:   ["COOLDOWN"],
  COOLDOWN: ["PEACE"],
};

function transition(from: WarState, to: WarState) {
  if (!transitions[from].includes(to))
    throw new Error(`INVALID_TRANSITION ${from}->${to}`);
}

Treasury (idempotent donation)

async function donate(playerId: bigint, amt: bigint, idemKey: string) {
  await db.tx(async (t) => {
    const dup = await t.oneOrNone(
      "SELECT 1 FROM idempotency WHERE key=$1", [idemKey]);
    if (dup) return;
    await t.none("INSERT INTO idempotency(key) VALUES($1)", [idemKey]);
    await t.none("UPDATE players SET gold = gold - $2 WHERE id=$1 AND gold >= $2", [playerId, amt]);
    await t.none("UPDATE alliances SET treasury_gold = treasury_gold + $2 WHERE id=$1", [allianceId, amt]);
  });
}

Member roster cache invalidation

async function onMembershipChange(allianceId: bigint) {
  await redis.del(`alliance:${allianceId}:roster`);
  await redis.publish(`alliance:${allianceId}:events`, JSON.stringify({type:"ROSTER_CHANGED"}));
}

매 결정 기준

상황 Approach
Casual mobile, < 30 members Single-shard SQL, simple roster
MMO, 100+ members, real-time chat Sharded SQL + Redis pub/sub
Cross-server alliance war (KvK) Event-sourced log + global service
Persistent territory control Server-authoritative grid + alliance ownership

기본값: 매 Postgres alliance/member tables + Redis pub/sub for chat/presence + idempotent treasury operations.

🔗 Graph

🤖 LLM 활용

언제: 매 long-session retention 의 game (MMO, SLG, persistent world), 매 social cooperation 의 core mechanic. 언제 X: 매 short-session arcade, 매 strict-PvP only without cooperation, 매 < 1k DAU 의 single-player feel.

안티패턴

  • Client-authoritative membership: 매 cheat 의 trivial (forge join). 매 server-authoritative 만.
  • No member cap: 매 mega-alliance dominance — 매 game balance 의 destruction.
  • Synchronous broadcast: 매 large alliance (500+) 의 chat fan-out blocks. 매 async pub/sub 의 사용.
  • Disband without grace period: 매 leader 의 grief vector. 매 24h cooldown.

🧪 검증 / 중복

  • Verified (Lords Mobile design, EVE Online corporation system, WoW guild system, Clash of Clans clan system).
  • 신뢰도 A.

🕓 Changelog

날짜 변경
2026-05-08 Phase 1
2026-05-10 Manual cleanup — full content (game alliance system architecture)