- 개요, 조사(BGA·한국 인기 보드게임·UI/UX), 아키텍처, 실시간 프로토콜,
안정성·보안, 계정, 로비·방, UI/UX, 데이터 모델, 게임 엔진, 테스트,
배포·운영, 로드맵(M0~M9), 사용자 결정 항목
- 게임별 규칙·엔진 설계·UI·테스트 체크리스트 26종
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
마일스톤: M2 · 인원: 최소 2 ~ 최대 2명 · 예상 시간: 약 20~40분 · 난이도: 보통
1. 개요
초(楚)와 한(漢)이 각 16개의 기물로 상대 궁(장)을 외통수로 몰면 이기는 한국 전통 2인 전략 게임. 공원·경로당·가정에서 어르신부터 어린이까지 두는 친숙한 게임이며 장기판 앞의 "장군!" "멍군!"이 상징적이다.
인원 근거: 전통 규칙상 초·한 2인. 최소 2, 최대 2.
규칙 기준: (사)대한장기연맹(KJF) 규정을 기본으로 하고, 온라인에 맞게 구현 방식을 정했다. 단체마다 다른 부분(빅장 조건, 반복수 처리, 상차림 순서)은 옵션으로 둔다.
완전 정보 게임. 단, pairing=simultaneous 상차림 옵션을 쓰면 상대 상차림이 확정 전까지 비공개(8.3).
2. 모드와 옵션
옵션 키
설명
선택지
기본값
scoring
점수제(판정이 필요한 종료를 기물 점수로 가림)
true / false(무승부 처리)
true
bikjang
빅장 규칙
off(궁이 마주봐도 아무 일 없음) / kjf(기물이 하나라도 잡힌 다음 수부터 빅장 가능) / kja30(양쪽 기물 점수가 모두 30점 미만일 때만 가능)
kjf
repetition
반복수 처리 (같은 국면 4번째)
forbid(4번째를 만드는 수를 둘 수 없음) / lose(KJF: 4회 반복한 쪽 실격패) / scoreEnd(4번째 국면 발생 시 점수 판정 종료)
forbid
allowPass
한수쉼 허용
true / false
true
setupOrder
상차림 순서
hanFirst(KJF: 후수 한이 먼저 공개적으로 차리고 초가 나중) / simultaneous(동시에 비공개로 고른 뒤 공개)
hanFirst
sideAssignment
초·한 결정
random / hostCho / hostHan / alternate
random
moveLimit
수 제한(도달 시 점수 판정)
0(없음) / 150 / 200 / 250 (양쪽 합산 수)
0
timeControl.kind
시간 방식
none / byoyomi
byoyomi
timeControl.mainSec / periodSec / periods
기본 시간 / 초읽기 1회 / 횟수
5분·10분·20분 / 20·30·60초 / 1~5회
10분 / 30초 / 3회
idleLimitSec
none일 때 무응답 한도
300 / 600
600
undo
무르기 허용 횟수(상대 동의)
off / 1 / 3 / unlimited
3
drawOffer
무승부 제안
true / false
true
hints
이동 가능 칸 표시, 장군 위험 경고
off / moves / movesAndThreats
moves
disconnectGraceSec
연결 끊김 유예
60 / 120
60
3. 구성물
판: 가로 9줄 × 세로 10줄, 교차점 90개. 기물은 교차점에 놓는다. 강(river) 없음.
궁성: 양쪽 끝 가운데 3×3 영역. 궁성 안에는 X자 대각선이 그려져 있다.
기물(각 진영 16개):
기물
초 / 한
수
점수
궁(장)
楚 / 漢
1
—
차
車
2
13
포
包
2
7
마
馬
2
5
상
象
2
3
사
士
2
3
졸 / 병
卒 / 兵
5
2
기물 점수 합 72점. 후수(한)에게 덤 1.5점 → 초 72, 한 73.5로 시작.
내부 좌표: (f, r), f 08(초 기준 왼→오), r 09(초의 뒷줄 0, 한의 뒷줄 9).
초 궁성: f 35, r 02, 중심 (4,1). 대각선: (3,0)–(4,1)–(5,2), (5,0)–(4,1)–(3,2).
한 궁성: f 35, r 79, 중심 (4,8). 대각선: (3,7)–(4,8)–(5,9), (5,7)–(4,8)–(3,9).
4. 준비(셋업)
sideAssignment로 초(선수)·한(후수) 결정. 초가 먼저 둔다.
고정 배치(초 기준, 한은 r → 9 - r로 대칭):
궁 (4,1) (궁성 중심), 사 (3,0), (5,0), 차 (0,0), (8,0), 포 (1,2), (7,2), 졸 (0,3) (2,3) (4,3) (6,3) (8,3).
한: 궁 (4,8), 사 (3,9)(5,9), 차 (0,9)(8,9), 포 (1,7)(7,7), 병 (0,6) (2,6) (4,6) (6,6) (8,6).
상차림: 마·상 4개를 뒷줄의 차와 사 사이 두 칸씩(왼쪽 2칸, 오른쪽 2칸)에 놓는다. 각 플레이어가 자기 시점에서 왼쪽→오른쪽으로 4가지 중 선택:
선택
자기 시점 왼→오
별칭
MSSM
마 상 · 상 마
안상차림, 원앙마
SMMS
상 마 · 마 상
바깥상차림, 양귀마
SMSM
상 마 · 상 마
왼상차림(귀마)
MSMS
마 상 · 마 상
오른상차림(귀마)
초의 왼쪽은 f 작은 쪽 → 칸 (1,0) (2,0) (6,0) (7,0). 한은 판 반대편에 앉아 있으므로 한의 "왼쪽"은 f 큰 쪽 → 칸 (7,9) (6,9) (2,9) (1,9) 순서로 대응.
setupOrder=hanFirst: 한이 먼저 선택(즉시 공개) → 초가 보고 선택. simultaneous: 둘 다 비공개로 선택, 둘 다 확정되면 동시에 공개.
시계 초기화, 반복 국면 카운트에 초기 국면(초 차례) 1회 기록.
5. 진행 규칙
5.1 턴
초부터 번갈아 한 수씩: 기물 하나 이동(이동 칸에 상대 기물이 있으면 잡음) 또는 한수쉼(allowPass, 장군 상태가 아닐 때만).
자기 기물이 있는 칸으로는 이동 불가. 판 밖 이동 불가.
이동 후 자기 궁이 상대 기물의 공격을 받게 되는 수는 불법(장군을 피하지 않는 수, 스스로 장군에 들어가는 수 모두 포함).
5.2 기물별 행마
궁, 사: 궁성 안에서만, 궁성의 선을 따라 1칸. 가로·세로 1칸, 그리고 대각선이 그려진 곳(모서리↔중심)에서만 대각 1칸. 즉 모서리 4곳·중심은 대각 가능, 변의 가운데 4곳((4,0) (3,1) (5,1) (4,2) 등)에서는 대각 불가.
차: 가로·세로로 막히지 않는 한 몇 칸이든. 궁성 안(양쪽 궁성 모두)에서는 대각선을 따라서도 이동: 모서리→중심→반대 모서리까지(중심이 비어 있어야 반대 모서리로 감), 중심→모서리 1칸.
포: 차와 같은 선(가로·세로, 궁성 대각선)을 따라 움직이되, 반드시 정확히 1개의 기물(포다리)을 뛰어넘어야 한다.
포다리는 피아 구분 없이 아무 기물이나 되지만 포(어느 편이든)는 포다리가 될 수 없다.
포다리 너머 첫 기물 앞까지의 빈칸 어디든 이동 가능, 또는 포다리 너머 첫 기물이 상대 기물이면 잡을 수 있다. 단 포는 포를 잡을 수 없다.
포다리 없이는 한 칸도 움직일 수 없다.
궁성 대각선: 모서리에 있는 포는 중심에 포가 아닌 기물이 있으면 그것을 넘어 반대 모서리로 갈 수 있다(빈칸이면 이동, 상대 비-포 기물이면 잡기).
마: 가로 또는 세로 1칸 간 뒤 그 방향으로 대각선 1칸(목적지 8곳: (±1,±2) (±2,±1)). 첫 1칸(멱)에 기물이 있으면 그 방향으로는 갈 수 없다. 궁성 대각선과 무관.
상: 가로 또는 세로 1칸 간 뒤 그 방향으로 대각선 2칸(목적지 8곳: (±2,±3) (±3,±2)). 경로의 첫 칸(직선 1칸)과 둘째 칸(대각 1칸째)이 모두 비어 있어야 한다. 예: (f,r)에서 위로 가는 상 → 멱 (f,r+1), (f±1,r+2), 목적지 (f±2,r+3).
졸(초)/병(한): 앞 또는 옆으로 1칸. 뒤로는 못 간다. 상대 궁성 안에서는 앞쪽 대각선을 따라 1칸 추가 가능: 초 졸은 (3,7)→(4,8), (5,7)→(4,8), (4,8)→(3,9), (4,8)→(5,9); 한 병은 (3,2)→(4,1), (5,2)→(4,1), (4,1)→(3,0), (4,1)→(5,0). 마지막 줄에서는 옆으로만 이동.
모든 기물은 승급 없음.
5.3 장군·멍군·외통
장군: 다음 수에 상대 궁을 잡을 수 있는 상태(포다리를 이용한 포의 장군 포함). 장군을 받은 쪽은 반드시 피해야 하며 한수쉼 불가.
멍군: 장군을 막아낸 응수(가리기, 공격 기물 잡기, 궁 피하기). UI 표시용 용어이며 규칙상 별도 효과 없음.
외통(체크메이트): 장군 상태에서 합법 수가 없음 → 패배. 궁을 실제로 잡지는 않는다.
장군 상태가 아닌데 합법 이동이 없으면 한수쉼(옵션이 꺼져 있어도 이 경우 자동 한수쉼 처리).
5.4 빅장
정의: 두 궁이 같은 세로줄(f 같음)에 있고 그 사이에 기물이 하나도 없는 상태. 장군이 아니다.
빅장 성립(부르기): 어떤 수의 결과로 빅장 상태가 되고, 그 시점에 bikjang 조건이 충족되면 그 수를 둔 쪽이 빅장을 부른 것이다.
kjf: 이전 수들 중 잡기가 한 번 이상 있었다(첫 잡기가 일어난 그 수 자체로는 부를 수 없고, 그 다음 수부터).
kja30: 그 수를 둔 직후 양쪽 기물 점수(덤 제외)가 모두 30점 미만.
조건이 충족되지 않으면 궁이 마주봐도 아무 일도 없다(합법이며 빅장 아님).
응수: 빅장을 받은 쪽은 한 수를 둔다. 그 수 후에도 두 궁이 여전히 마주보고 있으면(한수쉼, 무관한 수, 잡기만 하고 막지 않은 수 포함) 빅 → 즉시 종료: scoring=true면 남은 기물 점수로 판정, false면 무승부. 마주보지 않게 만들면(사이에 기물을 넣거나 궁을 옆으로) 대국 계속.
빅장을 부르는 수가 동시에 장군이 될 수는 없다(궁끼리는 장군 관계가 아님). 다른 기물로 장군을 건 상태라면 상대는 장군을 먼저 해결해야 하며, 그 응수 후에도 마주보면 빅.
bikjang=off: 마주보기는 아무 효과 없음(카카오 장기 등 온라인 일부 방식).
5.5 반복수
국면 = 판 배치 + 둘 차례. 같은 국면이 3번까지는 허용, 4번째로 나타나는 경우:
forbid(기본): 4번째 국면을 만드는 수는 둘 수 없다("같은 수를 너무 많이 반복해서 둘 수 없어요"). 예외: 장군을 받았는데 그 수 말고 합법 수가 없으면 허용하고 즉시 점수 판정(scoring=false면 무승부).
lose: 4번째 국면을 만든 쪽 실격패(KJF 규정 "4회 반복 시 실격패").
scoreEnd: 4번째 국면이 나오면 점수 판정 종료.
한수쉼으로 생긴 국면도 같은 방식으로 센다(차례만 바뀌므로 별도 국면).
5.6 한수쉼 연속
양쪽이 연속으로 한수쉼 → 종료: scoring=true면 점수 판정, 아니면 무승부.
6. 승패와 점수 계산
완승: 외통, 상대 기권, 상대 시간패, 상대 실격(repetition=lose).
점수 판정이 일어나는 경우(scoring=true): 빅장 성립, 양쪽 연속 한수쉼, 수 제한 도달, repetition=scoreEnd, forbid의 예외 상황.
점수 = 판에 남은 자기 기물 점수 합(차 13, 포 7, 마 5, 상 3, 사 3, 졸·병 2, 궁 0) + 한에게 덤 1.5.
덤이 0.5 단위라 동점은 나오지 않는다. 높은 쪽 승.
예시: 빅 성립 시 초에 차2·포1·마1·사2·졸3 = 26+7+5+6+6 = 50, 한에 차1·포2·마2·상1·사2·병4 = 13+14+10+3+6+8 = 54 + 1.5 = 55.5 → 한 5.5점 승("한 판정승 55.5 : 50").
scoring=false면 위 판정 상황은 모두 무승부.
무승부 합의도 가능.
7. 지역 룰 / 하우스 룰 (옵션으로 켜고 끄기)
점수제/비점수제 × 빅장 있음/없음의 4가지 대국 방식이 실제로 공존한다. 기본은 정식 대회에서 주로 쓰는 "점수제 + 빅장".
빅장 조건: KJF(기물 1개 이상 잡힌 뒤) / 대한장기협회(KJA, 양쪽 30점 미만) / 없음.
반복수: 기본은 "막기"(어린이에게 실격패보다 친절), 공식처럼 실격패도 선택 가능.
상차림 순서: KJF는 후수(한)가 먼저 차린다. 친선에서는 각자 동시에 정하는 경우가 많아 옵션 제공.
접장기(실력 차 보정으로 차·포 등을 떼고 두기), 기물 점수 변형은 v1 미지원(추후 handicapRemove 옵션 후보).
8. 엔진 설계
8.1 상태(State)
typeSide='cho'|'han';typePieceKind='king'|'cha'|'po'|'ma'|'sang'|'sa'|'jol';typePiece={side: Side;kind: PieceKind};typeSq={f: number;r: number};typeArrangement='MSSM'|'SMMS'|'SMSM'|'MSMS';interfaceJanggiState{options: JanggiOptions;players: Record<Side,PlayerId>;phase:'setup'|'playing'|'finished';setup:{choice: Record<Side,Arrangement|null>};// simultaneous면 상대 값은 view에서 숨김
board:(Piece|null)[];// index = r*9 + f
turn: Side;moves:({kind:'move';side: Side;from:Sq;to: Sq;captured: PieceKind|null}|{kind:'pass';side: Side})[];firstCapturePly: number|null;// 첫 잡기가 일어난 수 번호
positionCounts: Record<string,number>;// 배치+차례 -> 횟수
bikjang:{calledBy: Side;ply: number}|null;// 응수 대기 중인 빅장
consecutivePasses: number;inCheck: Side|null;clock:{mainMs: Record<Side,number>;periodsLeft: Record<Side,number>;turnStartedAt: number};undo:{usedBy: Record<Side,number>;pending:{by: Side;plies: 1|2}|null};draw:{pendingBy: Side|null;lastOfferPly: Record<Side,number>};result: JanggiResult|null;rng: RngState;}
8.2 액션
type
payload
누가 / 언제
검증 조건
chooseSetup
{ arrangement }
setup 단계. hanFirst: 한이 먼저, 그다음 초 / simultaneous: 둘 다
아직 선택 안 함, 순서 맞음. 둘 다 정해지면 배치 확정, playing 진입, 초 차례
move
{ from: Sq, to: Sq }
차례인 플레이어, playing
from에 자기 기물, to가 5.2 행마로 도달 가능, 자기 기물 없음, 포는 포다리 조건(포 넘기·포 잡기 금지), 이동 후 자기 궁이 공격받지 않음, 반복수 forbid 위반 아님. 사유 예: "포는 포를 넘을 수 없어요", "마의 길(멱)이 막혀 있어요", "장군을 피해야 해요", "궁은 궁성 밖으로 나갈 수 없어요"
pass
{}
차례인 플레이어
allowPass 또는 합법 이동 없음, 장군 상태 아님("장군을 받은 상태에서는 쉴 수 없어요"), 반복수 위반 아님
requestUndo / respondUndo
{} / { accept }
플레이어 / 상대
횟수 남음, 대기 없음. 수락 시 moves 재생으로 판·positionCounts·firstCapturePly·bikjang 재구성, 시계 유지
offerDraw / respondDraw / cancelOffer
플레이어
거절 후 본인 5수 이후 재제안
resign
{}
플레이어
setup 중이면 결과 없이 취소
timeout
{}
시스템
8.5
move/pass 적용 후 처리 순서:
시계 차감(초읽기 규칙은 바둑과 동일: 기본 시간 → 초읽기 회차, 회차 초과 시 소진, 0이면 시간패로 액션 무효).
판 갱신, 잡기면 firstCapturePly 설정(처음일 때).
응수 대기 중인 빅장이 있고 지금 둔 사람이 응수자라면: 여전히 마주봄 → 빅(종료). 아니면 bikjang = null.
상대 외통 판정 → 승리.
새로 빅장 상태가 되었고 조건 충족 → bikjang = { calledBy: 둔 사람 }, 이벤트 "빅장".
반복 카운트 증가, lose/scoreEnd 처리, 연속 한수쉼 처리, moveLimit 처리.
장군 여부 이벤트(janggun), 직전 장군 해소면 meonggun 이벤트.
공격 판정 함수 attacks(board, side, sq): 차(직선·궁성 대각), 포(포다리 1개, 포 제외), 마·상(멱 확인), 졸(앞·옆·궁성 대각 앞), 사·궁(궁성 안 인접 선) 모두 포함. 궁끼리 마주봄은 공격이 아니다.
8.3 공개/비공개 정보 (view)
공개: 판, 차례, 기보, 잡은 기물, 현재 점수(덤 포함), 장군 상태, 빅장 응수 대기 여부, 반복 경고(같은 국면 3회째 도달 시 "한 번 더 반복하면 둘 수 없어요"), 시계, deadline, 대기 중인 요청, 결과.
setup 단계 + simultaneous: 상대 선택값은 "선택 완료"(true/false)만 보이고 배치는 둘 다 확정될 때까지 숨긴다(플레이어·관전자 모두). hanFirst에서는 한의 선택이 즉시 공개.
차례인 플레이어에게만: legalMoves(하이라이트용), 힌트 옵션 시 threatened(잡힐 위험 기물).
제외: rng, 상대의 미공개 상차림.
8.4 랜덤 요소 (시드 RNG 사용 지점)
sideAssignment=random의 초·한 결정.
상차림 시간 초과 시 자동 선택(4가지 중 균등 무작위).
대국 중에는 사용하지 않는다.
8.5 타이머·시간 초과·연결 끊김 시 자동 행동
deadline(state):
setup: 단계 시작 + 60초(선택해야 할 사람 기준).
playing + byoyomi: turnStartedAt + mainMs[turn] + periodsLeft[turn]*periodSec*1000.
playing + none: turnStartedAt + idleLimitSec*1000.
종료: null.
activePlayers: setup에서 hanFirst면 순서에 맞는 1명, simultaneous면 아직 선택 안 한 전원. playing이면 차례인 1명.
onTimeout(state, player):
setup: { type: 'timeout' } → apply가 시드 RNG로 4가지 중 하나를 골라 chooseSetup과 같은 처리(onTimeout은 RNG를 쓰지 않는 순수 함수로 유지).
playing: { type: 'timeout' } → 시간패. 장기는 자동 수를 두지 않는다.
연결 끊김: 끊긴 플레이어가 행동해야 할 때 min(deadline, 끊김 + disconnectGraceSec)에 onTimeout.
8.6 종료 조건과 결과(GameResult)
interfaceJanggiResult{ranking:{playerId: PlayerId;rank: 1|2}[];// 무승부면 둘 다 1, 셋업 중 취소면 빈 배열
winner: Side|null;reason:'checkmate'|'resign'|'timeLoss'|'repetitionLoss'|'scoreBikjang'|'scorePasses'|'scoreMoveLimit'|'scoreRepetition'|'drawBikjang'|'drawPasses'|'drawRepetition'|'drawMoveLimit'|'drawAgreed'|'aborted';scores:{cho: number;han: number};// 항상 기록(한은 덤 포함)
moveCount: number;summaryKo: string;// "초 외통승 (63수)", "한 판정승 55.5 : 50 (빅장)"
}
9. UI/UX
판 방향: 내 진영이 아래. 초는 파랑(또는 초록), 한은 빨강. 기물은 팔각형(차·포·마·상 큰 것, 사·졸 작은 것) 한자 + 작은 한글 라벨 옵션("車 차")으로 한자에 익숙하지 않은 어린이 배려.
모바일 세로: 판(9×10)이 세로로 길어 세로 화면에 잘 맞는다. 폰 360px 폭에서 교차점 간격 약 38px이므로 기물 터치 영역을 교차점 간격 전체(정사각형)로 잡고, 선택 시 확대 강조.
조작: 기물 탭 → 이동 가능 점 표시(잡기 가능 칸은 빨간 링) → 탭. 포는 포다리 기물을 살짝 강조해 이유가 보이게.
상차림 화면: 4개 큰 카드(그림 + 이름 "안상차림 / 원앙마"), 한 화면에 미리보기.
"장군!" "멍군!" "빅장!" 큰 말풍선과 효과음(목소리 옵션). 외통 시 결과 모달.
상단에 양측 점수 바(초 72 : 한 73.5) 상시 표시 → 판정 상황 이해 도움.
한수쉼 버튼은 확인 모달. 반복 경고 배너.
규칙 보기: 기물별 행마 애니메이션, 멱(막힘) 예시, 포다리 예시, 빅장 설명.
10. 테스트 체크리스트
초기 배치 4가지 상차림 × 초/한 시점 매핑이 정확(비대칭인 SMSM 기준: 초 → (1,0) 상, (2,0) 마, (6,0) 상, (7,0) 마 / 한 → (7,9) 상, (6,9) 마, (2,9) 상, (1,9) 마).
마·상 멱: 첫 칸이 막히면 해당 방향 2곳 불가, 상은 둘째 칸 막힘도 확인.
포: 포다리 없이 이동 불가, 포를 포다리로 사용 불가, 포를 잡기 불가, 포다리 너머 여러 빈칸 이동 가능, 궁성 대각선 넘기.
차의 궁성 대각 이동: 모서리→반대 모서리(중심 비었을 때), 중심 막혔을 때 불가.
궁·사가 변 가운데 점에서 대각 이동 불가, 궁성 밖 이동 불가.
졸: 뒤로 불가, 상대 궁성 대각 앞 이동 가능, 자기 궁성에서 대각 불가.
스스로 장군에 들어가는 수 거부, 포다리 역할 기물을 치워 포 장군에 노출되는 수 거부(핀).
장군 중 한수쉼 거부, 외통 판정.
빅장(kjf): 잡기 전엔 마주봐도 무효, 첫 잡기 수 자체로는 불가, 그 이후 성립. 응수로 막으면 계속, 못 막으면 점수 판정(덤으로 동점 없음).
빅장(kja30): 한쪽이라도 30점 이상이면 무효.
반복수 forbid: 4번째 국면을 만드는 수 거부, 장군 중 유일한 수면 허용 후 판정 종료. lose: 실격패.
연속 한수쉼 → 점수 판정 / 무승부.
초읽기 시간패, 상차림 시간 초과 시 RNG 자동 선택이 같은 시드에서 동일.
simultaneous 상차림 중 상대·관전자 view에 선택값 누출 없음(선택 완료 여부만).