9.9 KiB
9.9 KiB
Play Uniare AI 작업 지침
1. 문서 목적
이 파일은 사람과 AI 작업자가 같은 기준으로 프로젝트를 이해하고 변경하기
위한 최상위 작업 규칙이다. 작업 시작 전에 이 파일과 docs/의 관련 문서를
반드시 읽는다.
2. 프로젝트 목표
- 기존 UA Deck Builder가 전달한 덱 데이터로 카드 시뮬레이터를 제공한다.
- 게임 규칙을 자동 판정하지 않는다.
- 사용자가 카드 이동, 뒤집기, 탭, 덱·손패·무덤 이동을 직접 수행한다.
- 혼자 연습하는 로컬 모드와 방 코드 기반 2인 세션을 제공한다.
- 각 플레이어는 자신이 소유한 카드만 조작한다.
- 상대 효과로 내 카드가 제거되는 경우에도 내 카드 소유자가 직접 처리한다.
3. 기술 기준
- 프런트엔드: Vue 3 Composition API와
<script setup> - 빌드 도구: Vite
- 스타일: Tailwind CSS 우선
- 상태 관리: Pinia
- 화면 이동: Vue Router
- 실시간 통신: Node.js와 Socket.IO
- 배포: Docker Compose와 Nginx
- 운영 환경: UGREEN NAS
새 라이브러리를 추가하기 전에는 기존 기술로 해결 가능한지 확인하고, 추가
이유와 영향을 docs/history.md에 기록한다.
4. 작업 시작 절차
AGENTS.md를 읽는다.docs/spec.md,docs/map.md,docs/todo.md를 읽는다.git status로 기존 변경 사항을 확인한다.- 관련 코드와 기존 패턴을 확인한다.
- 작업 범위를 작게 정하고 구현한다.
- 빌드와 관련 검증을 수행한다.
- 변경 성격에 맞는 문서를 업데이트한다.
- 작업 결과 형식에 맞춰 보고한다.
5. 코드 작성 규칙
5.1 공통
- 기존 패턴을 우선하며 최소 범위만 수정한다.
- 하나의 컴포넌트는 하나의 분명한 역할을 갖는다.
- 반복되는 값은 상수, 설정 또는 데이터 모델로 분리한다.
- 서버 주소, 이미지 원본 주소, 포트, 비밀값을 코드에 하드코딩하지 않는다.
- 임시 이름과 의미 없는 자리표시자 이름을 사용하지 않는다.
- 검증 없이 작업을 완료 처리하지 않는다.
5.2 네이밍
- 의미가 드러나는 영문 이름을 사용한다.
- 축약어를 남용하지 않는다.
temp,tmp,test1,data1,foo,bar,aaa같은 임시 이름을 금지한다.- Vue 컴포넌트 파일은 PascalCase를 사용한다.
- 함수와 변수는 camelCase를 사용한다.
- 상수는 의미가 분명한 경우 UPPER_SNAKE_CASE를 사용한다.
- 이벤트 이름은 발생한 사실을 나타내는 kebab-case를 사용한다.
- 불리언 값은
is,has,can,should접두어를 우선한다.
5.3 Vue
- Composition API와
<script setup>을 사용한다. - 화면 컴포넌트는
src/views/, 재사용 컴포넌트는src/components/에 둔다. - 비즈니스 상태를 컴포넌트 내부에 중복 저장하지 않는다.
- props를 직접 변경하지 않는다.
- 외부 효과와 구독은 컴포넌트 해제 시 반드시 정리한다.
5.4 스타일
- Tailwind CSS 유틸리티를 기본으로 사용한다.
- 반복되는 디자인 패턴은 컴포넌트로 추출한다.
- 임의 픽셀값은 게임 보드 좌표처럼 필요한 경우에만 사용하고 이유를 문서화한다.
- 카드 위치는 화면 픽셀이 아니라 보드 기준 정규화 좌표 또는 영역 기반으로 저장한다.
- 인라인
style은 드래그 좌표, 회전처럼 동적 계산이 필요한 경우에만 허용한다.
5.5 주석
- 함수와 클래스 앞에는 한국어 JSDoc 문서화 주석을 작성한다.
- 주석은 코드가 무엇을 하는지 반복하기보다 이유, 제약, 입력·출력을 설명한다.
- 복잡한 타입은
@typedef, 함수 입력은@param, 반환은@returns를 쓴다.
/**
* 덱 빌더에서 전달된 카드 목록을 실제 카드 인스턴스로 확장한다.
*
* @param {ImportedDeckCard[]} importedCards 수량이 포함된 덱 카드 목록
* @returns {GameCard[]} 고유 식별자가 부여된 카드 인스턴스 목록
*/
function createGameCardInstances(importedCards) {
return []
}
6. 상태 관리 원칙
6.1 상태 구분
- 서버 권위 상태: 방, 플레이어, 카드 소유권, 카드 영역, 덱 순서
- 공유 게임 상태: 필드 카드 좌표, 앞뒷면, 탭 상태, 무덤, 공개 영역
- 개인 비공개 상태: 내 손패 카드 식별 정보
- 로컬 UI 상태: 선택 카드, 드래그 중 여부, 메뉴 열림, 확대 상태
서버 권위 상태를 UI 컴포넌트의 로컬 상태만으로 확정하지 않는다.
6.2 카드 모델
카드는 덱 원본 정보와 플레이 중 인스턴스를 구분한다.
{
instanceId: 'player-1-card-17',
ownerId: 'player-1',
cardNumber: 'EX01BT/HTR-2-001',
imageFileName: 'EX01BT_HTR-2-001.png',
zone: 'deck',
position: { x: 0.5, y: 0.5 },
isFaceUp: false,
isTapped: false,
stackOrder: 17
}
instanceId는 같은 카드 여러 장을 구분하는 고유값이다.- 소유권은 게임 시작 후 임의로 변경하지 않는다.
- 카드 조작 요청은 요청자와
ownerId가 같은지 서버에서 확인한다. - 상대 손패에는 카드 번호와 이미지 정보를 전송하지 않는 방향을 우선한다.
6.3 실시간 이벤트
- 상태 전체를 매번 전송하기보다 명시적인 이벤트와 필요한 변경값을 전송한다.
- 이벤트 이름은
card-moved,card-flipped,deck-shuffled처럼 작성한다. - 재접속 시에는 서버의 최신 전체 스냅샷으로 복구한다.
- 같은 이벤트가 중복 수신되어도 상태가 망가지지 않도록 설계한다.
7. 데이터 처리 원칙
- 기존 덱 빌더 쿼리는 초기 덱 불러오기 용도로만 사용한다.
- 외부 입력은 신뢰하지 않고 스키마 검사 후 사용한다.
- 카드 이미지 URL은 환경변수의 원본 주소와 파일 경로를 조합한다.
- NAS 운영 환경에서는
/volume1/docker/projects/apps/uniare/config/www/images를 컨테이너에 읽기 전용으로 연결하고/card-assets경로로 제공한다. - 기존 Uniare의
config/www/images에는 쓰기 작업을 수행하지 않는다. - NAS 카드 파일 소유자 UID가
1000이므로 Nginx 워커는cardreader사용자(UID 1000)로 실행한다. 이 기준을 바꾸기 전에는 실제 NAS 파일 소유자를 다시 확인한다. - 로컬 Socket.IO 서버 기본 포트는
31873을 사용한다. - 운영 환경에서는 Socket.IO 포트를 브라우저에 직접 노출하지 않고 같은
도메인의
/socket.io경로로 리버스 프록시한다. - 컨테이너 내부 포트 중복은 허용되지만 NAS 호스트 공개 포트 중복은 금지한다.
- 운영 HTTP 호스트 포트는 Compose에 숫자를 직접 추가하지 않고
PLAYUNIARE_PRODUCTION_HTTP_PORT환경변수로 관리한다. - 기능 검증은 로컬에서 수행하고 NAS에는 운영 컨테이너만 배포한다.
- 운영 데이터가 생기면 마이그레이션 코드는 Git으로 관리하고, 백업과 재실행 안전성을 확인한 뒤 실행한다.
- NAS에서 즉석 데이터 변경 명령을 사용한 경우 같은 변경을 정식 마이그레이션 코드와 문서로 남긴다.
- URL 길이 문제를 고려해 장기적으로 덱 코드 또는
deckId방식을 도입한다. - URL, 로그, Git에 사용자 토큰이나 비밀값을 남기지 않는다.
- 오류가 있는 카드 한 장 때문에 전체 덱 불러오기가 중단되지 않도록 오류 목록을 사용자에게 명확히 보여준다.
8. 문서 업데이트 기준
- 기능 변경 →
docs/spec.md - 버그 수정 →
docs/update.md - 이슈 발견 →
docs/todo.md - 구조·기술·의사결정 변경 →
docs/history.md - 화면 변경 →
docs/map.md - 배포 변경 →
docs/deploy.md - 개발 명령 변경 →
docs/development.md - 데이터 구조·마이그레이션 변경 →
docs/migrations.md
문서 업데이트 누락을 금지한다.
9. 금지 사항
- 기존 스타일과 구조를 확인하지 않은 변경
- 요청 범위를 벗어난 대규모 리팩터링
- 서버 주소, 포트, API 키와 비밀번호 하드코딩
.env, 인증서, 개인키, NAS 계정 정보 커밋- 조작 권한 검증을 클라이언트에만 의존
- 상대의 비공개 손패 정보를 클라이언트로 전송
- 테스트 또는 빌드 검증 없이 완료 처리
- 관련 문서 업데이트 누락
10. 의사결정 원칙
- 기존 패턴 우선
- 최소 수정
- 유지보수 용이성
- 구조 파악 용이성
- 사용자가 직접 운영할 수 있는 단순성
- 비공개 정보와 카드 소유권의 안전성
11. 단계별 개발 계획
1단계: 로컬 싱글 플레이
- 대기실과 덱 URL 등록
- 덱 쿼리 파싱과 검증
- 카드 인스턴스 생성
- 첫 손패, 멀리건, 라이프 세팅
- 덱 셔플과 드로우
- 카드 이동, 뒤집기, 탭
- 손패, 필드, 무덤, 공개 영역
- 덱 위·아래 이동과 위에서 N장 공개
2단계: 2인 세션
- 로비와 방 생성·참가 완료
- 플레이어 좌석 할당 완료
- Socket.IO 서버와 NAS 리버스 프록시 완료
- 실시간 카드 이벤트 동기화
- 상대 손패 비공개 처리
- 연결 종료와 재접속 복구
3단계: 운영 안정화
- NAS Docker 배포
- 상태 저장소 도입 검토
- 모바일·태블릿 조작 개선
- 오류 로그와 상태 복구
- 관전자와 게임 기록은 별도 검토
구체적인 미완료 항목은 docs/todo.md에서 관리한다.
12. 작업 결과 보고 형식
- 수정 내용
- 변경 파일
- 검증 내용
- 문서 반영 여부
- 남은 이슈
13. 최종 체크리스트
AGENTS.md와 관련docs/확인- 코드 수정 완료
- 네이밍과 JSDoc 규칙 확인
- 빌드 또는 관련 검증 수행
- 변경 성격에 맞는 문서 반영
- 민감 정보 포함 여부 확인
- 남은 이슈를
docs/todo.md에 기록