마우스만으로 조작하면 느리다. 단축키 지원하자.


단축키 목록

기능
Delete선택된 객체 삭제
R90도 회전
Enter편집 폼 열기
Escape닫기 / 선택 해제
↑↓←→1px 이동
Shift + 화살표10px 이동
M측정 모드 토글
D문 열림 방향
H문 경첩 위치
Ctrl+ZUndo
Ctrl+YRedo

전역 이벤트 리스너

onMounted(() => {
  window.addEventListener('keydown', handleKeyDown);
});

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeyDown);
});

컴포넌트 마운트 시 등록, 언마운트 시 해제.


핸들러

function handleKeyDown(e: KeyboardEvent) {
  // 입력 중이면 무시
  if (isInputFocused()) return;
  
  // Ctrl/Cmd 조합
  if (e.ctrlKey || e.metaKey) {
    if (e.key === 'z') {
      e.preventDefault();
      undo();
    }
    if (e.key === 'y') {
      e.preventDefault();
      redo();
    }
    return;
  }
  
  // 단일 키
  switch (e.key) {
    case 'Delete':
    case 'Backspace':
      deleteSelected();
      break;
    case 'r':
    case 'R':
      rotateSelected(90);
      break;
    case 'Enter':
      openEditForm();
      break;
    case 'Escape':
      closeAll();
      break;
    case 'm':
    case 'M':
      toggleMeasureMode();
      break;
    case 'ArrowUp':
      moveSelected(0, e.shiftKey ? -10 : -1);
      break;
    case 'ArrowDown':
      moveSelected(0, e.shiftKey ? 10 : 1);
      break;
    case 'ArrowLeft':
      moveSelected(e.shiftKey ? -10 : -1, 0);
      break;
    case 'ArrowRight':
      moveSelected(e.shiftKey ? 10 : 1, 0);
      break;
  }
}

입력 중 무시

텍스트 입력 중에 단축키 작동하면 안 됨:

function isInputFocused() {
  const activeElement = document.activeElement;
  const tagName = activeElement?.tagName.toLowerCase();
  
  return tagName === 'input' || tagName === 'textarea';
}

회전

function rotateSelected(angle: number) {
  if (!selectedId.value) return;
  
  const furniture = store.getFurniture(selectedId.value);
  if (furniture) {
    store.updateFurniture(selectedId.value, {
      rotation: (furniture.rotation + angle) % 360
    });
  }
}

90도씩 회전. 360 넘으면 0으로.


이동

function moveSelected(dx: number, dy: number) {
  if (!selectedId.value) return;
  
  const furniture = store.getFurniture(selectedId.value);
  if (furniture) {
    store.updateFurniture(selectedId.value, {
      x: furniture.x + dx,
      y: furniture.y + dy
    });
  }
}

Shift 누르면 10px, 아니면 1px.


문 전용 단축키

문 선택됐을 때만:

if (selectedDoor.value) {
  if (e.key === 'd' || e.key === 'D') {
    toggleDoorDirection();
  }
  if (e.key === 'h' || e.key === 'H') {
    toggleDoorHinge();
  }
}

충돌 방지

브라우저 기본 단축키랑 겹치면 안 됨.

  • Ctrl+S: 저장 아님 (브라우저가 가로챔)
  • Ctrl+Z: Undo는 괜찮음

e.preventDefault() 안 하면 브라우저 동작이 우선.


다음 글에서 Undo/Redo 구현.

#13 - Undo/Redo