독자 수준별 구조 문서와 게이머 입문·전술 안내를 재정비한다

This commit is contained in:
2026-09-29 08:53:30 +00:00
parent 71155186b7
commit 08c3d0e936
34 changed files with 1175 additions and 271 deletions
+106
View File
@@ -0,0 +1,106 @@
# 코드가 처음인 사람을 위한 구조 안내
함수, 변수, 조건문은 알지만 웹 서버나 큰 프로젝트는 처음인 독자를 위한 글입니다.
이 문서를 읽고 나면 화면을 바꿀 파일, 게임 규칙을 바꿀 파일, 결과를 저장할 파일을
구분할 수 있습니다. 설치 전에 읽어도 됩니다.
## 먼저 게임을 한 문장으로 이해하기
플레이어는 장수 한 명의 행동을 미리 예약합니다. 서버는 장수의 차례가 되면
행동을 실행하고, 자원·도시·전투 결과를 저장합니다. 여러 플레이어와 컴퓨터 장수가
같은 세계에 참여하므로, 브라우저를 닫아도 세계는 계속 진행될 수 있습니다.
게임 자체가 낯설다면 [플레이어 첫 안내](../user/index.md)를 먼저 읽으세요.
## 화면과 서버는 다른 프로그램입니다
**프론트엔드**는 브라우저에서 실행되어 화면을 그리고 버튼 입력을 받는 프로그램입니다.
**백엔드**는 서버에서 실행되어 로그인과 권한을 확인하고 데이터를 읽거나 바꾸는
프로그램입니다. 버튼을 눌렀다고 브라우저가 직접 병력을 늘리는 것은 아닙니다.
브라우저는 서버에 요청하고, 서버가 허용한 결과를 받아 표시합니다.
**API**는 두 프로그램이 요청과 응답을 주고받는 약속입니다. 이 프로젝트는
TypeScript 타입으로 호출 형태를 연결하는 **tRPC**를 사용합니다. 타입이 맞더라도
사용자 권한과 실제 자원은 서버가 다시 검사해야 합니다.
예를 들어 “모병을 예약”하는 과정은 다음과 같습니다.
```text
브라우저: 병종과 수량을 고른다
↓ 저장 요청
게임 API: 로그인과 입력을 검사하고 예약 변경을 처리한다
↓ 예약 목록에 반영
턴 데몬: 내 차례가 되면 모병 조건을 다시 검사한다
↓ 실행 결과
데이터베이스: 병력, 자원, 로그와 남은 예약을 저장한다
↓ 알림을 받고 다시 조회
브라우저: 실제 결과를 보여 준다
```
예약 저장과 모병 실행은 서로 다른 일입니다. 예약 이후 돈을 쓰거나 도시를
잃었다면 실행에 실패할 수 있습니다. API를 읽을 때도 “요청을 받았다”와
“게임 행동이 끝났다”를 구분해야 합니다.
## 다섯 곳을 먼저 익히세요
| 위치 | 역할 | 찾아볼 때 |
| ------------------- | ------------------------------------- | -------------------------------------------- |
| `app/game-frontend` | Vue 화면과 사용자 입력 | 예턴 입력창이나 도시 화면을 이해하고 싶을 때 |
| `app/game-api` | 로그인·권한 검사, 조회·변경 요청 접수 | 누가 어떤 정보를 볼 수 있는지 궁금할 때 |
| `app/game-engine` | 시간에 맞춘 실행, 세계 상태와 저장 | 예약한 행동이 언제 실행되는지 궁금할 때 |
| `packages/logic` | 모병·내정·전투 등 게임 규칙 | 비용이나 성공 조건을 찾을 때 |
| `packages/infra` | 데이터베이스 구조와 연결 | 무엇을 영구히 저장하는지 찾을 때 |
`app`은 실행하는 프로그램, `packages`는 여러 프로그램이 함께 쓰는 코드 묶음입니다.
한 저장소에 여러 프로그램과 묶음이 있는 구성을 **모노레포**라고 합니다.
`pnpm`은 이 묶음들의 설치와 명령 실행을 관리합니다.
`resources`에는 지도, 병종, 시나리오 설정이 있습니다. 규칙 코드가 같아도 선택한
설정이 다르면 다른 게임이 됩니다. `tools`에는 검사·변환 같은 개발 도구가,
`docs`에는 지금 읽고 있는 문서가 있습니다.
## 로비와 게임을 나누는 이유
**Gateway**는 회원 계정과 로비를 담당합니다. 하나의 계정으로 여러 게임 서버에
들어갈 수 있지만, 각 게임의 장수·국가·진행 시점은 다릅니다. 운영 대상 게임을
코드에서는 **프로필(profile)**이라고 부릅니다. 프로필은 개인 프로필 사진과 다른
뜻입니다. **시나리오**는 그 게임에 적용할 시작 조건·지도·규칙 묶음입니다.
Gateway의 화면과 API는 `app/gateway-frontend`, `app/gateway-api`에 있습니다.
게임 업데이트와 실행 관리도 필요하므로 운영용 프로그램이 추가로 있습니다.
처음부터 이들을 모두 읽을 필요는 없습니다. [전체 구조](../architecture/overview.md)를
읽을 때 게임 진행 경로와 운영 경로를 나눠 보세요.
## 데이터는 어디에 남나요?
**메모리**는 실행 중인 프로그램이 빠르게 사용하는 작업 공간입니다. 프로그램을
다시 시작하면 사라질 수 있습니다. **PostgreSQL**은 세계 상태와 기록을 영구히
보관하는 데이터베이스입니다. **Prisma**는 코드에서 데이터베이스를 다루기 위한
도구이며, `packages/infra/prisma/game.prisma`는 게임 데이터 구조를 정의합니다.
턴 데몬은 세계를 메모리에 읽어 와 계산합니다. 계산이 끝나면 바뀐 값과 로그를
**트랜잭션(transaction)**으로 묶어 저장합니다. 모병으로 돈만 줄고 병력은 늘지
않는 식의 반쪽 저장을 막기 위한 경계입니다. 저장에 실패하면 메모리 상태도
되돌려야 다음 행동이 잘못된 세계에서 실행되지 않습니다.
**Redis**는 세션이나 작업 전달·알림 등에 쓰는 별도의 저장·통신 도구입니다.
게임 진행이 확정됐는지는 PostgreSQL 저장 결과를 기준으로 판단합니다.
**SSE**는 서버가 열린 브라우저로 변경 알림을 보내는 연결입니다. 알림이 늦었다고
곧바로 행동이 실패한 것은 아니므로 최신 데이터를 다시 조회합니다.
## 파일 하나에서 시작하는 읽기 연습
1. `packages/logic/src/actions/turn/general/che_모병.ts`를 엽니다.
입력값, 조건, 결과를 나눠 봅니다. 지금은 모든 계산을 외우지 않아도 됩니다.
2. `app/game-engine/src/turn/reservedTurnHandler.ts`를 읽으며 예약 행동이
실행되는 곳을 찾습니다. 규칙 파일과 실행 담당 파일이 다른 이유를 확인합니다.
3. `app/game-api/src/router/turns/`에서 예약을 바꾸는 요청을 찾습니다.
예약과 실행 사이에 시간이 흐른다는 점을 다시 확인합니다.
4. [파일 지도](./code-map.md)로 화면과 저장 담당 위치를 이어 봅니다.
파일명 뒤 `.ts`는 TypeScript 코드, `.vue`는 Vue 화면 구성요소, `.json`은 설정
데이터, `.prisma`는 데이터베이스 구조, `.test.ts`는 검증 코드입니다.
`import`는 다른 파일의 기능을 가져오는 선언입니다. 함수를 읽다가 모르는 이름을
만나면 선언 위치로 이동해 입력과 반환값부터 확인하세요.
다음은 [개발자 핸드북](./index.md)의 경험자 경로입니다. 실행 환경을 준비할 때는
저장소 `README.md`의 개발 환경과 [테스트 정책](../testing-policy.md)을 따릅니다.