본문 바로가기

AI

개발자가 로컬에서 Playwright 기반 스크립트 실행 엔진을 구축

반응형



이 가이드는 다음과 같은 목표를 가집니다:

> ✅ 개발자가 로컬에서 Playwright 기반 스크립트 실행 엔진을 구축하고,  
> ✅ 웹에서 저장된 JavaScript 코드를 동적으로 실행할 수 있도록 준비

---

# 🚀 Playwright 실행 엔진  
## 개발자 환경 설치 및 개발 가이드

---

## 📋 목차

1. [필요한 도구 설치](#1-필수-도구-설치)
2. [프로젝트 생성](#2-프로젝트-생성)
3. [Playwright 설치 및 설정](#3-playwright-설치-및-설정)
4. [기본 스크립트 실행기 개발](#4-기본-스크립트-실행기-개발)
5. [보안 강화: 샌드박스 적용 (vm2)](#5-보안-강화-샌드박스-적용-vm2)
6. [실행 예제 및 테스트](#6-실행-예제-및-테스트)
7. [다음 단계: API 서버 연동](#7-다음-단계-api-서버-연동)

---

## 1. ✅ 필수 도구 설치

### 1.1 Node.js 설치 (v16 이상 권장)
- 다운로드: [https://nodejs.org](https://nodejs.org)
- 확인:
  ```bash
  node --version  # v16.x 이상
  npm --version   # 8.x 이상
  ```

### 1.2 텍스트 에디터 (VS Code 추천)
- 다운로드: [https://code.visualstudio.com](https://code.visualstudio.com)
- 추천 확장:
  - **ESLint**
  - **Prettier**
  - **Playwright Test for VS Code**

---

## 2. 📂 프로젝트 생성

```bash
mkdir playwright-runner
cd playwright-runner
npm init -y
```

---

## 3. 🧰 Playwright 설치 및 설정

### 3.1 Playwright 설치

```bash
npm install @playwright/test
```

### 3.2 브라우저 설치

```bash
npx playwright install
```

> ✅ Chromium, Firefox, WebKit 설치됨

(선택) Linux 사용자라면 의존성도 설치:
```bash
npx playwright install-deps
```

---

## 4. 🧪 기본 스크립트 실행기 개발

### 4.1 파일 생성: `runner.js`

```javascript
// runner.js
const { chromium } = require('playwright');

// 브라우저 재사용 (성능 향상)
let browser = null;

async function getBrowser() {
  if (!browser) {
    browser = await chromium.launch({ headless: true }); // false로 하면 창 보임
  }
  return browser;
}

/**
 * 외부 JS 코드를 실행
 * @param {string} code - 사용자 정의 Playwright 코드 (문자열)
 * @returns {Promise<Object>} 실행 결과 또는 에러
 */
async function runUserScript(code) {
  const browser = await getBrowser();
  const context = await browser.newContext();
  const page = await context.newPage();

  try {
    // 주의: new Function은 보안 위험 있음 (나중에 샌드박스 처리)
    const scriptFunction = new Function('page', code);
    await scriptFunction(page);

    await context.close();
    return { success: true, message: '스크립트 성공적으로 실행됨' };
  } catch (error) {
    await context.close();
    return { success: false, error: error.message };
  }
}

module.exports = { runUserScript, getBrowser };
```

---

## 5. 🔐 보안 강화: 샌드박스 적용 (vm2)

> ⚠️ `new Function()`은 코드 주입 위험이 큼 → `vm2`로 샌드박스

### 5.1 vm2 설치

```bash
npm install vm2
```

### 5.2 보안 실행기: `secure-runner.js`

```javascript
// secure-runner.js
const { chromium } = require('playwright');
const { NodeVM } = require('vm2');

let browser = null;

async function getBrowser() {
  if (!browser) {
    browser = await chromium.launch({ headless: true });
  }
  return browser;
}

/**
 * 샌드박스 환경에서 코드 실행
 * @param {string} code - 사용자 코드
 * @returns {Promise<Object>}
 */
async function runUserScriptSecure(code) {
  const browser = await getBrowser();
  const context = await browser.newContext();
  const page = await context.newPage();

  const vm = new NodeVM({
    sandbox: { page }, // page 객체만 허용
    require: {
      external: false, // 외부 모듈 차단
      builtin: []      // 허용된 내장 모듈 없음
    }
  });

  try {
    // 비동기 코드를 즉시 실행하는 IIFE로 감쌈
    const wrappedCode = `(async () => { ${code} })();`;
    await vm.run(wrappedCode, __filename);

    await context.close();
    return { success: true, message: '성공' };
  } catch (error) {
    await context.close();
    return { success: false, error: error.message };
  }
}

module.exports = { runUserScriptSecure, getBrowser };
```

---

## 6. 🧪 실행 예제 및 테스트

### 6.1 테스트 스크립트: `test.js`

```javascript
// test.js
const { runUserScriptSecure } = require('./secure-runner');

const testScript = `
  await page.goto('https://example.com');
  console.log('페이지 제목:', await page.title());
  await page.screenshot({ path: 'example.png' });
  await page.waitForTimeout(1000);
`;

(async () => {
  console.log('🟢 스크립트 실행 시작...');
  const result = await runUserScriptSecure(testScript);

  if (result.success) {
    console.log('✅ 성공:', result.message);
  } else {
    console.error('❌ 실패:', result.error);
  }
})();
```

### 6.2 실행

```bash
node test.js
```

> ✅ 성공 시: `example.png` 파일 생성 및 로그 출력

---

## 7. 🔄 다음 단계: API 서버 연동 (Express 기반)

### 7.1 Express 설치

```bash
npm install express
```

### 7.2 `server.js` 생성

```javascript
// server.js
const express = require('express');
const { runUserScriptSecure } = require('./secure-runner');

const app = express();
app.use(express.json());

// 스크립트 실행 엔드포인트
app.post('/run', async (req, res) => {
  const { code } = req.body;

  if (!code) {
    return res.status(400).json({ error: '코드가 필요합니다.' });
  }

  console.log('실행 요청 수신');
  const result = await runUserScriptSecure(code);
  res.json(result);
});

const PORT = 3000;
app.listen(PORT, () => {
  console.log(`🚀 서버 실행 중: http://localhost:${PORT}`);
});
```

### 7.3 서버 실행

```bash
node server.js
```

### 7.4 테스트 요청 (예: curl)

```bash
curl -X POST http://localhost:3000/run \
  -H "Content-Type: application/json" \
  -d '{
    "code": "await page.goto(\"https://example.com\"); await page.screenshot({ path: \"test.png\" });"
  }'
```

---

## 🧰 개발 환경 정리

| 항목 | 버전/설정 |
|------|----------|
| Node.js | v16 이상 |
| npm | v8 이상 |
| Playwright | 최신 버전 |
| 브라우저 | Chromium, Firefox, WebKit |
| 보안 | vm2 사용 (샌드박스) |
| 실행 모드 | headless: true (배포용), false (디버깅용) |

---

## 🛠️ 개발 팁

| 팁 | 설명 |
|----|------|
| 🐞 디버깅 | `headless: false`로 설정하면 브라우저 창이 보임 |
| 📦 의존성 관리 | `package.json`에 스크립트 추가: `"dev": "node test.js"` |
| 🧹 정리 | 브라우저 종료: `await browser.close()` (개발 중에는 생략 가능) |
| ⏳ 타임아웃 | `page.setDefaultTimeout(10000)` 설정 가능 |
| 🖼️ 스크린샷 | 경로를 동적으로 설정: `screenshot-${Date.now()}.png` |

---

## 📚 참고 문서

- Playwright 공식 문서: [https://playwright.dev](https://playwright.dev)
- vm2 GitHub: [https://github.com/patriksimek/vm2](https://github.com/patriksimek/vm2)
- Express.js: [https://expressjs.com](https://expressjs.com)

---

## 🎁 최종 폴더 구조

```
playwright-runner/
├── node_modules/
├── runner.js               # 기본 실행기
├── secure-runner.js        # 보안 실행기 (vm2)
├── test.js                 # 테스트 코드
├── server.js               # Express API 서버
├── example.png             # 출력 파일
├── package.json
└── package-lock.json

반응형