---
title: "체스"
description: "체스판. 합법적인 수가 빛나고, 손에 든 기물이 칸이 거부되는 이유를 알려 주며, 폰은 무엇으로 승진할지 묻고, 봇은 실제 드래그로 응수합니다."
canonical_url: "https://zizigy.github.io/vue-dnd-kit/ko/docs/examples/chess"
---

> Index of every page on this site: [llms.txt](https://zizigy.github.io/vue-dnd-kit/llms.txt)

<example height="36rem" hint="흰 기물을 드래그하거나, Tab과 Space를 쓰세요. Bot이 흑을 둡니다" name="chess" title="체스">



</example>

## 살펴볼 점

- **Square.vue**: 64개 칸은 각각 `accept`가 함수인 드롭 대상입니다. 이 함수는 기물을 드래그하는
동안 호출되므로, 칸은 그 기물의 합법적인 수만 받습니다. 다른 곳에 드롭하면 거부되고 기물은 원래
자리로 돌아갑니다. 합법적인 칸은 `isAccepting`으로 빛납니다. 게임 자체에는 드래그를 추적하는 상태가
없습니다.
- **Piece.vue**: 일반 draggable이며, 그 편의 차례가 아니면 `disabled`입니다. `keyboardMode: 'droppables'`를 쓰면 화살표 키가 기물을 받는 칸, 즉 합법적인 수 사이만 건너뜁니다.
`snapCenterToCursor`는 기물을 포인터 아래 가운데에 두고, 터치하면 바로 드래그됩니다(`activation`).
`canDrag`는 아무것도 비활성화하지 않고도 봇의 기물과, 코드가 두는 동안의 체스판에 손대지 못하게
합니다. 코드가 시작한 드래그에는 입력 이벤트가 없으므로 그런 드래그에는 `canDrag`를 묻지 않습니다.
- **`chess.ts`**와 **game.ts**: 드래그 앤 드롭은 전혀 없는 규칙과 게임입니다. 체크를 고려한
합법적인 수, 캐슬링, 앙파상, 프로모션, 기물 부족에 의한 무승부, 기보 표기, 실행 취소. `onDrop`에서
수를 두면 새 국면이 만들어지고, 오버레이는 움직인 기물 위에 안착합니다.

## 손에 든 기물

**`PieceOverlay.vue`**는 기물의 `overlay` 컴포넌트입니다. 모든 오버레이가 받는 props를 읽고, 그 아래
칸에 드롭하면 무슨 일이 일어날지 알려 줍니다. 거부된 칸 위(`over`가 있고 `accepted`가 false)에서는
기물이 빨갛게 변하며 이유를 보여 주고, 기물을 잡는 수 위에서는 표적과 무엇을 잡는지 보여 줍니다.
드롭이 정해지면(`status: 'dropping'`) 힌트가 사라지고 기물이 내려앉습니다.

```ts [PieceOverlay.vue]
const props = defineProps<{
  active: Active;
  over: Over | null;
  accepted: boolean;
  status: DnDStatus;
}>();

const hint = computed(() => {
  const square = props.over ? String(props.over.id) : null;
  if (props.status !== 'dragging' || !square || square === piece.value.square) return null;
  if (props.accepted) {
    const taken = game.takes(piece.value.id, square);
    return taken ? { type: 'capture', text: `Takes ${NAMES[taken.kind]}` } : null;
  }
  // "Pinned", "King in check", "Not a legal move"
  return { type: 'refused', text: game.whyNot(piece.value.id, square) };
});
```

`Board.vue`는 `useDnD()`의 `active`와 `over`로, 기물이 출발한 칸에서 그 아래 칸까지 화살표를 그립니다.
프로바이더의 `haptics`는 터치 드래그가 합법적인 칸에 올 때와 드롭이 거부될 때 진동하고, `index.vue`의
`announcements`는 같은 이유를 읽어 줍니다. "f7, pinned".

## 프로모션: 답을 기다리는 드롭

마지막 랭크 위에서는 손에 든 폰이 승진할 기물을 보여 줍니다. 칸 자체의 `overOverlay`
(`PromotionOverlay.vue`)가 기물의 오버레이를 대신합니다. 놓으면 칸의 `onBeforeDrop`이 promise를
반환하고, 그 옆에 있는 페이지의 일반 요소인 **`PromotionPicker.vue`**가 어떤 기물로 할지 묻는 동안
오버레이는 칸 위에서 기다립니다. 기물을 고르면 `'accept'`, Escape나 Cancel은 `'reject'`(폰이 원래
자리로 돌아갑니다), Keep dragging은 `'continue'`로 답합니다. 폰이 다시 포인터를 따라가고, 다음
클릭에 드롭되며 다시 묻습니다.

```ts [Square.vue]
makeDroppable(el, {
  id: props.square,
  accept: ({ active }) => game.canMove(active.id, props.square),
  // Shown instead of the piece's overlay while a pawn is over the last rank.
  overOverlay: computed(() =>
    held.value?.kind === 'P' && isLastRank(props.square) ? PromotionOverlay : undefined,
  ),
  // A promise: the drop waits on the square until the picker answers.
  onBeforeDrop: ({ active }) =>
    game.promotes(active.id, props.square) ? game.askPromotion(props.square) : 'accept',
  onDrop: ({ active }) => game.move(String(active.id), props.square),
});
```

드롭 대상의 `onBeforeDrop`은 일반 드래그 이벤트를 받으며, `accept`가 드래그를 받았을 때만 호출됩니다.
정렬 가능한 리스트의 `onBeforeDrop`은 드롭의 `result`가 담긴 리스트의 `SortableDropEvent`를 받습니다.
대상의 `overOverlay`는 안착을 위해 `onDrop` 뒤에 한 번 더 읽힙니다. 그래서 게임(그때는 이미 퀸이 되어
있습니다)이 아니라 집을 때의 폰(`active.data`)을 확인합니다.

## 봇, 리플레이, 캐슬링: 코드가 재생하는 드래그

**`autoplay.ts`**는 `@vue-dnd-kit/actions`로 수를 둡니다. 봇의 수(`bot.ts`, 탐욕적인 봇),
**Replay**의 수, 그리고 직접 둔 것을 포함한 모든 캐슬링의 룩 이동입니다. 각각이 실제 드래그입니다.
집히고, 한 프레임씩 체스판 위를 미끄러지고, 칸의 `accept`와 `onDrop`을 거쳐 드롭되며, 직접 하는
드래그처럼 안내되고 애니메이션됩니다. 스크립트로 하는 프로모션은 승진할 기물을 함께 지정하므로 선택기가
열리지 않습니다. Escape는 다른 드래그처럼 스크립트 드래그도 취소합니다.

```ts [autoplay.ts]
const { startDrag } = useDnDActions();

const drag = startDrag(id); // from the piece's center
for (let frame = 1; frame <= FRAMES; frame++) {
  const t = easeOut(frame / FRAMES);
  const point = { x: start.x + (end.x - start.x) * t, y: start.y + (end.y - start.y) * t };
  if (!(await drag.moveTo(point))) return false; // cancelled meanwhile (Escape)
}
return drag.drop(); // true once it has landed, false if the square refused it
```

캐슬링은 손으로 둘 때처럼 두 번에 나누어 재생됩니다. 킹이 두 파일을 움직이고, 이어서 그 룩이 킹을
넘어 드래그되므로 룩이 순간 이동하는 일은 없습니다.

## 기물 잡기, 실행 취소, 새 게임

잡힌 기물은 그 편의 **`Tray.vue`**로 갑니다. `animateChanges`를 위해서만 쓰는 정렬 가능한 리스트입니다
(`disabled`이며, 그곳에서는 아무것도 드래그하지 않습니다). 그 `enter`는 함수입니다. 새 기물은 잡힌
칸에서 날아오고, 나머지는 자리를 내줍니다.

```ts [Tray.vue]
makeSortableList(tray, {
  items: () => props.pieces,
  getKey: (piece) => `taken-${piece.id}`,
  disabled: true,
  animateChanges: { enter: flyIn, leave: false },
});
```

Undo와 New game은 드래그 없이 국면을 바꾸므로 **`glide.ts`**가 직접 애니메이션합니다(FLIP). 모든
기물이 칸 위든 트레이 안이든 원래 있던 곳에서 지금 있는 곳으로 날아갑니다. 봇과 대국할 때 Undo는 직접
둔 수와 봇의 응수를 함께 되돌립니다.

더 읽어 보기: [드롭 결정하기](https://zizigy.github.io/docs/guides/drop-decisions) ·
[오버레이](https://zizigy.github.io/docs/guides/overlay#targets-that-change-the-overlay) ·
[프로그래밍 방식 드래그](https://zizigy.github.io/docs/guides/sensors#programmatic-drags) ·
[데이터 변경: `animateChanges`](https://zizigy.github.io/docs/guides/animations#data-changes-animatechanges) ·
[접근성](https://zizigy.github.io/docs/guides/accessibility)

## Sitemap

See the full [sitemap](https://zizigy.github.io/vue-dnd-kit/sitemap.md) for all pages.
