다이얼로그 — 파일 열기, 저장, 메시지 박스
"다이얼로그는 OS 네이티브 UI를 활용하는 대표 기능" — 웹에서는 불가능한 네이티브 파일 선택기와 시스템 알림을 Electron에서 쓸 수 있습니다.
파일 열기 다이얼로그
const { dialog, ipcMain } = require('electron');
ipcMain.handle('dialog:openFile', async (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
const result = await dialog.showOpenDialog(win, {
title: '파일 선택',
defaultPath: app.getPath('documents'),
properties: ['openFile', 'multiSelections'],
filters: [
{ name: '텍스트 파일', extensions: ['txt', 'md'] },
{ name: '이미지', extensions: ['jpg', 'png', 'gif'] },
{ name: '모든 파일', extensions: ['*'] },
],
});
if (result.canceled) return null;
return result.filePaths;
});
properties 옵션
| 옵션 | 설명 |
|---|---|
openFile | 파일 선택 허용 |
openDirectory | 디렉토리 선택 허용 |
multiSelections | 다중 선택 허용 |
showHiddenFiles | 숨김 파일 표시 |
createDirectory | macOS: 디렉토리 생성 버튼 표시 |
파일 저장 다이얼로그
ipcMain.handle('dialog:saveFile', async (event, content) => {
const win = BrowserWindow.fromWebContents(event.sender);
const result = await dialog.showSaveDialog(win, {
title: '파일 저장',
defaultPath: path.join(app.getPath('documents'), 'untitled.txt'),
filters: [
{ name: '텍스트 파일', extensions: ['txt'] },
{ name: 'Markdown', extensions: ['md'] },
],
});
if (result.canceled) return false;
// 선택한 경로에 파일 저장
await fs.promises.writeFile(result.filePath, content, 'utf-8');
return true;
});
메시지 박스
ipcMain.handle('dialog:confirm', async (event, options) => {
const win = BrowserWindow.fromWebContents(event.sender);
const result = await dialog.showMessageBox(win, {
type: 'question', // none, info, warning, error, question
title: options.title || '확인',
message: options.message,
detail: options.detail, // 부가 설명
buttons: ['확인', '취소'],
defaultId: 0, // 기본 선택 버튼
cancelId: 1, // ESC로 닫을 때 반환값
checkboxLabel: '다시 묻지 않기', // 체크박스 (선택)
});
return {
response: result.response, // 클릭한 버튼 인덱스
checkboxChecked: result.checkboxChecked,
};
});
메시지 박스 타입별 아이콘
info: 정보 아이콘 (i)warning: 경고 아이콘 (!)error: 에러 아이콘 (x)question: 질문 아이콘 (?)
에러 다이얼로그
// 앱이 준비되기 전에도 사용 가능
dialog.showErrorBox('치명적 오류', '앱을 시작할 수 없습니다.');
showErrorBox는 동기 함수이고, app.whenReady() 전에도 사용할 수 있어서 초기화 에러 표시에 유용합니다.
실전 패턴: 저장하지 않은 변경사항 확인
// 메인 프로세스에서 창 닫기 전 확인
mainWindow.on('close', async (event) => {
if (!hasUnsavedChanges) return;
event.preventDefault();
const { response } = await dialog.showMessageBox(mainWindow, {
type: 'warning',
title: '저장하지 않은 변경사항',
message: '변경사항을 저장하시겠습니까?',
buttons: ['저장', '저장 안 함', '취소'],
defaultId: 0,
cancelId: 2,
});
switch (response) {
case 0: // 저장
await saveDocument();
mainWindow.destroy();
break;
case 1: // 저장 안 함
mainWindow.destroy();
break;
// case 2: 취소 — 아무것도 안 함
}
});
preload에서의 다이얼로그 API 노출
// preload.js
contextBridge.exposeInMainWorld('electronAPI', {
openFile: () => ipcRenderer.invoke('dialog:openFile'),
saveFile: (content) => ipcRenderer.invoke('dialog:saveFile', content),
confirm: (options) => ipcRenderer.invoke('dialog:confirm', options),
});
// renderer.js
document.getElementById('openBtn').addEventListener('click', async () => {
const files = await window.electronAPI.openFile();
if (files) {
files.forEach(f => console.log('선택:', f));
}
});
document.getElementById('deleteBtn').addEventListener('click', async () => {
const { response } = await window.electronAPI.confirm({
title: '삭제 확인',
message: '정말 삭제하시겠습니까?',
detail: '이 작업은 되돌릴 수 없습니다.',
});
if (response === 0) {
performDelete();
}
});
면접 포인트 정리
dialog모듈은 메인 프로세스에서만 사용 가능showOpenDialog,showSaveDialog는 비동기(Promise),showErrorBox는 동기properties배열로 파일/폴더/다중선택을 세밀하게 제어showMessageBox의cancelId는 ESC키 또는 창 닫기 시 반환값- 실전에서는 "저장하지 않은 변경사항" 패턴이 필수
다이얼로그를 마스터했으면, 다음은 Node.js fs 모듈과 연계한 파일 시스템 접근을 다뤄봅시다.
댓글 로딩 중...