ํฌ์ŠคํŠธ

๐ŸŽฎ RummiArena โ€” ํ”„๋กœ์ ํŠธ ๊ธฐ์ˆ  ๊ฐ€์ด๋“œ

๐ŸŽฎ RummiArena โ€” ํ”„๋กœ์ ํŠธ ๊ธฐ์ˆ  ๊ฐ€์ด๋“œ

๋ฃจ๋ฏธํ๋ธŒ(Rummikub) ๋ณด๋“œ๊ฒŒ์ž„ ๊ธฐ๋ฐ˜ ๋ฉ€ํ‹ฐ LLM ์ „๋žต ์‹คํ—˜ ํ”Œ๋žซํผ
Human + AI ํ˜ผํ•ฉ 2~4์ธ ์‹ค์‹œ๊ฐ„ ๋Œ€์ „ ์ง€์› ยท ๋‹ค์–‘ํ•œ LLM ๋ชจ๋ธ ์ „๋žต ๋น„๊ต/๋ถ„์„


๐Ÿ“‹ ๋ชฉ์ฐจ

  1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”
  2. ์ „์ฒด ์•„ํ‚คํ…์ฒ˜
  3. ๊ธฐ์ˆ  ์Šคํƒ ์ƒ์„ธ ์„ค๋ช…
  4. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ
  5. ๊ฒŒ์ž„ ๊ทœ์น™ & ํƒ€์ผ ์ธ์ฝ”๋”ฉ
  6. AI ์บ๋ฆญํ„ฐ ์‹œ์Šคํ…œ
  7. API ๋ช…์„ธ ๊ฐœ์š”
  8. ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์‹คํ–‰
  9. Kubernetes ๋ฐฐํฌ (Helm)
  10. ํ…Œ์ŠคํŠธ ํ˜„ํ™ฉ

1. ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

RummiArena๋Š” ๊ณ ์ „ ๋ณด๋“œ๊ฒŒ์ž„ ๋ฃจ๋ฏธํ๋ธŒ(Rummikub)๋ฅผ ์˜จ๋ผ์ธ ๋ฉ€ํ‹ฐํ”Œ๋ ˆ์ด ํ™˜๊ฒฝ์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ณ , ์—ฌ๋Ÿฌ LLM(Large Language Model) AI๋ฅผ ํ”Œ๋ ˆ์ด์–ด๋กœ ์ฐธ์—ฌ์‹œ์ผœ ์ „๋žต์„ ๋น„๊ตยท๋ถ„์„ํ•˜๋Š” ์‹คํ—˜ ํ”Œ๋žซํผ์ž…๋‹ˆ๋‹ค.

ํ•ต์‹ฌ ์„ค๊ณ„ ์›์น™

์›์น™์„ค๋ช…
LLM ์‹ ๋ขฐ ๊ธˆ์ง€LLM ์‘๋‹ต์€ ํ•ญ์ƒ Game Engine์œผ๋กœ ์œ ํšจ์„ฑ ๊ฒ€์ฆ. Invalid move โ†’ ์žฌ์š”์ฒญ (์ตœ๋Œ€ 3ํšŒ) โ†’ ์‹คํŒจ ์‹œ ๊ฐ•์ œ ๋“œ๋กœ์šฐ
AI Adapter ๋ถ„๋ฆฌGame Engine์€ ํŠน์ • LLM์— ์˜์กดํ•˜์ง€ ์•Š์Œ. ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค(MoveRequest / MoveResponse)๋กœ ๋ชจ๋ธ ๊ต์ฒด ๊ฐ€๋Šฅ
Stateless ์„œ๋ฒ„๋ชจ๋“  ๊ฒŒ์ž„ ์ƒํƒœ๋Š” Redis์— ์ €์žฅ. Pod ์žฌ์‹œ์ž‘์—๋„ ๊ฒŒ์ž„ ์œ ์ง€
GitOpsArgoCD๊ฐ€ Helm chart ๊ธฐ๋ฐ˜ ๋ฐฐํฌ ๋‹ด๋‹น

2. ์ „์ฒด ์•„ํ‚คํ…์ฒ˜

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                        Client Layer                         โ”‚
โ”‚   Frontend (Next.js 15)          Admin Panel (Next.js)      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                       โ”‚ WebSocket / REST  โ”‚ ๊ด€๋ฆฌ API
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                   Game Server (Go / gin)                     โ”‚
โ”‚   โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚   โ”‚ Game Engine โ”‚  โ”‚  REST API (gin) โ”‚  โ”‚ WebSocket Hub  โ”‚  โ”‚
โ”‚   โ”‚ (๊ทœ์น™ ๊ฒ€์ฆ)  โ”‚  โ”‚                 โ”‚  โ”‚ (์‹ค์‹œ๊ฐ„ ๋™๊ธฐํ™”) โ”‚  โ”‚
โ”‚   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
           โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ–ผ             โ–ผ                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Redis  โ”‚  โ”‚Postgres โ”‚  โ”‚  AI Adapter (NestJS)  โ”‚
โ”‚(๊ฒŒ์ž„   โ”‚  โ”‚(์˜์†    โ”‚  โ”‚  OpenAI / Claude      โ”‚
โ”‚ ์ƒํƒœ)  โ”‚  โ”‚ ๋ฐ์ดํ„ฐ) โ”‚  โ”‚  DeepSeek / Ollama    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

์ธํ”„๋ผ ๊ตฌ์„ฑ

1
2
3
4
5
6
7
8
9
10
11
Docker Desktop Kubernetes
โ””โ”€โ”€ Namespace: rummikub
    โ”œโ”€โ”€ frontend        (Next.js โ€” NodePort 30000)
    โ”œโ”€โ”€ game-server     (Go โ€” NodePort 30080)
    โ”œโ”€โ”€ ai-adapter      (NestJS โ€” NodePort 30081)
    โ”œโ”€โ”€ postgres        (PostgreSQL 16 โ€” NodePort 30432)
    โ””โ”€โ”€ redis           (Redis 7)

CI/CD: GitLab CI + GitLab Runner
GitOps: ArgoCD + Helm 3
Ingress: Traefik v3

3. ๊ธฐ์ˆ  ์Šคํƒ ์ƒ์„ธ ์„ค๋ช…

์ „์ฒด ์Šคํƒ ์š”์•ฝ

๊ณ„์ธต๊ธฐ์ˆ 
FrontendNext.js 15, TailwindCSS, Framer Motion, dnd-kit, Zustand
Backend (game-server)Go 1.24, gin, gorilla/websocket, GORM, zap
Backend (ai-adapter)NestJS, TypeScript, class-validator
DatabasePostgreSQL 16, Redis 7
AI ModelsOpenAI API, Claude API, DeepSeek API, Ollama (LLaMA)
InfraDocker Desktop Kubernetes, Helm 3, ArgoCD, Traefik v3
CI/CDGitLab CI + GitLab Runner
QualitySonarQube, Trivy, OWASP ZAP
AuthGoogle OAuth 2.0 (NextAuth.js)

3.1 Frontend โ€” Next.js 15

๊ฒŒ์ž„ UI์™€ ์‹ค์‹œ๊ฐ„ ์ƒํ˜ธ์ž‘์šฉ์„ ๋‹ด๋‹นํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ ๋ ˆ์ด์–ด์ž…๋‹ˆ๋‹ค.

Next.js 15 (App Router)

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ(RSC): ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„๋ฅผ ๋†’์ด๊ณ  JS ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • ์„œ๋ฒ„ ์•ก์…˜(Server Actions): ํผ ์ œ์ถœ๊ณผ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ์„œ๋ฒ„์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • Partial Prerendering(PPR): ์ •์  ์ฝ˜ํ…์ธ ๋Š” ์ฆ‰์‹œ ์ „๋‹ฌํ•˜๊ณ , ๋™์  ์ฝ˜ํ…์ธ ๋งŒ ์ŠคํŠธ๋ฆฌ๋ฐํ•˜์—ฌ ์ตœ์ƒ์˜ ์„ฑ๋Šฅ์„ ๋ƒ…๋‹ˆ๋‹ค.

TailwindCSS

  • ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋งŒ์œผ๋กœ ๋น ๋ฅด๊ฒŒ ์Šคํƒ€์ผ๋งํ•  ์ˆ˜ ์žˆ์–ด ๋ณ„๋„์˜ CSS ํŒŒ์ผ์ด ๋ถˆํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
  • ๋Ÿฐํƒ€์ž„ JS ์—†์ด ์ •์  CSS๋ฅผ ์ƒ์„ฑํ•˜๋ฏ€๋กœ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ๊ถํ•ฉ์ด ์ข‹์Šต๋‹ˆ๋‹ค.

Framer Motion

  • layout prop ํ•˜๋‚˜๋กœ ํƒ€์ผ์ด ์ด๋™ํ•  ๋•Œ ๋ถ€๋“œ๋Ÿฌ์šด ์Šฌ๋ผ์ด๋”ฉ ํšจ๊ณผ๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
  • layoutId๋ฅผ ํ™œ์šฉํ•ด ํƒ€์ผ ๋“œ๋ž˜๊ทธ ์ข…๋ฃŒ ํ›„ ์ž์—ฐ์Šค๋Ÿฌ์šด ์žฌ๋ฐฐ์น˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ฃผ์˜: ๋ฐ˜๋“œ์‹œ 'use client' ์ปดํฌ๋„ŒํŠธ์—์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

dnd-kit (Drag & Drop)

  • ๋ชจ๋“ˆํ˜• ์„ค๊ณ„๋กœ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ๋งŒ ์„ ํƒํ•ด ๊ฐ€๋ณ๊ฒŒ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ํ‚ค๋ณด๋“œ ์กฐ์ž‘๊ณผ ์Šคํฌ๋ฆฐ ๋ฆฌ๋”๋ฅผ ๊ธฐ๋ณธ ์ง€์›ํ•˜์—ฌ ์ ‘๊ทผ์„ฑ(A11y)์„ ํ™•๋ณดํ•ฉ๋‹ˆ๋‹ค.
  • react-beautiful-dnd๋ณด๋‹ค ์„ฑ๋Šฅ๊ณผ ์œ ์ง€๋ณด์ˆ˜ ๋ฉด์—์„œ ์šฐ์ˆ˜ํ•ฉ๋‹ˆ๋‹ค.

Zustand (์ƒํƒœ ๊ด€๋ฆฌ)

  • Redux๋ณด๋‹ค ํ›จ์”ฌ ๊ฐ€๋ณ๊ณ  ๋ณด์ผ๋Ÿฌํ”Œ๋ ˆ์ดํŠธ๊ฐ€ ์ ์Šต๋‹ˆ๋‹ค.
  • ๋“œ๋ž˜๊ทธ ์ค‘์ธ ํƒ€์ผ ์œ„์น˜, ๋ชจ๋‹ฌ ์ƒํƒœ, ์†ํŒจ(hand tile) ๋ชฉ๋ก ๋“ฑ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • Selector ๊ธฐ๋ฐ˜ ์ ‘๊ทผ์œผ๋กœ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// store/useTaskStore.ts โ€” Zustand + dnd-kit ์กฐํ•ฉ ์˜ˆ์‹œ
import { create } from 'zustand';
import { arrayMove } from '@dnd-kit/sortable';

interface TaskStore {
  tasks: string[];
  reorderTasks: (activeId: string, overId: string) => void;
}

export const useTaskStore = create<TaskStore>((set) => ({
  tasks: ['Task 1', 'Task 2', 'Task 3'],
  reorderTasks: (activeId, overId) => set((state) => {
    const oldIndex = state.tasks.indexOf(activeId);
    const newIndex = state.tasks.indexOf(overId);
    return { tasks: arrayMove(state.tasks, oldIndex, newIndex) };
  }),
}));

ํ”„๋ก ํŠธ์—”๋“œ ์‹œ๋„ˆ์ง€ ํ๋ฆ„

1
2
3
4
5
1. Zustand ์Šคํ† ์–ด์— ํƒ€์ผ ๋ฐฐ์—ด ์ €์žฅ
2. dnd-kit SortableContext๋กœ ํƒ€์ผ ๋ชฉ๋ก ๊ฐ์‹ธ๊ธฐ
3. onDragEnd โ†’ Zustand arrayMove๋กœ ๋ฐฐ์—ด ์ˆœ์„œ ์—…๋ฐ์ดํŠธ
4. Framer Motion layout ์• ๋‹ˆ๋ฉ”์ด์…˜์œผ๋กœ ๋ถ€๋“œ๋Ÿฌ์šด ์žฌ๋ฐฐ์น˜
5. Next.js Server Action์œผ๋กœ ๋ณ€๊ฒฝ ์‚ฌํ•ญ DB์— ์ €์žฅ (useOptimistic ํ™œ์šฉ)

3.2 Backend: Game Server โ€” Go 1.24

์‹ค์‹œ๊ฐ„ ๊ฒŒ์ž„ ๋กœ์ง, WebSocket ํ†ต์‹ , ๊ทœ์น™ ๊ฒ€์ฆ์„ ๋‹ด๋‹นํ•˜๋Š” ํ•ต์‹ฌ ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค.

Go 1.24

  • ๊ณ ๋ฃจํ‹ด(Goroutines): ์ˆ˜๋งŒ ๋ช…์˜ ๋™์‹œ ์ ‘์†์ž๋ฅผ ๊ฐ€๋ฒผ์šด ์Šค๋ ˆ๋“œ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • ๋‚ฎ์€ ์ง€์—ฐ ์‹œ๊ฐ„(Low Latency): ์ปดํŒŒ์ผ ์–ธ์–ด ํŠน์„ฑ์ƒ ์‹คํ–‰ ์†๋„๊ฐ€ ๋น ๋ฅด๋ฉฐ, ๊ฒŒ์ž„ ์„œ๋ฒ„์˜ ํ•ต์‹ฌ ์š”๊ตฌ์‚ฌํ•ญ์ธ ๋น ๋ฅธ ์‘๋‹ต์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.
  • Go 1.24์˜ ์ œ๋„ค๋ฆญ ์„ฑ๋Šฅ ํ–ฅ์ƒ๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์ตœ์ ํ™”๊ฐ€ ๋ฐ˜์˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Gin (HTTP ํ”„๋ ˆ์ž„์›Œํฌ)

  • ๋กœ๊ทธ์ธ, ๋ฐฉ ์ƒ์„ฑ, ํ”„๋กœํ•„ ์กฐํšŒ ๋“ฑ REST API ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋ถˆํ•„์š”ํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ฑฐํ•˜๊ณ  ์†๋„์— ์ง‘์ค‘ํ•œ ๊ฒฝ๋Ÿ‰ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

Gorilla/WebSocket

  • ๊ฒŒ์ž„ ๋‚ด ์ฑ„ํŒ…, ํƒ€์ผ ์ด๋™ ๋™๊ธฐํ™”, ์‹ค์‹œ๊ฐ„ ์ „ํˆฌ ๋“ฑ ์–‘๋ฐฉํ–ฅ ํ†ต์‹ ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.
  • Go ์ƒํƒœ๊ณ„์—์„œ ๊ฐ€์žฅ ์•ˆ์ •์ ์ด๊ณ  ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” WebSocket ๊ตฌํ˜„์ฒด์ž…๋‹ˆ๋‹ค.
  • ๊ณ ๋ฃจํ‹ด๊ณผ ๊ฒฐํ•ฉํ•˜์—ฌ ๋‹ค์ˆ˜์˜ ํด๋ผ์ด์–ธํŠธ๋ฅผ ๋™์‹œ์— ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

GORM (ORM)

  • SQL ์—†์ด Go ๊ตฌ์กฐ์ฒด(Struct)๋กœ DB๋ฅผ ์กฐ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  • ์œ ์ € ๋ฐ์ดํ„ฐ, ์•„์ดํ…œ ์ธ๋ฒคํ† ๋ฆฌ, ๊ฒŒ์ž„ ์ „์  ๋“ฑ์„ PostgreSQL๊ณผ ์—ฐ๋™ํ•ฉ๋‹ˆ๋‹ค.
  • Auto Migration์œผ๋กœ ๋ฐ์ดํ„ฐ ๋ชจ๋ธ ๋ณ€๊ฒฝ ์‹œ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋ฅผ ์ž๋™ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

Zap (๋กœ๊น…)

  • Uber์—์„œ ๊ฐœ๋ฐœํ•œ ์ดˆ๊ณ ์† ๊ตฌ์กฐํ™” ๋กœ๊น… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค.
  • ์ตœ์†Œํ•œ์˜ ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น์œผ๋กœ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜์—ฌ ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • JSON ํ˜•์‹ ์ถœ๋ ฅ์œผ๋กœ ๋Œ€๋Ÿ‰์˜ ๊ฒŒ์ž„ ๋กœ๊ทธ๋ฅผ ์ˆ˜์ง‘ยท๋ถ„์„ํ•˜๊ธฐ ์šฉ์ดํ•ฉ๋‹ˆ๋‹ค.

๋กœ์ปฌ ์‹คํ–‰

1
2
cd src/game-server
go build ./cmd/server && ./server

Go๋Š” ์ปดํŒŒ์ผ ์–ธ์–ด์ด๋ฏ€๋กœ go build๋กœ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•œ ํ›„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.


3.3 Backend: AI Adapter โ€” NestJS

AI ๋ชจ๋ธ(OpenAI, Claude, DeepSeek, Ollama)๊ณผ ๊ฒŒ์ž„ ์„œ๋ฒ„ ์‚ฌ์ด์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ์ค‘๊ฐœํ•˜๋Š” ์„œ๋ฒ„์ž…๋‹ˆ๋‹ค.

NestJS

  • ๋ชจ๋“ˆ/์ปจํŠธ๋กค๋Ÿฌ/์„œ๋น„์Šค ๊ตฌ์กฐ๋กœ ์—ญํ• ์„ ๋ช…ํ™•ํžˆ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
  • AI ์‘๋‹ต ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง€๋Š” ํŠน์„ฑ์— ๊ฐ•ํ•œ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • ํŒ€ ๊ฐœ๋ฐœ๊ณผ ํ”„๋กœ์ ํŠธ ํ™•์žฅ ์‹œ ์ฝ”๋“œ ์ผ๊ด€์„ฑ์„ ์œ ์ง€ํ•˜๊ธฐ ์ข‹์Šต๋‹ˆ๋‹ค.

TypeScript

  • AI ๋ชจ๋ธ์— ๋ณด๋‚ด๋Š” ํ”„๋กฌํ”„ํŠธ์™€ ์‘๋‹ต ๋ฐ์ดํ„ฐ์˜ ํƒ€์ž…์„ ์ •์˜ํ•˜์—ฌ ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณต์žกํ•œ AI ์‘๋‹ต ๊ฐ์ฒด ๊ตฌ์กฐ๋ฅผ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๊ด€๋ฆฌํ•˜์—ฌ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ๋†’์ž…๋‹ˆ๋‹ค.

class-validator

  • ํ”„๋ก ํŠธ์—”๋“œ ์š”์ฒญ์ด ์„œ๋ฒ„์— ๋„๋‹ฌํ•˜๊ธฐ ์ „, ์ž…๊ตฌ์—์„œ ์ฆ‰์‹œ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ๋ฐฉ์‹์œผ๋กœ ์ง๊ด€์ ์ธ ๊ฒ€์ฆ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
1
2
3
4
5
6
7
8
9
10
11
12
// DTO ์„ค์ • ์˜ˆ์‹œ
import { IsString, IsInt, Min, Max } from 'class-validator';

export class ChatRequestDto {
  @IsString()
  message: string;       // AI์—๊ฒŒ ์ „๋‹ฌํ•  ๋ฉ”์‹œ์ง€

  @IsInt()
  @Min(1)
  @Max(500)
  maxTokens: number;     // ์ตœ๋Œ€ ํ† ํฐ ์ˆ˜ ์ œํ•œ ๊ฒ€์ฆ
}
  • ValidationPipe ์„ค์ • ์‹œ, ์ž˜๋ชป๋œ ๋ฐ์ดํ„ฐ ์ž…๋ ฅ์— ์ž๋™์œผ๋กœ 400 Bad Request ์‘๋‹ต์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

AI Adapter ์ฒ˜๋ฆฌ ํ๋ฆ„

1
2
3
4
5
1. class-validator       โ†’ ์š”์ฒญ ๋ฐ์ดํ„ฐ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
2. NestJS Service        โ†’ ํ•ด๋‹น AI ๋ชจ๋ธ SDK ํ˜ธ์ถœ (OpenAI / Claude / DeepSeek / Ollama)
3. ์‘๋‹ต ํŒŒ์‹ฑ ๋ฐ ๋ณ€ํ™˜     โ†’ MoveResponse ํ˜•ํƒœ๋กœ ์ •ํ˜•ํ™”
4. Game Server ๋ฐ˜ํ™˜      โ†’ ๊ฒฐ๊ณผ ์ „๋‹ฌ
5. Game Engine ์ตœ์ข… ๊ฒ€์ฆ โ†’ ์ตœ๋Œ€ 3ํšŒ ์žฌ์š”์ฒญ ํ›„ ์‹คํŒจ ์‹œ ๊ฐ•์ œ ๋“œ๋กœ์šฐ

๋กœ์ปฌ ์‹คํ–‰

1
2
cd src/ai-adapter
npm install && npm run start:dev

start:dev๋Š” Watch mode๋กœ ์‹คํ–‰๋˜์–ด ์ฝ”๋“œ ์ˆ˜์ • ์‹œ ์„œ๋ฒ„๊ฐ€ ์ž๋™ ์žฌ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค.


3.4 ์ธ์ฆ โ€” Google OAuth 2.0 (NextAuth.js)

NextAuth.js v5(Auth.js) ๊ธฐ๋ฐ˜์œผ๋กœ Google ์†Œ์…œ ๋กœ๊ทธ์ธ์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.

Google Cloud Console ์„ค์ •

  1. Google Cloud Console์—์„œ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
  2. API ๋ฐ ์„œ๋น„์Šค > OAuth ๋™์˜ ํ™”๋ฉด โ†’ ์™ธ๋ถ€(External) ์„ ํƒ ํ›„ ํ•„์ˆ˜ ์ •๋ณด ์ž…๋ ฅ
  3. ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ๋งŒ๋“ค๊ธฐ > OAuth ํด๋ผ์ด์–ธํŠธ ID ์ƒ์„ฑ
    • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์œ ํ˜•: ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
    • ์Šน์ธ๋œ ๋ฆฌ๋””๋ ‰์…˜ URI: http://localhost:3000/api/auth/callback/google
  4. ๋ฐœ๊ธ‰๋œ ํด๋ผ์ด์–ธํŠธ ID์™€ ๋ณด์•ˆ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ .env์— ์ €์žฅ

ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ • (.env)

AUTH_GOOGLE_ID=๋ฐœ๊ธ‰๋ฐ›์€_ํด๋ผ์ด์–ธํŠธ_ID
AUTH_GOOGLE_SECRET=๋ฐœ๊ธ‰๋ฐ›์€_ํด๋ผ์ด์–ธํŠธ_๋ณด์•ˆ_๋น„๋ฐ€๋ฒˆํ˜ธ
AUTH_SECRET=๋žœ๋ค_์‹œํฌ๋ฆฟ_ํ‚ค  # openssl rand -base64 32 ์œผ๋กœ ์ƒ์„ฑ ๊ถŒ์žฅ

์ฝ”๋“œ ๊ตฌํ˜„

1
npm install next-auth@beta
1
2
3
4
5
6
7
// auth.ts
import NextAuth from "next-auth"
import Google from "next-auth/providers/google"

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [Google],
})
1
2
3
// app/api/auth/[...nextauth]/route.ts
import { handlers } from "@/auth"
export const { GET, POST } = handlers
1
2
3
4
5
6
// ๋กœ๊ทธ์ธ ๋ฒ„ํŠผ (Client Component)
import { signIn } from "next-auth/react"

export default function LoginButton() {
  return <button onClick={() => signIn("google")}>Google๋กœ ๋กœ๊ทธ์ธ</button>
}
1
2
3
4
5
6
7
8
// ์„ธ์…˜ ํ™•์ธ (Server Component)
import { auth } from "@/auth"

export default async function Page() {
  const session = await auth()
  if (!session) return <div>๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.</div>
  return <div>์•ˆ๋…•ํ•˜์„ธ์š”, {session.user?.name}๋‹˜!</div>
}

๋ณด์•ˆ ์ฃผ์˜: AUTH_SECRET์€ ์„ธ์…˜ ์ฟ ํ‚ค ์•”ํ˜ธํ™”์— ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ ˆ๋Œ€ ์™ธ๋ถ€์— ๋…ธ์ถœํ•˜์ง€ ๋งˆ์„ธ์š”.


3.5 ๋ณด์•ˆ & ํ’ˆ์งˆ ๋„๊ตฌ โ€” DevSecOps

CI/CD ํŒŒ์ดํ”„๋ผ์ธ์— ํ†ตํ•ฉ๋œ ์ž๋™ํ™” ๋ณด์•ˆ ๋ฐ ํ’ˆ์งˆ ๊ฒ€์‚ฌ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

๋„๊ตฌ ๋น„๊ต

๋„๊ตฌ๋ถ„์„ ๋ฐฉ์‹์ฃผ์š” ๋Œ€์ƒ์ฃผ์š” ๋ชฉ์ 
SonarQube์ •์  (SAST)์†Œ์Šค ์ฝ”๋“œ์ฝ”๋“œ ํ’ˆ์งˆ ํ–ฅ์ƒ ๋ฐ ์ดˆ๊ธฐ ๋ฒ„๊ทธ ๋ฐฉ์ง€
Trivy์ •์  / ํ™˜๊ฒฝ์ปจํ…Œ์ด๋„ˆ, ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฐฐํฌ ํ™˜๊ฒฝ ๋ฐ ์ธํ”„๋ผ ๋ณด์•ˆ ๊ฐ•ํ™”
OWASP ZAP๋™์  (DAST)์‹คํ–‰ ์ค‘์ธ ์›น ์•ฑ์‹ค์ œ ์„œ๋น„์Šค ํ™˜๊ฒฝ์˜ ๊ณต๊ฒฉ ๋ฐฉ์–ด ๋ฐ ๊ฒ€์ฆ

SonarQube โ€” ์ •์  ์ฝ”๋“œ ๋ถ„์„ (SAST)

์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ๋ถ„์„ํ•˜์—ฌ ํ’ˆ์งˆ๊ณผ ๋ณด์•ˆ ์ทจ์•ฝ์ ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.

  • ๋ฒ„๊ทธ ๋ฐ ์ฝ”๋“œ ์Šค๋ฉœ ํƒ์ง€: ์ž ์žฌ์  ์˜ค๋ฅ˜์™€ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ค์šด ์ฝ”๋“œ ๊ตฌ์กฐ๋ฅผ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ณด์•ˆ ์ทจ์•ฝ์  ๋ถ„์„: SQL Injection, XSS ๋“ฑ ์ฝ”๋“œ ์ˆ˜์ค€์˜ ๋ณด์•ˆ ์œ„ํ˜‘์„ ๊ฒฝ๊ณ ํ•ฉ๋‹ˆ๋‹ค.
  • ์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€: ํ…Œ์ŠคํŠธ๊ฐ€ ์‹ค์ œ ๋กœ์ง์„ ์–ผ๋งˆ๋‚˜ ๊ฒ€์ฆํ•˜๋Š”์ง€ ์‹œ๊ฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
  • CI/CD ํ†ตํ•ฉ: GitLab CI์™€ ์—ฐ๊ฒฐํ•˜์—ฌ ์ปค๋ฐ‹๋งˆ๋‹ค ์ž๋™ ๊ฒ€์‚ฌ๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

Trivy โ€” ์ปจํ…Œ์ด๋„ˆ ๋ฐ ์ธํ”„๋ผ ๋ณด์•ˆ

Aqua Security์—์„œ ๊ฐœ๋ฐœํ•œ ์˜คํ”ˆ์†Œ์Šค ๋ณด์•ˆ ์Šค์บ๋„ˆ์ž…๋‹ˆ๋‹ค.

  • OS ํŒจํ‚ค์ง€ ์ทจ์•ฝ์  ์Šค์บ”: ์ปจํ…Œ์ด๋„ˆ ์ด๋ฏธ์ง€(Alpine, Ubuntu ๋“ฑ) ๋‚ด ํŒจํ‚ค์ง€ ๋ณด์•ˆ ๊ฒฐํ•จ์„ ํƒ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ข…์†์„ฑ ์Šค์บ”: Go, Node.js ๋“ฑ ํ”„๋กœ์ ํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ทจ์•ฝ์ ์„ ํƒ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  • ์„ค์ • ์˜ค๋ฅ˜ ์ ๊ฒ€: Dockerfile, Kubernetes YAML์˜ ๋ณด์•ˆ ์œ„ํ—˜ ๊ตฌ์„ฑ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
  • ๋น ๋ฅธ ์†๋„: ๊ฐ€๋ณ๊ณ  ๋นจ๋ผ ์ปจํ…Œ์ด๋„ˆ ๋นŒ๋“œ ์‹œ ์ฆ‰๊ฐ์ ์ธ ๊ฒ€์‚ฌ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

OWASP ZAP โ€” ๋™์  ์›น ๋ณด์•ˆ ํ…Œ์ŠคํŠธ (DAST)

์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹ค์ œ ์‹คํ–‰ํ•œ ์ƒํƒœ์—์„œ ๊ณต๊ฒฉ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ํ•ฉ๋‹ˆ๋‹ค.

  • ์ž๋™ ์Šค์บ”: ์›น ์‚ฌ์ดํŠธ๋ฅผ ํฌ๋กค๋งํ•˜๋ฉฐ OWASP Top 10 ์ทจ์•ฝ์ ์„ ์ž๋™ ํ…Œ์ŠคํŠธํ•ฉ๋‹ˆ๋‹ค.
  • ์ŠคํŒŒ์ด๋”๋ง: ๋ชจ๋“  ํŽ˜์ด์ง€์™€ URL ๊ตฌ์กฐ๋ฅผ ์ž๋™์œผ๋กœ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.
  • API/CLI ์ง€์›: ์ž๋™ํ™”๋œ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ์— ๋ณด์•ˆ ์ ๊ฒ€์„ ํฌํ•จ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

4. ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
RummiArena/
โ”œโ”€โ”€ docs/
โ”‚   โ”œโ”€โ”€ 01-planning/       # ๊ธฐํš (ํ—Œ์žฅ, ์š”๊ตฌ์‚ฌํ•ญ, WBS, ๋ฐฑ๋กœ๊ทธ)
โ”‚   โ”œโ”€โ”€ 02-design/         # ์„ค๊ณ„ (์•„ํ‚คํ…์ฒ˜, DB, API, WebSocket, AI Adapter)
โ”‚   โ”œโ”€โ”€ 03-development/    # ๊ฐœ๋ฐœ ๊ฐ€์ด๋“œ
โ”‚   โ”œโ”€โ”€ 04-testing/        # ํ…Œ์ŠคํŠธ ์ „๋žต + ๋ณด๊ณ ์„œ
โ”‚   โ””โ”€โ”€ 05-deployment/     # ๋ฐฐํฌ ๊ฐ€์ด๋“œ + K8s ์•„ํ‚คํ…์ฒ˜
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ game-server/       # Go ๋ฐฑ์—”๋“œ (REST API + Game Engine)
โ”‚   โ”‚   โ””โ”€โ”€ cmd/server/    # ๋ฉ”์ธ ์—”ํŠธ๋ฆฌํฌ์ธํŠธ
โ”‚   โ”œโ”€โ”€ ai-adapter/        # NestJS AI ์„œ๋น„์Šค
โ”‚   โ”œโ”€โ”€ frontend/          # Next.js ๊ฒŒ์ž„ UI
โ”‚   โ””โ”€โ”€ admin/             # ๊ด€๋ฆฌ์ž ๋Œ€์‹œ๋ณด๋“œ
โ”œโ”€โ”€ helm/
โ”‚   โ””โ”€โ”€ charts/
โ”‚       โ”œโ”€โ”€ postgres/      # PostgreSQL Helm ์ฐจํŠธ (Bitnami ๊ธฐ๋ฐ˜)
โ”‚       โ”œโ”€โ”€ redis/         # Redis Helm ์ฐจํŠธ (Bitnami ๊ธฐ๋ฐ˜)
โ”‚       โ”œโ”€โ”€ game-server/   # ์ปค์Šคํ…€ Helm ์ฐจํŠธ (์ง์ ‘ ์ž‘์„ฑ)
โ”‚       โ”œโ”€โ”€ ai-adapter/    # ์ปค์Šคํ…€ Helm ์ฐจํŠธ (์ง์ ‘ ์ž‘์„ฑ)
โ”‚       โ””โ”€โ”€ frontend/      # ์ปค์Šคํ…€ Helm ์ฐจํŠธ (์ง์ ‘ ์ž‘์„ฑ)
โ””โ”€โ”€ work_logs/             # ์„ธ์…˜ / ๋ฐ์ผ๋ฆฌ / ์Šคํฌ๋Ÿผ ๋กœ๊ทธ

Helm ์ฐจํŠธ ์ข…๋ฅ˜

์ฐจํŠธ์ถœ์ฒ˜๋น„๊ณ 
charts/postgresBitnami ๊ณต์šฉ ์ฐจํŠธ ํ™œ์šฉbitnami/postgresql ๊ธฐ๋ฐ˜
charts/redisBitnami ๊ณต์šฉ ์ฐจํŠธ ํ™œ์šฉbitnami/redis ๊ธฐ๋ฐ˜
charts/game-server์ง์ ‘ ์ž‘์„ฑGo ์„œ๋ฒ„ ์ด๋ฏธ์ง€, ํฌํŠธ, ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
charts/ai-adapter์ง์ ‘ ์ž‘์„ฑNestJS ์„œ๋ฒ„ ์ด๋ฏธ์ง€, AI API ํ‚ค ์„ค์ •
charts/frontend์ง์ ‘ ์ž‘์„ฑNext.js ์ด๋ฏธ์ง€, NodePort ์„ค์ •

์ปค์Šคํ…€ ์ฐจํŠธ๋Š” helm create charts/game-server๋กœ ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ ์ƒ์„ฑ ํ›„ values.yaml์„ ์ˆ˜์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.


5. ๊ฒŒ์ž„ ๊ทœ์น™ & ํƒ€์ผ ์ธ์ฝ”๋”ฉ

์ปดํ“จํ„ฐ์™€ AI๊ฐ€ ํƒ€์ผ์„ ๊ณ ์œ ํ•˜๊ฒŒ ์‹๋ณ„ํ•˜๊ธฐ ์œ„ํ•œ ์ฝ”๋“œ ๊ทœ์น™์ž…๋‹ˆ๋‹ค.

ํƒ€์ผ ์ฝ”๋“œ ๊ตฌ์กฐ: {Color}{Number}{Set}

๊ตฌ์„ฑ ์š”์†Œ๊ฐ’์„ค๋ช…
ColorR, B, Y, KRed, Blue, Yellow, Black
Number1 ~ 13ํƒ€์ผ์— ์ ํžŒ ์ˆซ์ž
Seta, b๋™์ผํ•œ ํƒ€์ผ์ด 2๊ฐœ์”ฉ ์กด์žฌํ•˜๋ฏ€๋กœ ๊ตฌ๋ถ„ ํ•„์š”
JokerJK1, JK2์กฐ์ปค ํƒ€์ผ 2๊ฐœ ๊ตฌ๋ถ„

Black์„ โ€˜Kโ€™๋กœ ํ‘œ๊ธฐํ•˜๋Š” ์ด์œ : โ€˜Bโ€™๊ฐ€ ์ด๋ฏธ Blue์— ์‚ฌ์šฉ๋˜์–ด ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•จ์ž…๋‹ˆ๋‹ค.

ํƒ€์ผ ์ฝ”๋“œ ์˜ˆ์‹œ

์ฝ”๋“œ์˜๋ฏธ
R7a๋นจ๊ฐ„์ƒ‰ 7๋ฒˆ ์ฒซ ๋ฒˆ์งธ ํƒ€์ผ
R7b๋นจ๊ฐ„์ƒ‰ 7๋ฒˆ ๋‘ ๋ฒˆ์งธ ํƒ€์ผ
B13bํŒŒ๋ž€์ƒ‰ 13๋ฒˆ ๋‘ ๋ฒˆ์งธ ํƒ€์ผ
Y1a๋…ธ๋ž€์ƒ‰ 1๋ฒˆ ์ฒซ ๋ฒˆ์งธ ํƒ€์ผ
K10b๊ฒ€์ •์ƒ‰ 10๋ฒˆ ๋‘ ๋ฒˆ์งธ ํƒ€์ผ
JK1์ฒซ ๋ฒˆ์งธ ์กฐ์ปค
JK2๋‘ ๋ฒˆ์งธ ์กฐ์ปค

๊ฐœ๋ฐœ ์‹œ ํ™œ์šฉ

  • Go ์„œ๋ฒ„: WebSocket ํŒจํ‚ท์„ ๋ฌธ์ž์—ด ๋ฐฐ์—ด๋กœ ์ „์†กํ•˜์—ฌ ํŒจํ‚ท ํฌ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ•ฉ๋‹ˆ๋‹ค.
    ์˜ˆ) ["R7a", "R8a", "R9a"] โ†’ ๊ฐ™์€ ์ƒ‰ ์—ฐ์† 3์žฅ ๊ทธ๋ฃน ๊ตฌ์„ฑ
  • AI ์–ด๋Œ‘ํ„ฐ: LLM ํ”„๋กฌํ”„ํŠธ์— ์ธ์ฝ”๋”ฉ ๊ทœ์น™์„ ํฌํ•จํ•˜์—ฌ AI๊ฐ€ ๊ฒŒ์ž„ ์ƒํƒœ๋ฅผ ์ •ํ™•ํžˆ ํŒŒ์•…ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • Zustand: ํƒ€์ผ ์ฝ”๋“œ๋ฅผ key ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

6. AI ์บ๋ฆญํ„ฐ ์‹œ์Šคํ…œ

๋‹ค์–‘ํ•œ ์ „๋žต ์Šคํƒ€์ผ์„ ๊ฐ€์ง„ AI ์บ๋ฆญํ„ฐ๋กœ ๊ฒŒ์ž„ ๋‹ค์–‘์„ฑ์„ ํ™•๋ณดํ•ฉ๋‹ˆ๋‹ค.

์บ๋ฆญํ„ฐ์Šคํƒ€์ผ์„ค๋ช…
Rookie๋ณด์ˆ˜์ ์•ˆ์ „ํ•œ ์ˆ˜๋งŒ ์„ ํƒ
Calculatorํ™•๋ฅ  ๊ธฐ๋ฐ˜๊ธฐ๋Œ€๊ฐ’ ๊ณ„์‚ฐ์œผ๋กœ ์ตœ์  ์ˆ˜ ๋„์ถœ
Shark๊ณต๊ฒฉ์ ์ƒ๋Œ€ ๊ฒฌ์ œ + ๋Œ€๋Ÿ‰ ๋ฐฐ์น˜ ์ „๋žต
Fox๊ธฐ๋งŒ์ ์˜๋„์  ์ง€์—ฐ + ์—ญ์ „ ํŒจํ„ด ๊ตฌ์‚ฌ
Wall์ˆ˜๋น„์ ์ตœ์†Œ ๋ฐฐ์น˜ + ์ž์› ๋น„์ถ•
Wildcard์˜ˆ์ธก ๋ถˆ๊ฐ€๋žœ๋ค ์ „๋žต ํ˜ผํ•ฉ
  • ๋‚œ์ด๋„: ํ•˜์ˆ˜ / ์ค‘์ˆ˜ / ๊ณ ์ˆ˜
  • ์‹ฌ๋ฆฌ์ „ ๋ ˆ๋ฒจ: 0 ~ 3๋‹จ๊ณ„

7. API ๋ช…์„ธ ๊ฐœ์š”

Room Management

1
2
3
4
5
6
7
POST   /api/rooms             # ๋ฐฉ ์ƒ์„ฑ
GET    /api/rooms             # ๋ฐฉ ๋ชฉ๋ก ์กฐํšŒ
GET    /api/rooms/:id         # ๋ฐฉ ์ƒ์„ธ ์กฐํšŒ
POST   /api/rooms/:id/join    # ๋ฐฉ ์ž…์žฅ
POST   /api/rooms/:id/leave   # ๋ฐฉ ํ‡ด์žฅ
POST   /api/rooms/:id/start   # ๊ฒŒ์ž„ ์‹œ์ž‘
DELETE /api/rooms/:id         # ๋ฐฉ ์‚ญ์ œ

Game Actions

1
2
3
4
5
GET    /api/games/:id            # ๊ฒŒ์ž„ ์ƒํƒœ ์กฐํšŒ (1์ธ์นญ ๋ทฐ)
POST   /api/games/:id/place      # ํƒ€์ผ ์ž„์‹œ ๋ฐฐ์น˜
POST   /api/games/:id/confirm    # ํ„ด ํ™•์ • (Game Engine ์œ ํšจ์„ฑ ๊ฒ€์ฆ)
POST   /api/games/:id/draw       # ํƒ€์ผ ๋“œ๋กœ์šฐ
POST   /api/games/:id/reset      # ํ„ด ๋˜๋Œ๋ฆฌ๊ธฐ

Health Check

1
2
GET    /health    # ์„œ๋ฒ„ ์ƒํƒœ + Redis ์—ฐ๊ฒฐ ํ™•์ธ
GET    /ready     # ์ค€๋น„ ์ƒํƒœ ํ™•์ธ

Service Endpoints (NodePort)

์„œ๋น„์ŠคURLPort
Frontendhttp://localhost:3000030000
Game Serverhttp://localhost:3008030080
AI Adapterhttp://localhost:3008130081
PostgreSQLlocalhost:3043230432

8. ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์‹คํ–‰

์‚ฌ์ „ ์š”๊ตฌ์‚ฌํ•ญ

  • Docker Desktop (Kubernetes ํ™œ์„ฑํ™”)
  • Helm 3
  • Node.js 20+
  • Go 1.24+

1. Game Server ์‹คํ–‰ (Go)

1
2
cd src/game-server
go build ./cmd/server && ./server
  • go build ./cmd/server: ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ•˜์—ฌ ๋ฐ”์ด๋„ˆ๋ฆฌ ์ƒ์„ฑ
  • && ./server: ๋นŒ๋“œ ์„ฑ๊ณต ์‹œ ์ฆ‰์‹œ ์„œ๋ฒ„ ์‹คํ–‰
  • WebSocket ํ†ต์‹  ๋Œ€๊ธฐ ์ƒํƒœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

2. AI Adapter ์‹คํ–‰ (NestJS)

1
2
cd src/ai-adapter
npm install && npm run start:dev
  • npm install: package.json์— ๋ช…์‹œ๋œ ๋ชจ๋“  ํŒจํ‚ค์ง€ ์„ค์น˜
  • npm run start:dev: ๊ฐœ๋ฐœ ๋ชจ๋“œ(Watch mode) ์‹คํ–‰. ์ฝ”๋“œ ์ €์žฅ ์‹œ ์ž๋™ ์žฌ์‹œ์ž‘

3. Frontend ์‹คํ–‰ (Next.js)

1
2
cd src/frontend
npm install && npm run dev
  • npm run dev: Next.js ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์‹คํ–‰ (http://localhost:3000)
  • Hot Module Replacement(HMR) ์ง€์›. ์ฝ”๋“œ ์ˆ˜์ • ์‹œ ์ฆ‰์‹œ ํ™”๋ฉด ๋ฐ˜์˜

์ฃผ์˜: Framer Motion, dnd-kit, Zustand๋Š” ๋ธŒ๋ผ์šฐ์ € API๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๋ฐ˜๋“œ์‹œ 'use client' ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ตฌํ˜„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.


9. Kubernetes ๋ฐฐํฌ (Helm)

๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ

1
kubectl create namespace rummikub

rummikub ์ „์šฉ ๊ฒฉ๋ฆฌ ๊ณต๊ฐ„์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋Š” ์ด ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์•ˆ์—์„œ ๊ด€๋ฆฌ๋ฉ๋‹ˆ๋‹ค.

Helm ์ฐจํŠธ ๋ฐฐํฌ (5๊ฐœ ์„œ๋น„์Šค)

1
2
3
4
5
6
7
8
9
10
11
12
cd helm

# 1. ์ธํ”„๋ผ ๊ณ„์ธต (DB, ์บ์‹œ)
helm install postgres    charts/postgres    -n rummikub
helm install redis       charts/redis       -n rummikub

# 2. ๋ฐฑ์—”๋“œ ์„œ๋น„์Šค
helm install game-server charts/game-server -n rummikub
helm install ai-adapter  charts/ai-adapter  -n rummikub

# 3. ํ”„๋ก ํŠธ์—”๋“œ
helm install frontend    charts/frontend    -n rummikub

๋ฐฐํฌ ํ™•์ธ

1
2
3
4
5
6
7
8
# ํŒŒ๋“œ ์ƒํƒœ ํ™•์ธ
kubectl get pods -n rummikub

# ์„œ๋น„์Šค ํ™•์ธ
kubectl get services -n rummikub

# ํŠน์ • ํŒŒ๋“œ ๋กœ๊ทธ ํ™•์ธ
kubectl logs -f <pod-name> -n rummikub

๋ฐฐํฌ ์—…๋ฐ์ดํŠธ / ์‚ญ์ œ

1
2
3
4
5
6
7
8
# ์„ค์ • ๋ณ€๊ฒฝ ํ›„ ์—…๋ฐ์ดํŠธ
helm upgrade game-server charts/game-server -n rummikub

# ํŠน์ • ์„œ๋น„์Šค ์‚ญ์ œ
helm uninstall game-server -n rummikub

# ์ „์ฒด ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์‚ญ์ œ
kubectl delete namespace rummikub

10. ํ…Œ์ŠคํŠธ ํ˜„ํ™ฉ

ํ…Œ์ŠคํŠธ ๋ถ„๋ฅ˜๊ฒฐ๊ณผ์ปค๋ฒ„๋ฆฌ์ง€
Engine Unit Testsโœ… 69/69 PASS96.5%
Smoke Testsโœ… 16/16 PASSโ€”
Integration Testsโœ… 31/31 PASSREST API + DB/Redis

๊ด€๋ จ ๋ฌธ์„œ

๋ฌธ์„œ๊ฒฝ๋กœ
ํ”„๋กœ์ ํŠธ ํ—Œ์žฅdocs/01-planning/01-project-charter.md
์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„docs/02-design/01-architecture.md
DB ์„ค๊ณ„docs/02-design/02-database-design.md
API ์„ค๊ณ„docs/02-design/03-api-design.md
WebSocket ํ”„๋กœํ† ์ฝœdocs/02-design/10-websocket-protocol.md
ํ…Œ์ŠคํŠธ ์ „๋žตdocs/04-testing/01-test-strategy.md
ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ ๋ณด๊ณ ์„œdocs/04-testing/06-integration-test-report.md

์ด ๋ฌธ์„œ๋Š” RummiArena ํ”„๋กœ์ ํŠธ์˜ ๊ธฐ์ˆ  ์Šคํƒ, ์•„ํ‚คํ…์ฒ˜, ๊ฐœ๋ฐœ ๋ฐ ๋ฐฐํฌ ๊ฐ€์ด๋“œ๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
๋ฌธ์˜ ๋ฐ ๊ธฐ์—ฌ: GitHub โ€” RummiArena

์ด ๊ธฐ์‚ฌ๋Š” ์ €์ž‘๊ถŒ์ž์˜ CC BY 4.0 ๋ผ์ด์„ผ์Šค๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.

ยฉ BLUEBUG. ์ผ๋ถ€ ๊ถŒ๋ฆฌ ๋ณด์œ 

Powered by Jekyll with Chirpy theme