"드래그 앤 드롭은 데스크톱 앱의 기본 인터랙션" — 파일을 끌어다 놓는 동작은 웹 API와 Electron의 네이티브 기능을 결합해서 구현합니다.


파일 드롭 받기 (외부 → 앱)

렌더러에서 HTML5 DnD API 사용

JAVASCRIPT
// renderer.js
const dropZone = document.getElementById('drop-zone');

// 기본 동작 방지 (파일을 열지 않도록)
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  dropZone.addEventListener(eventName, (e) => {
    e.preventDefault();
    e.stopPropagation();
  });
});

// 드래그 오버 시 시각적 피드백
dropZone.addEventListener('dragover', (e) => {
  dropZone.classList.add('drag-over');
});

dropZone.addEventListener('dragleave', () => {
  dropZone.classList.remove('drag-over');
});

// 드롭 이벤트 처리
dropZone.addEventListener('drop', async (e) => {
  dropZone.classList.remove('drag-over');

  const files = Array.from(e.dataTransfer.files);
  for (const file of files) {
    console.log('드롭된 파일:', file.name, file.path);
    // file.path는 Electron에서만 사용 가능 (웹에서는 없음)
    await window.electronAPI.processFile(file.path);
  }
});

전체 윈도우에서 드롭 방지

JAVASCRIPT
// 드롭 영역 밖에서 파일을 드롭하면 파일이 열리는 것 방지
document.addEventListener('dragover', (e) => e.preventDefault());
document.addEventListener('drop', (e) => e.preventDefault());

파일 드래그 내보내기 (앱 → 외부)

JAVASCRIPT
// main.js — 앱에서 외부로 파일 드래그
ipcMain.on('drag:start', (event, filePath) => {
  event.sender.startDrag({
    file: filePath,
    icon: path.join(__dirname, 'assets', 'drag-icon.png'),
  });
});
JAVASCRIPT
// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
  startDrag: (filePath) => ipcRenderer.send('drag:start', filePath),
});
JAVASCRIPT
// renderer.js
const fileItem = document.querySelector('.file-item');

fileItem.addEventListener('dragstart', (e) => {
  e.preventDefault();
  window.electronAPI.startDrag('/path/to/file.txt');
});

다중 파일 드롭 처리

JAVASCRIPT
// 여러 파일을 동시에 드롭할 때
dropZone.addEventListener('drop', async (e) => {
  const files = Array.from(e.dataTransfer.files);

  // 파일 필터링 (확장자 검사)
  const allowedExtensions = ['.txt', '.md', '.json', '.csv'];
  const validFiles = files.filter(file => {
    const ext = path.extname(file.name).toLowerCase();
    return allowedExtensions.includes(ext);
  });

  if (validFiles.length !== files.length) {
    showWarning(`${files.length - validFiles.length}개 파일이 지원되지 않는 형식입니다.`);
  }

  // 순차 처리
  for (const file of validFiles) {
    await processFile(file.path);
  }
});

폴더 드롭

JAVASCRIPT
dropZone.addEventListener('drop', async (e) => {
  const items = Array.from(e.dataTransfer.items);

  for (const item of items) {
    const entry = item.webkitGetAsEntry();
    if (entry) {
      if (entry.isDirectory) {
        console.log('폴더가 드롭됨:', entry.name);
        // 폴더 경로를 메인 프로세스로 전달
        const file = item.getAsFile();
        await window.electronAPI.openFolder(file.path);
      } else {
        console.log('파일이 드롭됨:', entry.name);
      }
    }
  }
});

CSS 스타일링

CSS
.drop-zone {
  border: 2px dashed #ccc;
  border-radius: 8px;
  padding: 2rem;
  text-align: center;
  transition: all 0.2s ease;
}

.drop-zone.drag-over {
  border-color: #4a9eff;
  background-color: rgba(74, 158, 255, 0.1);
}

.drop-zone.drag-over::after {
  content: '여기에 파일을 놓으세요';
  display: block;
  font-size: 1.2rem;
  color: #4a9eff;
}

면접 포인트 정리

  • 외부→앱: HTML5 DnD API 사용, file.path는 Electron 전용 속성
  • 앱→외부: webContents.startDrag()으로 네이티브 드래그
  • 전체 윈도우의 dragover/drop 기본 동작을 방지해야 함
  • 폴더 드롭도 webkitGetAsEntry()로 처리 가능

드래그 앤 드롭을 다뤘으면, 다음은 클립보드 API를 살펴봅시다.

댓글 로딩 중...