본문으로 건너뛰기

JunkBox apps/chess-ui 가이드

1. 애플리케이션 역할

  • apps/chess-ui는 Lichess sleepzz-bot 체스 샌드박스용 React + Vite 프런트엔드 앱임.
  • 사용자가 상대 유형, 상대 ID, 색상, Lichess 내장 AI 레벨을 선택하고 대국 시작 요청을 보낼 수 있는 화면을 제공함.
  • react-chessboard로 현재 대국 판세를 화면 안에 상시 렌더링함.
  • 실시간 WebSocket 이벤트를 받아 대국 상태, 시스템 상태, sleepzz-bot 선택 수, sleepzz-bot thought, 상대 move, Lichess 이벤트를 타임라인 카드로 표시함.
  • 저장된 게임 이력을 Tabulator로 표시하고, 과거 게임 선택 시 저장 로그를 조회하며, 완료된 과거 게임의 move 로그 선택 시 해당 로그 시점의 보드 상태를 표시함.
  • 진행 중인 게임 선택 시 WebSocket live timeline으로 복귀함.
  • 체스판은 서버가 전달한 turn log의 FEN을 기준으로 표시하며, 보드 하단 메타 영역에서 현재 판세 요약과 AI 판단 상태를 함께 표시함.
  • Lichess 사이트의 원본 대국 화면으로 이동하는 링크를 함께 제공함.
  • 스타일은 shared-ui 공통 자산을 사용하며 화면 전용 CSS 파일을 소유하지 않음.

2. 런타임 구성

  • 번들러는 Vite 6 계열을 사용함.
  • React + TypeScript strict 기준으로 구현함.
  • 체스판 UI는 react-chessboard를 사용함.
  • FEN 검증, 현재 턴, 체크 여부, legal move 개수, material balance 계산과 향후 사용자 move 입력 확장을 위한 체스 규칙 유틸은 chess.js를 사용함.
  • 개발 서버 기본 포트는 15002임.
  • 빌드 산출물은 apps/chess-ui/dist에 생성됨.
  • 운영에서는 chess-api가 이 산출물을 /chess-ui 경로로 직접 서빙함.
  • API와 WebSocket 호출은 같은 origin의 /api/chess/* 경로를 사용함.

3. 엔트리와 화면 구조

  • 메인 엔트리는 src/entries/chess.tsx임.
  • 루트 마운트 지점은 chess 화면 템플릿의 React root임.
  • 메인 화면 컴포넌트는 src/pages/ChessSandbox.tsx임.
  • 타입 계약은 src/types/models.ts에서 관리함.

4. 사용자 흐름

4.1 대국 시작

  • 사용자는 상대 ID를 입력할 수 있음.
  • 설정 영역은 상대 유형 / 색상, 상대 ID / AI 레벨 2개 행으로 구성됨.
  • 상대 유형 기본값은 Lichess 내장 AI이며, select 옵션도 Lichess 내장 AI, 일반/봇 계정 순서로 표시함.
  • 상대 유형이 일반/봇 계정이면 입력한 상대 ID 또는 서버 기본 상대 ID를 사용함.
  • 상대 유형이 Lichess 내장 AI이면 상대 ID 입력은 비활성화되고 AI 레벨 선택값을 사용함.
  • 대국 설정 필드는 항상 화면에 표시되며, 현재 상대 유형에 맞지 않는 필드만 disabled 상태가 됨.
  • 색상은 random, white, black 중 하나를 선택함.
  • 색상 select 표기값은 Random, White, Black 형식을 사용함.
  • AI 레벨 기본값은 Level 1이며, select 표기값은 Level 1부터 Level 8까지의 형식을 사용함.
  • 대국 시작 버튼은 POST /api/chess/challenges를 호출함.

4.2 상대 유형

  • 일반/봇 계정은 Lichess username 기반 challenge를 의미함.
  • 일반/봇 계정은 백엔드에서 /api/challenge/{username}으로 변환됨.
  • Lichess 내장 AI는 Lichess가 제공하는 AI challenge를 의미함.
  • Lichess 내장 AI는 백엔드에서 /api/challenge/ailevel 파라미터로 변환됨.
  • Lichess 내장 AI의 level은 1부터 8까지 선택함.

4.3 실시간 로그 관전

  • 화면 로드 시 /api/chess/ws/logs WebSocket에 연결함.
  • game 이벤트는 좌측 메타 영역의 game, side, status, started 값을 갱신함.
  • status 이벤트는 info/warning/error 카드로 표시함.
  • turn_log 이벤트는 턴 번호, 주체, 색상, thought, move, SAN/UCI, PGN, 시간, sleepzz-bot 생성 latency 정보를 카드로 표시함.
  • 로그 카드의 시간은 yyyy-mm-dd hh24:mi:ss 형식을 사용함.
  • 화면 중앙 체스판은 현재 선택된 active game의 최신 turn_log.fen을 기준으로 현재 판세를 갱신함.
  • 최신 turn_log.uci 또는 turn_log.move가 UCI 좌표 형식이면 출발 칸과 도착 칸을 마지막 수로 하이라이트함.
  • 진행 중인 게임이 없거나 유효한 FEN이 아직 없으면 기본 시작 포지션을 표시함.
  • 보드 방향은 최신 own_bot turn log의 side를 우선 사용하고, 없으면 현재 게임의 side를 사용해 Lichess처럼 내 봇이 하단에 오도록 표시함.
  • 보드 하단 메타 영역은 현재 FEN을 chess.js로 해석해 Turn, Check, Material, Legal 값을 계산함.
  • Material은 보드 방향 기준 내 봇 관점의 단순 기물 점수 득실임.
  • Legal은 현재 차례의 legal move 개수임.
  • Decision, Latency, Choice, Candidates, Fallback은 현재 보드 로그 또는 최신 own_bot 로그의 event_payloadcommentary_latency_ms를 기준으로 표시함.
  • 대국 ID가 있으면 체스판 헤더에서 Lichess 원본 대국 링크를 제공함.
  • 진행 중인 live 게임이면 체스판 헤더에 게임 포기 버튼을 표시하고, 사용자가 confirm을 승인하면 /api/chess/games/{game_id}/resign을 호출함.
  • turn_log 카드의 move 배지는 우리 white, 상대 black 같은 주체 라벨 우측에 표시함.
  • move 배지는 색상별로 구분하며 white 착수는 흰 배경과 검은 글씨, black 착수는 검은 배경과 흰 글씨 및 흰 테두리를 사용함.
  • move 배지 텍스트는 SAN 표기가 있으면 SAN을 우선 표시하고, 없으면 UCI 또는 move 값을 표시함.
  • SAN 표기에서 폰 이동은 도착 칸만 표시하며, 기물 이동은 K, Q, R, B, N 기호를 사용함.
  • SAN 표기에서 x는 capture, +는 check, #는 checkmate, O-OO-O-O는 castling을 의미함.
  • live timeline은 같은 game_id, ply_number, actor_type, event_type을 가진 turn_log를 다시 받으면 기존 카드를 갱신함.
  • live 로그가 여러 게임 이벤트를 포함하더라도 체스판 상태 계산은 active game 로그만 사용함.
  • own bot move는 sleepzz-bot이 직접 선택한 수와 현재 상황 / 착수 근거 thought를 함께 표시하며, 두 말머리는 badge 형태로 강조함.
  • 타임라인은 최신 생성 이벤트가 가장 위에 오도록 정렬함.
  • 로그 카드 하단의 PGN/기보 영역은 기본적으로 접힘 상태이며, 기보 토글 버튼으로 표시 여부를 전환함.
  • PGN 텍스트는 카드 좌우 폭 전체를 사용해 줄바꿈되도록 표시함.

4.4 게임 이력 조회

  • 좌측 하단 Game History 영역은 Tabulator 기반 페이지네이션 표임.
  • 게임 이력은 chess 앱을 통해 발생한 AI 대국을 대상으로 함.
  • 정렬 기준은 시작 시각 역순임.
  • 표시는 시작 시각, 내 색상, 결과를 중심으로 구성함.
  • 시작 시각은 yyyy-mm-dd hh24:mi:ss 형식을 사용함.
  • 결과의 은 파란색 텍스트, 는 빨간색 텍스트로 표시하고, started 같은 진행 상태는 일반 텍스트로 표시함.
  • 과거 게임 행을 선택하면 GET /api/chess/games/{game_id}/logs로 저장 로그를 조회해 우측 로그 패널에 표시함.
  • 과거 게임 행을 선택하면 저장 로그의 최신 FEN으로 중앙 체스판도 함께 갱신함.
  • 완료된 과거 게임의 우측 로그 패널에서는 유효한 fen을 가진 event_type=move 로그 카드만 보드 시점 선택 대상으로 동작함.
  • 완료된 과거 게임에서 move 로그 카드를 클릭하거나 키보드 Enter 또는 Space로 선택하면 중앙 체스판은 해당 로그의 fen을 우선 표시함.
  • 선택된 move 로그의 uci 또는 move가 UCI 좌표 형식이면 해당 시점의 마지막 수 출발 칸과 도착 칸을 하이라이트함.
  • game_start, game_finish, status, warning, error 같은 비 move 이벤트 로그는 저장된 fen이 있더라도 로그 카드 선택으로 보드 시점을 바꾸지 않음.
  • 진행 중인 게임이나 live timeline의 로그 카드는 보드 시점 선택 대상으로 동작하지 않음.
  • 과거 게임 행을 새로 선택하거나 live mode로 복귀하면 기존 move 로그 선택 상태는 초기화됨.
  • 현재 진행 중인 게임 행을 선택하면 WebSocket live timeline 표시로 복귀함.

5. API 계약

5.1 Challenge 요청

  • 요청 경로는 POST /api/chess/challenges임.
  • 요청 본문은 opponent_id, opponent_type, lichess_ai_level, side를 포함함.
  • opponent_typeuser 또는 lichess_ai를 사용함.
  • lichess_ai_level은 내장 AI challenge에서 사용함.
  • siderandom, white, black 중 하나임.

5.2 Challenge 응답

  • 응답은 challenge_id, game_id, status, raw를 포함함.
  • challenge 요청 실패는 status timeline error 카드로 표시함.

5.3 Game 목록과 이력

  • 화면 초기화 시 GET /api/chess/games를 호출해 현재 런타임에서 알고 있는 game 목록을 읽음.
  • 가장 최신 game을 좌측 메타 영역에 표시함.
  • GET /api/chess/game-historypage, size query를 받아 페이지 단위 게임 이력을 반환함.
  • 게임 이력 응답은 items, page, size, total, last_page를 포함함.
  • GET /api/chess/games/{game_id}/logs는 선택한 게임의 저장 로그 목록을 반환함.

5.4 WebSocket 이벤트

  • ChessSocketEventgame, turn_log, status union 타입으로 관리함.
  • status 이벤트의 생성 시각은 클라이언트 수신 시각을 기준으로 생성함.
  • turn log 이벤트의 생성 시각은 서버가 전달한 KST ISO 문자열을 사용함.
  • turn log 이벤트는 commentary_latency_ms를 포함할 수 있음.
  • own bot turn log의 event_payloadcommentary_status, choice, candidate_moves, fallback_reason 같은 보드 하단 판단 메타의 원본으로 사용될 수 있음.
  • 게임 이력에서 과거 로그를 보고 있는 동안에도 WebSocket 연결은 유지되며, 진행 중인 게임 선택 시 live timeline 데이터를 다시 표시함.
  • 저장 로그 응답의 각 turn_log.fen은 과거 게임의 특정 move 시점 보드 복원에 직접 사용됨.

6. 화면 레이아웃

6.1 PC/FHD 기준

  • PC에서는 3컬럼 구조를 사용함.
  • 좌측 컬럼은 360px 고정 폭을 사용함.
  • 좌측 컬럼은 대국 설정 카드, 연결/게임 상태 메타, Game History 영역을 포함함.
  • 중앙 컬럼은 react-chessboard 기반 현재 판세 보드와 보드 메타 영역을 포함함.
  • 중앙 컬럼은 체스판 실제 표시 폭에 맞춰 과도한 좌우 여백을 줄이고, 우측 로그 컬럼에 더 넓은 가로 공간을 배정함.
  • 중앙 컬럼의 보드 메타는 패널 하단에 붙이고, 남는 세로 공간은 체스판 표시 영역에 우선 배정함.
  • 중앙 컬럼의 보드 메타는 체스판 표시 폭과 같은 폭으로 정렬함.
  • PC/FHD 기준 보드 메타는 3열 grid를 사용함.
  • 보드 메타 표시 순서는 Last move, Turn, Check, Material, Legal, Ply, Bot time, Opponent, Result, Mode, Decision, Latency, Choice, Candidates, Fallback 순서임.
  • Fallback 행은 값이 길 수 있으므로 PC/FHD 기준 3열 전체 폭을 사용함.
  • 우측 컬럼은 실시간 에이전트 로그 패널임.
  • FHD 기준 페이지 전체 브라우저 스크롤이 생기지 않도록 화면 높이를 viewport에 맞춤.
  • 로그가 많아지면 우측 로그 패널 내부에서만 스크롤됨.
  • Game History와 우측 로그 영역은 같은 화면 하단 기준에 맞게 배치됨.
  • 좌측 상단 설정/상태 영역은 360px 폭 안에서 텍스트가 잘리지 않도록 2컬럼 grid와 요약 메타 grid를 사용함.
  • 좌측 메타 영역의 Started 행은 2컬럼 전체 폭을 차지하고 라벨은 좌측, 값은 우측 정렬해 yyyy-mm-dd hh24:mi:ss 값을 온전히 표시함.

6.2 모바일 기준

  • 모바일은 360px 가로 폭을 주요 기준으로 함.
  • 모바일에서는 매칭 설정, Game History, 체스판, 로그가 위아래로 배치되는 단일 컬럼 구조를 사용함.
  • 좁은 화면에서는 우측 로그 영역이 Game History 아래로 내려가며 360px 기준 폭에 맞춰 표시됨.
  • 체스판은 모바일에서도 항상 표시하며 정사각형 비율을 유지함.
  • 모바일 보드 메타는 2열 grid를 유지함.
  • 모바일에서도 공통 다크 톤과 Pretendard 폰트 규칙을 유지함.

7. 공통 UI 사용 규칙

  • chess-ui는 화면 전용 CSS 파일을 만들지 않음.
  • 체스 화면 전용에 가까운 레이아웃 클래스도 shared-uijb-chess-* 공통 base CSS에서 관리함.
  • 체스 HTML 템플릿은 CSS 링크를 직접 선언하지 않고 shared-ui stylesheet 매크로를 통해 hub-ui-base.css, hub-ui-desktop.css, Tabulator vendor CSS, junkbox-tabulator.css 조합을 로드함.
  • 입력과 select는 jb-dark-field를 사용함.
  • jb-dark-field select option은 흰 배경이 노출되지 않도록 공통 다크 option 스타일을 사용함.
  • 버튼은 공통 jb-buttonjb-button-full을 사용함.
  • 패널은 공통 jb-panel을 사용함.
  • 칩은 공통 jb-chip을 사용함.
  • 게임 이력 표는 Tabulator와 junkbox-tabulator.css 공통 자산을 사용함.
  • 현재 판세 보드 패널은 jb-chess-board-* 계열 클래스를 사용함.
  • 보드 하단 메타는 jb-chess-board-metajb-chess-meta-row 계열 클래스를 사용함.
  • jb-chess-board-meta는 모바일 2열, PC/FHD 3열 grid 기준으로 동작함.
  • 로그 카드 상태 구분은 jb-chess-log-card--status, jb-chess-log-card--warning, jb-chess-log-card--error 클래스를 사용하되, 기본 카드는 미니멀하고 플랫한 시각 규칙을 따름.
  • 완료된 과거 게임의 선택 가능한 move 로그 카드는 jb-chess-log-card--selectable을 함께 사용하고, 현재 보드에 반영된 로그 카드는 jb-chess-log-card--selected를 함께 사용함.
  • 로그 헤더의 기보 표시 전환은 jb-chess-log-toggle 계열 클래스를 사용함.
  • 진행 중인 게임 포기 버튼은 jb-chess-resign-button 클래스를 사용하며, 흰색 강조 버튼으로 표시함.
  • 좌측 메타와 보드 메타의 넓은 행은 jb-chess-meta-row--wide를 사용하고, 로그 카드의 move 배지는 jb-chess-log-move를 inline 요소로 사용함.
  • 로그 카드의 thought 말머리 badge는 jb-chess-thought-badge를 사용함.
  • 폰트는 shared-ui가 제공하는 Pretendard 기준을 따르며 별도 monospace를 강제하지 않음.

8. 시간 표시

  • 시간 표시 함수는 Asia/Seoul timezone을 명시함.
  • 날짜 객체 파싱에 실패하면 원본 문자열을 그대로 표시함.
  • created_at, started_at 같은 서버 전달 시간은 KST offset 포함 ISO 문자열을 기준으로 함.
  • 좌측 메타 영역의 Started, Game History의 시작 시각, 우측 로그 카드 시간은 yyyy-mm-dd hh24:mi:ss 형식을 사용함.

9. 운영 제약

  • 체스판은 화면 내에서 렌더링하지만 실제 Lichess 대국의 원본 상태 확인을 위해 Lichess 링크를 유지함.
  • 이번 단계에서 프론트엔드는 사용자 move 입력을 받지 않으며 보드는 관전 전용으로 동작함.
  • 사용자 직접 플레이를 도입할 경우 프런트는 chess.js로 1차 합법수 검증을 수행하고, 서버는 기존 python-chess 기준으로 최종 검증해야 함.
  • sleepzz-bot 수 선택과 서버 측 move legality 검증은 백엔드에서 수행함.
  • 사용자 노출 문구는 화면 컴포넌트에 한국어 문구로 직접 작성하고, 통일이 필요한 표현은 공통 UI 가이드와 현재 화면 문구를 기준으로 맞춤.

10. 관련 문서

  • 프로젝트 전체 구조는 docs/junkbox/common/guide-jb-core.md를 따름.
  • 체스 백엔드 구조는 docs/junkbox/apps/guide-apps-chess-api.md를 따름.
  • 공통 AI 런타임은 docs/junkbox/libs/guide-lib-shared-ai.md를 따름.
  • 공통 UI 자산 구조는 docs/junkbox/libs/guide-lib-shared-ui.md를 따름.
  • 공통 UI 규칙은 docs/junkbox/common/guide-jb-common-ui.md를 따름.