자유로운 개발일지/실험일지

[직링 자동화 프로그램] 퍼피티어, 일렉트론을 이용한 콘서트 예매 프로그램

그낙이 2025. 5. 26. 15:58
반응형

지난 글에서는 Selenium을 활용해 콘서트 예매 자동화를 성공적으로 구현해 보았습니다. 이번 글에서는 자동화 코드에 GUI를 입혀 누구나 버튼 한 번으로 자동화를 실행하고, 멈출 수 있는 직관적인 데스크탑 애플리케이션으로 발전시켜 보려고 합니다. 이를 위해 Puppeteer 기반 자동화 코드를 Electron으로 감싸고, 사용자가 콘서트 예매처와 ID, 날짜 및 시간을 직접 입력할 수 있는 인터페이스를 제공하는 방식으로 구현해볼 예정입니다.

 

⚠️ 이 글은 기술적인 호기심과 실험적인 분석을 위한 목적으로 작성되었습니다. 실제 예매 과정에서 이를 악용하거나 무단으로 활용하는 것은 서비스 약관 위반이 될 수 있으며, 법적 책임이 따를 수 있습니다. 또한, 이번 글에서는 코드 구현보다는 개념과 작동 원리에 집중하니 가볍게 읽어주시길 바랍니다.

 

Electron이란?

Electron은 웹 기술인 HTML, CSS, JavaScript를 이용해 데스크탑 애플리케이션을 만들 수 있게 해주는 프레임워크입니다.
즉, 웹사이트처럼 만들었지만, 실제로는 Windows / macOS / Linux에서 실행되는 앱(.exe, .app 등)으로 배포할 수 있게 도와줍니다. 내부적으로는 Chromium과 Node.js를 함께 사용해 하나의 앱 안에서 웹 페이지처럼 UI를 구성할 수 있을 뿐 아니라, 파일 시스템 접근이나 프로세스 실행 같은 데스크탑 환경에서만 가능한 기능도 함께 구현할 수 있습니다. 이런 구조 덕분에 웹과 로컬 기능을 모두 활용할 수 있는 강력한 앱을 만들 수 있는 것이죠. 즉, 웹 개발자에게는 익숙한 환경 그대로, 일반 사용자에게는 완성된 실행파일(.exe)을 제공할 수 있게 해주는 아주 강력한 도구입니다.

 

Electron로 GUI 만들기

0. 폴더 구조 : 폴더 구조는 아래와 같습니다.

puppeteer/
├── node_modules/
├── index.html
├── main.js
├── package-lock.json
├── package.json
├── preload.js
└── puppeteer.js

 

1. Electron 설치 : 퍼피티어 자동화 코드 글에서 우리는 이미 VS Code와 Node.js를 설치했습니다. 이제 필요한건 Electron 설치입니다. 아래 명령어를 통해 Electron을 설치해줍시다. --save-dev 옵션은 개발용 의존성으로 설치하겠단 의미입니다. 설치가 완료되면 기존 package.json 파일의 devDependencies에 Electron이 추가됩니다. 

npm install --save-dev electron

 

2. package.json 설정 확인 및 수정 : Electron 설치가 끝났다면, 만들 앱을 실행하거나 빌드하기 위해 몇 가지 설정을 추가해줍시다. 아래는 package.json 예시입니다. 

{
  "name": "puppeteer",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "start": "electron .",
    "build": "electron-packager . puppeteer-app --platform=win32 --arch=x64 --icon=icon.ico --overwrite"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "예매 자동화 프로그램",
  "dependencies": {
    "electron": "^36.3.1",
    "puppeteer": "^24.9.0"
  },
  "devDependencies": {
    "electron-packager": "^17.1.2"
  }
}

 

Electron 앱의 시작 파일인 main.js를 설정해주고, 해당 파일에서 창을 띄우거나 로직을 제어하는 코드를 작성하게 됩니다. script안에 start와 build는 npm start, npm run build 를 통해 각각 앱을 실행하고, .exe 파일을 만드는 명령어입니다. 

 

3. main.js 코드 작성 :  이제 Electron 앱의 진입점인 main.js 파일을 작성할 차례입니다. 이 파일은 앱 실행 시 가장 먼저 동작하는 메인 프로세스로 창을 띄우고, 이벤트 처리 등 역할을 합니다. 아래는 필요한 main.js 코드입니다. 

const { app, BrowserWindow, ipcMain } = require("electron");
const path = require("path");
const { startPuppeteer } = require("./puppeteer");

function createWindow() {
  const win = new BrowserWindow({
    width: 300,
    height: 400,
    webPreferences: {
      preload: path.join(__dirname, "preload.js"),
      contextIsolation: true,
      nodeIntegration: false,
    },
    autoHideMenuBar: true, // 상단 메뉴바 숨김
  });

  win.loadFile("index.html");
  win.webContents.openDevTools(); // 개발자 도구 자동 열기 (디버깅용)
}

app.whenReady().then(() => {
  createWindow();

  // 렌더러에서 보내는 puppeteer 실행 요청 처리
  ipcMain.handle("run-puppeteer", async () => {
    await startPuppeteer();
  });
});

// 모든 창이 닫혔을 때 앱 종료 (Mac은 예외)
app.on("window-all-closed", () => {
  if (process.platform !== "darwin") app.quit();
});

 

기존에 index.js로 생성한 파일 명을 puppeteer.js로 수정했습니다. 처음에는 Puppeteer 자동화 기능만 존재했기 때문에, 파일명을 단순하게 index.js로 만들었지만, 자동화 로직을 담당하는 파일의 역할이 명확해졌기 때문에, 이름을 다음과 같이 바꾸었습니다. 코드 주요 부분은 다음과 같습니다.

BrowserWindow()를 통해 앱 창을 생성하고, 여기서 창의 크기, 메뉴바 제거 여부, 보안 설정등을 지정합니다

win.loadFile("index.html")은 실제로 사용자에게 보여줄 HTML 페이지를 로드하는 부분입니다. 

ipcMain.handle("run-puppeteer", ...) 부분은 Electron 앱에서 버튼 클릭 시 Puppeteer 실행을 시켜줍니다.

 

4. index.html 및 preload.js 작성 : 사용자에게 보이는 창인 index.html은 빠르게 넘어가겠습니다. 코드는 아래와 같습니다. 

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <title>티켓 자동 예매기</title>
    <style>
      body {
        font-family: "Segoe UI", sans-serif;
        background: #fff;
        padding: 0px 10px;
      }

      h2 {
        display: flex;
        align-items: center;
        gap: 10px;
      }

      select,
      input,
      button {
        margin: 10px 0;
        padding: 8px;
        font-size: 14px;
        width: 100%;
        box-sizing: border-box;
      }

      button {
        background-color: #4caf50;
        color: white;
        border: none;
        cursor: pointer;
      }

      button:hover {
        background-color: #45a049;
      }

      .form-group {
        margin-bottom: 10px;
      }
    </style>
  </head>
  <body>
    <div class="form-group">
      <label for="platform">예매처</label>
      <select id="platform">
        <option value="sample_1">sample_1</option>
        <option value="sample_2">sample_2</option>
        <option value="sample_3">sample_3</option>
      </select>
    </div>

    <div class="form-group">
      <label for="concertId">콘서트 ID</label>
      <input type="text" id="concertId" placeholder="예: 123456" />
    </div>

    <div class="form-group">
      <label for="concertDate">선호 날짜</label>
      <input type="date" id="concertDate" />
    </div>

    <button id="run">예매 시작하기</button>

    <script>
      document.getElementById("run").addEventListener("click", () => {
        const platform = document.getElementById("platform").value;
        const concertId = document.getElementById("concertId").value;
        const concertDate = document.getElementById("concertDate").value;
        const concertTime = document.getElementById("concertTime").value;

        window.electronAPI.runPuppeteer({
          platform,
          concertId,
          concertDate,
          concertTime,
        });
      });
    </script>
  </body>
</html>

 

preload.js 코드는 아래와 같습니다.  preload.js의 역할은 렌더러(index.html)과 메인(main.js) 간의 통신을 안전하게 연결해주는 중간다리 역할을 합니다. Electron은 보안 강화를 위해 기본적으로 index.html 쪽 자바스크립트(렌더러 프로세스)에서 Node.js 기능이나 Electron의 내부 API(require("fs"), ipcRenderer, child_process)를 직접 쓰지 못하게 막아둡니다. 이걸 허용하면 악성 스크립트가 사용자 컴퓨터를 조작할 위험이 있기 때문이에요. 그래서 Node.js와 Electron 기능을 간접적으로 쓸 수 있게 도와주는 안전한 다리가 바로 preload.js + contextBridge입니다.

const { contextBridge, ipcRenderer } = require("electron");

contextBridge.exposeInMainWorld("electronAPI", {
  runPuppeteer: () => ipcRenderer.invoke("run-puppeteer"),
});

 

5. 실행해보기 : puppeteer.js(구 index.js) 코드는 이미 지난 시간에 작성했으니, 이제 Electron을 실행해봅시다. Ctrl + `을 눌러 VS Code 터미널 실행 후, npm start 명령어를 입력해줍시다. 

 

아래와 같은 창이 뜨고, 예매 시작하기 버튼을 누르면(아직은 예매처, 콘서트 ID, 날짜 등은 동작하지 않지만) index.html 자바스크립트의 window.electronAPI.runPuppeteer({...})가 실행되면서 preload.js를 거쳐 main.js에서 puppeteer.js가 실행되는 구조입니다.

 

콘서트 ID 등의 데이터를 선택해서 예매하기를 시작한다면, 넘긴다면 로그인 직후 원하는 콘서트 예매 페이지로 바로 이동이 가능해집니다. 기존에 작성한 코드는 로그인 이후 사용자가 페이지를 이동했을 때, id가 url에 포함되어 있다면 예매하기 버튼을 클릭하는 구조였기 때문에 데이터를 넘겨주고, 코드를 수정해봅시다. 현재 흐름은 HTML에서 데이터 선택 > preload.js가 중간다리 > main.js에서 Electron 시작 > puppeteer.js 호출입니다. 

 

HTML 자바스크립트에서는 이미 데이터를 전달하게 코드를 작성했으므로, preload.js, main.js 그리고 puppeteer.js를 수정해줍시다. 

// preload.js
contextBridge.exposeInMainWorld("electronAPI", {
  runPuppeteer: (data) => ipcRenderer.invoke("run-puppeteer", data),
});
// main.js
...
app.whenReady().then(() => {
  createWindow();

  ipcMain.handle("run-puppeteer", async (e, data) => {
    await startPuppeteer(data);
  });
});
...
// puppeteer.js 
const puppeteer = require("puppeteer");

async function startPuppeteer({ platform, concertId, concertDate }) {
  console.log(platform, concertId, concertDate);
...

 

이제 각각 예매처별 코드를 분기처리해 사용자가 선택한 사이트에 접속이 가능하고, 콘서트ID, 날짜를 활용해서 로그인 직후 바로 사이트 이동 및 선호 날짜 우선 선택이 가능하게 되었습니다. 데이터가 잘 넘어오는지 확인해봅시다. 위의 코드에 console.log도 남겨놨으니 바로 터미널에서 npm start로 실행해보면, 터미널에 데이터가 잘 넘어와서, 찍히는 것을 확인할 수 있었습니다.

 

이제 이 데이터를 바탕으로 특정 예매 사이트 코드 분기, 특정 콘서트 사이트 이동, 특정 날짜 선택이 가능해졌습니다. 그렇다면 이제 프로그램으로 뽑아봅시다. 맨 처음 package.json에 작성한대로 npm run build를 터미널에 입력해봅시다.

루트 폴더 안에 puppeteer-app-win32-x64 폴더가 생성되고, .dll, .bin, resources 등 잡다한게 너무 많이생겼습니다. 보기가 안좋으니 단일 실행 파일 형태의 Electron앱으로 바꿔봅시다. 우선 electron packager 대신에 builder을 설치해야 합니다.

npm install --save-dev electron-builder

 

그리고 package.json을 수정해줍시다. Windows 기준입니다. 

{
  "name": "puppeteer",
  "version": "1.0.0",
  "description": "예매 자동화 프로그램",
  "main": "main.js",
  "author": "",
  "license": "ISC",
  "scripts": {
    "start": "electron .",
    "build": "electron-builder --win portable --x64"
  },
  "build": {
    "win": {
      "icon": "icon.png"
    },
    "mac": false
  },
  "dependencies": {
    "puppeteer": "^24.9.0"
  },
  "devDependencies": {
    "electron": "^36.3.1",
    "electron-builder": "^26.0.12"
  }
}

 

npm run build 하는데 계속 오류가 나서 저는 관리자 권한으로 명령 프롬프트(CMD)를 실행해준 뒤, 다시 명령어를 입력했습니다.

dist 폴더에 puppeteer 1.0.0.exe가 생성된 걸 확인할 수 있습니다.

 

폴더를 배경화면으로 복사하고, 실행해봅시다. 짜잔~ 잘 실행됩니다. 

 

이렇게 해서 퍼피티어 기반 자동화 코드에 Electron을 결합해, 누구나 쉽게 사용할 수 있는 직관적인 데스크탑 예매 도구를 만들어보았습니다. 콘솔에서만 실행되던 자동화가 버튼 클릭 하나로 작동하는 GUI 앱으로 발전하면서, 활용성과 접근성이 크게 향상되었죠. 앞으로는 예매 성공률을 높이기 위한 로직 개선, 다양한 예매처 지원, 예매 상황 실시간 표시 등 사용자 경험을 더욱 향상시킬 수 있는 기능들을 고민해볼 수 있겠습니다. 여러분도 이 글을 참고해 자신만의 자동화 도구를 만들어보고, 나아가 실생활에서 유용한 프로그램으로 발전시켜 보시길 바랍니다. 물론 이 프로그램은 순수한 기술적 호기심과 학습 목적에서 구현된 것이며, 실제 예매 시스템을 자동화해 사용하는 것은 각 플랫폼의 서비스 약관 위반이 될 수 있습니다. 따라서 학습용으로만 활용해주시길 바랍니다. 그럼 다음 글에서 더 재미있는 주제로 찾아뵙겠습니다.

 

언제나처럼 — 시작은 삽질이지만, 끝은 지식입니다.

 

 

반응형