리펙토링: IPC(Inter-Process Communication) 코드

This commit is contained in:
2026-05-14 10:46:00 +09:00
parent 9d6a7085dc
commit 8b7bd3864a
4 changed files with 107 additions and 84 deletions
+74
View File
@@ -0,0 +1,74 @@
const { ipcMain } = require('electron');
const path = require('path');
const fs = require('fs');
const EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.tif', '.webp', '.heic', '.heif']);
function register() {
ipcMain.handle('list-images', async (_event, folderPath) => {
try {
const files = fs.readdirSync(folderPath)
.filter(f => EXTS.has(path.extname(f).toLowerCase()))
.map(f => {
const fullPath = path.join(folderPath, f);
const stat = fs.statSync(fullPath);
return { name: f, path: fullPath, mtime: stat.mtimeMs, size: stat.size };
})
.sort((a, b) => a.name.localeCompare(b.name, 'ko'));
return files;
} catch (e) {
return [];
}
});
ipcMain.handle('read-image', async (_event, filePath) => {
try {
const data = fs.readFileSync(filePath);
const ext = path.extname(filePath).toLowerCase().replace('.', '');
const mime = (ext === 'jpg' || ext === 'jpeg') ? 'jpeg'
: ext === 'png' ? 'png'
: ext === 'gif' ? 'gif'
: ext === 'webp' ? 'webp'
: 'jpeg';
return `data:image/${mime};base64,${data.toString('base64')}`;
} catch (e) {
return null;
}
});
ipcMain.handle('rename-files', async (_event, { folderPath, files, prefix, digits }) => {
const errors = [];
const pad = (n) => String(n + 1).padStart(digits, '0');
// 1단계: 임시 이름으로 이동 (충돌 방지)
const tmpMap = [];
for (let i = 0; i < files.length; i++) {
const src = files[i].path;
const ext = path.extname(files[i].name).toLowerCase();
const tmp = path.join(folderPath, `__tmp_rename_${i}_${Date.now()}${ext}`);
try {
fs.renameSync(src, tmp);
tmpMap.push({ tmp, ext, idx: i });
} catch (e) {
errors.push(`${files[i].name}: ${e.message}`);
}
}
// 2단계: 최종 이름으로 이동
const renamed = [];
for (const { tmp, ext, idx } of tmpMap) {
const newName = `${prefix}_${pad(idx)}${ext}`;
const dest = path.join(folderPath, newName);
try {
fs.renameSync(tmp, dest);
renamed.push({ oldPath: tmp, newPath: dest, newName });
} catch (e) {
errors.push(`${newName}: ${e.message}`);
}
}
return { renamed, errors };
});
}
module.exports = { register };
+18
View File
@@ -0,0 +1,18 @@
const { ipcMain, dialog, shell } = require('electron');
function register(mainWindow) {
ipcMain.handle('select-folder', async () => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory'],
title: '이미지 폴더 선택',
});
if (result.canceled) return null;
return result.filePaths[0];
});
ipcMain.handle('reveal-folder', async (_event, folderPath) => {
shell.openPath(folderPath);
});
}
module.exports = { register };
+9
View File
@@ -0,0 +1,9 @@
const folder = require('./folder');
const files = require('./files');
function registerAll(mainWindow) {
folder.register(mainWindow);
files.register();
}
module.exports = { registerAll };
+6 -84
View File
@@ -1,6 +1,6 @@
const { app, BrowserWindow, ipcMain, dialog, shell } = require('electron');
const { app, BrowserWindow } = require('electron');
const path = require('path');
const fs = require('fs');
const { registerAll } = require('./ipc');
let mainWindow;
@@ -27,7 +27,10 @@ function createWindow() {
});
}
app.whenReady().then(createWindow);
app.whenReady().then(() => {
createWindow();
registerAll(mainWindow);
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') app.quit();
@@ -36,84 +39,3 @@ app.on('window-all-closed', () => {
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});
// ── IPC 핸들러 ──────────────────────────────────────
ipcMain.handle('select-folder', async () => {
const result = await dialog.showOpenDialog(mainWindow, {
properties: ['openDirectory'],
title: '이미지 폴더 선택',
});
if (result.canceled) return null;
return result.filePaths[0];
});
ipcMain.handle('list-images', async (_event, folderPath) => {
const EXTS = new Set(['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.tif', '.webp', '.heic', '.heif']);
try {
const files = fs.readdirSync(folderPath)
.filter(f => EXTS.has(path.extname(f).toLowerCase()))
.map(f => {
const fullPath = path.join(folderPath, f);
const stat = fs.statSync(fullPath);
return { name: f, path: fullPath, mtime: stat.mtimeMs, size: stat.size };
})
.sort((a, b) => a.name.localeCompare(b.name, 'ko'));
return files;
} catch (e) {
return [];
}
});
ipcMain.handle('read-image', async (_event, filePath) => {
try {
const data = fs.readFileSync(filePath);
const ext = path.extname(filePath).toLowerCase().replace('.', '');
const mime = (ext === 'jpg' || ext === 'jpeg') ? 'jpeg'
: ext === 'png' ? 'png'
: ext === 'gif' ? 'gif'
: ext === 'webp' ? 'webp'
: 'jpeg';
return `data:image/${mime};base64,${data.toString('base64')}`;
} catch (e) {
return null;
}
});
ipcMain.handle('rename-files', async (_event, { folderPath, files, prefix, digits }) => {
const errors = [];
const pad = (n) => String(n + 1).padStart(digits, '0');
// 1단계: 임시 이름으로 이동 (충돌 방지)
const tmpMap = [];
for (let i = 0; i < files.length; i++) {
const src = files[i].path;
const ext = path.extname(files[i].name).toLowerCase();
const tmp = path.join(folderPath, `__tmp_rename_${i}_${Date.now()}${ext}`);
try {
fs.renameSync(src, tmp);
tmpMap.push({ tmp, ext, idx: i });
} catch (e) {
errors.push(`${files[i].name}: ${e.message}`);
}
}
// 2단계: 최종 이름으로 이동
const renamed = [];
for (const { tmp, ext, idx } of tmpMap) {
const newName = `${prefix}_${pad(idx)}${ext}`;
const dest = path.join(folderPath, newName);
try {
fs.renameSync(tmp, dest);
renamed.push({ oldPath: tmp, newPath: dest, newName });
} catch (e) {
errors.push(`${newName}: ${e.message}`);
}
}
return { renamed, errors };
});
ipcMain.handle('reveal-folder', async (_event, folderPath) => {
shell.openPath(folderPath);
});