---
type: tutorial
title: 마켓플레이스 게임 빌드하기
summary: "빈 폴더에서 Caputchin 게임을 빌드하세요: SDK를 설치하고, 팩토리를 등록하고, 라운드별 컨텍스트에서 렌더링하고, 트레이스를 기록하고, 샌드박스된 iframe을 위해 번들하기."
---

이 튜토리얼이 끝나면 당신은 하나의 JavaScript 번들에 완전하고 플레이 가능한 Caputchin 게임을, 재생 가능하게 만들고 공개할 준비가 된 채로 갖게 됩니다. 이것은 지루한, 모든-단계를-밟는 버전입니다; 그것이 어디에 맞는지는 먼저 [마켓플레이스에 게임 출시하기](/docs/marketplace-game-development/overview)를 읽으세요.

Node와 번들러(esbuild, rollup, vite, 또는 webpack, 무엇이든)가 필요합니다. 빌드하는 데는 Caputchin 계정이 필요 없고, 공개하는 데만 필요합니다.

## 1. SDK 설치하기

```bash
npm install @caputchin/game-sdk
```

SDK는 작습니다: `register` 헬퍼에 TypeScript 타입. 그것은 위젯 런타임을 번들하지 않으니, 당신의 게임이 작게 유지됩니다.

## 2. 게임 팩토리 등록하기

게임은 **팩토리 함수**로 `register`를 한 번 호출하는 것입니다. 위젯은 샌드박스된 iframe 안에서 당신의 팩토리를 호출합니다; 그 호출이 그 자체로 시작 신호입니다(기다릴 별도의 시작 이벤트가 없습니다).

```js
import { register } from "@caputchin/game-sdk";

register((container, bridge, ctx) => {
  // container - a DOM element inside the sandboxed iframe; render into it.
  // bridge    - push-only channel to the host (pass / error / setSize).
  // ctx       - the per-round context (seed + resolved locale/skin/config).

  const cleanup = startGame(container, bridge, ctx);

  // Return an optional cleanup function; the widget calls it on unmount.
  return cleanup;
});
```

당신은 `register`에 매니페스트를 넘기지 **않습니다**; 서버가 색인 시점에 `caputchin.json`을 읽고 해소된 프리셋을 `ctx`로 당신의 팩토리에 내려보냅니다. 전체 표면은 [SDK 레퍼런스](/docs/marketplace-game-development/game-sdk-reference)를 보세요.

## 3. 라운드별 컨텍스트에서 렌더링하기

팩토리의 세 번째 인자 `ctx`는 방문자마다 바뀌는 모든 것을 지닙니다:

- `ctx.seed` - 라운드별 시드. **모든 무작위성을 이것에서 도출하세요**(5단계 참고). 검증된 세션 밖에서는 `null`.
- `ctx.locale` - 해소된 언어 문자열(`ctx.locale._lang`에 당신의 키), 또는 `null`.
- `ctx.skin` - 해소된 색상과 자산 URL(`ctx.skin._theme`, 늘 `light` 또는 `dark`, 에 당신의 키), 또는 `null`.
- `ctx.config` - 해소된 게임플레이 구성, 또는 `null`.

이것들에서 당신 자신의 키를 읽으세요; 당신은 결코 직접 프리셋을 해소하지 않습니다. 당신의 매니페스트가 맞는 블록을 선언하지 않으면 각각 `null`이니, 늘 내장 기본값으로 물러나세요:

```js
function startGame(container, bridge, ctx) {
  const title = ctx.locale?.title ?? "Tap the targets";
  const accent = ctx.skin?.accent_color ?? "#2da44e";
  const targetCount = ctx.config?.target_count ?? 5;
  // ...render with these...
}
```

당신의 레이아웃에 명시적 크기가 필요하면, 첫 페인트 뒤에 `bridge.setSize(width, height)`를 한 번 호출하세요.

## 4. 승리, 패배, 오류

당신의 게임이 플레이어가 언제 이기는지 정하고 브리지를 통해 호스트에 알립니다:

- 승리 시, 라운드의 [트레이스](#5-record-a-trace)(5단계)로 `bridge.pass({ trace })`를 호출하세요.
- 패배나 포기 시, **아무것도** 호출하지 마세요; 침묵이 실패 신호입니다.
- 게임 자체가 깨지면(자산 실패, 예외), `bridge.error({ code, message })`를 호출하세요. 그것은 플레이어 패배가 아니라 게임 내부 실패를 위한 것입니다.

```js
function onWin(traceString) {
  bridge.pass({ trace: traceString });
}
function onAssetFailure(err) {
  bridge.error({ code: "asset-load-failed", message: String(err) });
}
```

## 5. 트레이스 기록하기 [#5-record-a-trace]

이것이 게임을 **재생 가능**하게 만드는 단계입니다. 플레이어가 행동하면서, 결과를 모는 입력(어떤 표적을 어떤 순서로 맞췄는지, 중요하면 타이밍과 함께)을 기록하고 그 기록을 문자열이나 바이트 배열로 직렬화하세요: 그것이 당신의 **트레이스**입니다. 시드와 결합해, 트레이스는 당신의 로직이 정확한 결과를 재현하게 해야 하는데, 서버가 그것을 다시 실행하기 때문입니다.

두 규칙이 그것을 가능하게 합니다:

1. **모든 무작위 선택을 `ctx.seed`에서 도출하세요.** 결과에 영향을 주는 어떤 것에도 결코 `Math.random()`을 호출하거나 벽시계를 읽지 마세요.
2. **리플레이할 만큼 충분히 기록하세요.** 트레이스에 시드를 더한 것이 당신의 게임 로직에 대한 완전한 입력입니다.

```js
function makeRng(seed) {
  // seed is ctx.seed; feed it so the server reproduces the same sequence.
  let s = hashSeed(seed);
  return () => (s = (s * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff;
}
```

`seed`, 트레이스, 판정의 모양은 [리플레이 계약](/docs/marketplace-game-development/replay-contract)이 정합니다; 그 페이지는 이 기록된 플레이를 서버가 리플레이하는 헤드리스 `run` 산출물로 바꾸는 것을 다룹니다. 결정론적 로직을 손으로 쓰는 것이 까다롭게 들리면, [엔진 키트](/docs/marketplace-game-development/engine-kit)가 선택적으로 그것을 당신을 위해 합니다.

## 6. 샌드박스를 위해 번들하기

위젯은 정확히 하나의 스크립트 URL을 로드하니, 모든 것이 그 한 파일에 있어야 합니다. 당신의 번들러를 하나의 자족적 출력으로 구성하세요:

- 자산(스프라이트, 사운드, 폰트)을 데이터 URL로 인라인;
- 코드 분할 비활성화;
- WASM을 쓰면, base64로 임베드하고 바이트에서 인스턴스화.

불투명 오리진, 엄격한 CSP iframe 안에서 동작하지 **않는** 패턴:

- 경로 상대 `fetch('./sprite.png')` - 가져올 경로가 없음;
- 동적 `import('./chunk.js')` - 두 번째 URL이 차단됨;
- `new Worker('./worker.js')` - 대신 인라인 `Blob` URL에서 워커를 띄우세요;
- 런타임의 외부 CDN 가져오기 - `connect-src`가 그것들을 차단함.

## 7. 로컬에서 테스트하기

특별한 하니스는 없습니다. 위젯을 정적 HTML 페이지에 임베드하고, `game-src`를 당신의 로컬 번들 출력으로 향하게 하고, 브라우저에서 플레이하세요:

```html
<caputchin-game sitekey="cpt_pub_..." game-src="http://localhost:8080/game.js"></caputchin-game>
```

이것은 [커스텀 게임](/docs/custom-game-development/self-hosted-game)이 쓰는 같은 `game-src` 경로입니다; 마켓플레이스에서는 대신 저장소를 공개해 플랫폼이 당신을 위해 번들을 고정하게 합니다.

## 다음 단계

- [리플레이 계약](/docs/marketplace-game-development/replay-contract): 당신의 기록된 플레이를 게임을 재생 가능하게 만드는 `run` 산출물로 바꾸기.
- [caputchin.json 매니페스트](/docs/marketplace-game-development/the-caputchin-json-manifest): 게임, 그 프리셋, 그 번들 설명하기.
- [마켓플레이스에 공개하기](/docs/marketplace-game-development/publish-to-the-marketplace): 저장소를 태그하고 라이브로 가기.

## 함께 보기

- [게임 SDK 레퍼런스](/docs/marketplace-game-development/game-sdk-reference): 모든 export, 전부.
- [engine-kit](/docs/marketplace-game-development/engine-kit): 결정론을 무료로 얻는 선택적 방법.
