⏱️ 예상 읽기 시간: 15분

서론

웹사이트를 자동으로 탐색하고, 폼을 작성하고, 버튼을 클릭하며, 정보를 추출하는 AI 에이전트를 만들고 싶다면 Browserable이 완벽한 선택입니다.

Browserable은 Web Voyager 벤치마크에서 90.4%의 성능을 달성한 오픈소스 브라우저 자동화 라이브러리로, AI 에이전트가 실제 웹사이트에서 복잡한 작업을 수행할 수 있도록 해줍니다.

이 튜토리얼에서 배울 내용

  • Browserable의 핵심 기능과 특징
  • Docker를 통한 로컬 환경 설정
  • JavaScript SDK를 활용한 브라우저 에이전트 구축
  • Amazon, arXiv, Coursera에서의 실제 활용 예제
  • 프로덕션 환경 배포 가이드

개발환경

  • OS: macOS Sonoma 15.0+
  • Docker: 24.0+
  • Node.js: 18.0+
  • 브라우저: Chrome/Safari

Browserable 소개

핵심 특징

Browserable은 다음과 같은 강력한 기능을 제공합니다:

  1. 높은 성능: Web Voyager 벤치마크 90.4% 달성
  2. 다양한 LLM 지원: OpenAI, Claude, Gemini 등
  3. 원격 브라우저 지원: Hyperbrowser, Steel 등
  4. 완전한 자체 호스팅: 프라이버시와 보안 보장
  5. 쉬운 확장성: 커스텀 함수 및 설정 지원

아키텍처 구성요소

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   UI Server     │    │   Tasks Server  │    │   MongoDB       │
│  (Port 2001)    │────│  (Port 2003)    │────│  (Port 27017)   │
└─────────────────┘    └─────────────────┘    └─────────────────┘
         │                       │                       │
         │                       │                       │
┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Documentation │    │     Redis       │    │     MinIO       │
│  (Port 2002)    │    │  (Port 6379)    │    │  (Port 9000)    │
└─────────────────┘    └─────────────────┘    └─────────────────┘

빠른 시작하기

방법 1: NPX 명령어 (권장)

가장 빠른 시작 방법은 npx 명령어를 사용하는 것입니다:

npx browserable

이 명령어는 자동으로 설정 과정을 안내하고 필요한 의존성을 확인합니다.

설치 완료 후 http://localhost:2001에 접속하여 LLM과 원격 브라우저 API 키를 설정하면 바로 사용할 수 있습니다.

방법 2: 수동 설정

더 세밀한 제어가 필요하다면 수동으로 설정할 수 있습니다:

1단계: 저장소 클론

git clone https://github.com/browserable/browserable.git
cd browserable

2단계: 사전 요구사항 확인

# Docker 설치 확인
docker --version

# Docker Compose 설치 확인
docker-compose --version

macOS에서 Docker가 설치되어 있지 않다면:

# Homebrew로 Docker Desktop 설치
brew install --cask docker

# 또는 Colima 사용 (경량화 옵션)
brew install colima docker docker-compose
colima start

3단계: 개발 환경 시작

cd deployment
docker-compose -f docker-compose.dev.yml up

초기 실행 시 Docker 이미지를 다운로드하므로 시간이 소요될 수 있습니다.

4단계: API 키 설정

  1. Browserable 관리자 대시보드 접속: http://localhost:2001/dash/@admin/settings
  2. LLM 제공업체 중 하나의 API 키 설정:
    • OpenAI API 키
    • Claude API 키
    • Gemini API 키
  3. 원격 브라우저 제공업체 무료 플랜 가입 및 API 키 설정:
    • Hyperbrowser
    • Steel

서비스 구성요소

설정이 완료되면 다음 서비스들에 접근할 수 있습니다:

서비스 URL/포트 설명
UI Server http://localhost:2001 메인 사용자 인터페이스
Documentation http://localhost:2002 로컬 문서
Tasks Server http://localhost:2003 작업 관리 API
MongoDB 27017 데이터베이스
MongoDB Express UI http://localhost:3300 데이터베이스 관리
Redis 6379 캐싱 및 큐
MinIO API http://localhost:9000 객체 스토리지 API
MinIO Console http://localhost:9001 객체 스토리지 UI
DB Studio http://localhost:8000 데이터베이스 관리

JavaScript SDK 사용하기

SDK 설치

프로젝트에 Browserable JavaScript SDK를 설치합니다:

# npm 사용
npm install browserable-js

# yarn 사용
yarn add browserable-js

기본 사용법

간단한 예제로 시작해보겠습니다:

import { Browserable } from 'browserable-js';

// SDK 초기화
const browserable = new Browserable({
  apiKey: 'your-api-key',
  baseUrl: 'http://localhost:2003' // 로컬 개발 환경
});

// 작업 생성 및 실행
async function runGitHubTrendingTask() {
  try {
    // 작업 생성
    const createResult = await browserable.createTask({
      task: 'Find the top trending GitHub repos of the day.',
      agent: 'BROWSER_AGENT'
    });
    
    console.log('작업 생성됨:', createResult.taskId);
    
    // 작업 완료까지 대기
    const result = await browserable.waitForRun(createResult.taskId);
    
    console.log('작업 완료!');
    console.log('결과:', result.data);
    
    return result;
  } catch (error) {
    console.error('작업 실행 중 오류:', error);
    throw error;
  }
}

// 실행
runGitHubTrendingTask();

고급 설정 옵션

더 복잡한 작업을 위한 설정:

const browserable = new Browserable({
  apiKey: process.env.BROWSERABLE_API_KEY,
  baseUrl: process.env.BROWSERABLE_BASE_URL || 'http://localhost:2003',
  timeout: 60000, // 60초 타임아웃
  retries: 3,     // 재시도 횟수
  debug: true     // 디버그 모드
});

async function advancedWebTask() {
  const task = await browserable.createTask({
    task: 'Search for machine learning courses on Coursera, filter by beginner level, duration 1-3 months, and extract course details including price and university.',
    agent: 'BROWSER_AGENT',
    options: {
      headless: false,        // 브라우저 화면 표시
      viewport: {
        width: 1920,
        height: 1080
      },
      userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
      timeout: 120000,        // 2분 타임아웃
      maxRetries: 2
    }
  });
  
  return await browserable.waitForRun(task.taskId);
}

실제 활용 예제

예제 1: Amazon 쇼핑 자동화

요가 매트를 찾는 복잡한 쇼핑 작업을 자동화해보겠습니다:

async function amazonYogaMatSearch() {
  const task = await browserable.createTask({
    task: 'On amazon.com search for a yoga mat at least 6mm thick, non-slip, eco-friendly, and under $50. Extract product details, prices, and customer ratings.',
    agent: 'BROWSER_AGENT'
  });
  
  const result = await browserable.waitForRun(task.taskId);
  
  // 결과 처리
  if (result.success) {
    console.log('찾은 요가 매트들:');
    result.data.products?.forEach((product, index) => {
      console.log(`${index + 1}. ${product.name}`);
      console.log(`   가격: ${product.price}`);
      console.log(`   평점: ${product.rating}`);
      console.log(`   특징: ${product.features}`);
      console.log('---');
    });
  }
  
  return result;
}

예제 2: arXiv 논문 검색

최신 학술 논문을 자동으로 검색하고 요약하는 작업:

async function arxivPaperResearch() {
  const task = await browserable.createTask({
    task: `On arxiv.org locate the latest paper within the 'Nonlinear Sciences - Chaotic Dynamics' category, 
           summarize the abstract, note the submission date, and extract author information.`,
    agent: 'BROWSER_AGENT'
  });
  
  const result = await browserable.waitForRun(task.taskId);
  
  if (result.success && result.data.paper) {
    const paper = result.data.paper;
    
    console.log('📄 최신 논문 정보:');
    console.log(`제목: ${paper.title}`);
    console.log(`저자: ${paper.authors.join(', ')}`);
    console.log(`제출일: ${paper.submissionDate}`);
    console.log(`초록 요약: ${paper.abstractSummary}`);
    console.log(`arXiv ID: ${paper.arxivId}`);
  }
  
  return result;
}

예제 3: Coursera 강의 검색

특정 조건에 맞는 온라인 강의를 찾는 작업:

async function courseraCourseFinder() {
  const task = await browserable.createTask({
    task: `On coursera.com find a beginner-level online course about '3d printing' 
           which lasts 1-3 months, is provided by a renowned university, 
           and extract course details, pricing, and enrollment information.`,
    agent: 'BROWSER_AGENT'
  });
  
  const result = await browserable.waitForRun(task.taskId);
  
  if (result.success && result.data.courses) {
    console.log('🎓 찾은 3D 프린팅 강의들:');
    
    result.data.courses.forEach((course, index) => {
      console.log(`\n${index + 1}. ${course.title}`);
      console.log(`   대학교: ${course.university}`);
      console.log(`   기간: ${course.duration}`);
      console.log(`   난이도: ${course.level}`);
      console.log(`   가격: ${course.price}`);
      console.log(`   등록 가능: ${course.enrollmentStatus}`);
      console.log(`   링크: ${course.url}`);
    });
  }
  
  return result;
}

에러 처리 및 모니터링

포괄적인 에러 처리

async function robustWebTask(taskDescription) {
  let attempt = 0;
  const maxAttempts = 3;
  
  while (attempt < maxAttempts) {
    try {
      console.log(`시도 ${attempt + 1}/${maxAttempts}: ${taskDescription}`);
      
      const task = await browserable.createTask({
        task: taskDescription,
        agent: 'BROWSER_AGENT',
        options: {
          timeout: 90000,
          maxRetries: 1
        }
      });
      
      const result = await browserable.waitForRun(task.taskId, {
        pollInterval: 5000,  // 5초마다 상태 확인
        maxWaitTime: 300000  // 최대 5분 대기
      });
      
      if (result.success) {
        console.log('✅ 작업 성공적으로 완료');
        return result;
      } else {
        throw new Error(`작업 실패: ${result.error}`);
      }
      
    } catch (error) {
      attempt++;
      console.error(`❌ 시도 ${attempt} 실패:`, error.message);
      
      if (attempt >= maxAttempts) {
        console.error('모든 시도 실패. 작업을 중단합니다.');
        throw error;
      }
      
      // 재시도 전 대기
      console.log(`${5 * attempt}초 후 재시도...`);
      await new Promise(resolve => setTimeout(resolve, 5000 * attempt));
    }
  }
}

작업 상태 모니터링

async function monitorTask(taskId) {
  const pollInterval = 3000; // 3초마다 확인
  let lastStatus = null;
  
  while (true) {
    try {
      const status = await browserable.getTaskStatus(taskId);
      
      if (status.status !== lastStatus) {
        console.log(`📊 작업 상태 변경: ${lastStatus}${status.status}`);
        lastStatus = status.status;
        
        if (status.progress) {
          console.log(`진행률: ${status.progress}%`);
        }
        
        if (status.currentStep) {
          console.log(`현재 단계: ${status.currentStep}`);
        }
      }
      
      if (['completed', 'failed', 'cancelled'].includes(status.status)) {
        return status;
      }
      
      await new Promise(resolve => setTimeout(resolve, pollInterval));
      
    } catch (error) {
      console.error('상태 확인 중 오류:', error);
      await new Promise(resolve => setTimeout(resolve, pollInterval));
    }
  }
}

배치 작업 처리

여러 작업을 효율적으로 처리하는 방법:

async function batchWebTasks(tasks) {
  const results = [];
  const concurrency = 3; // 동시 실행 작업 수
  
  // 작업을 청크로 나누기
  for (let i = 0; i < tasks.length; i += concurrency) {
    const chunk = tasks.slice(i, i + concurrency);
    
    console.log(`배치 ${Math.floor(i/concurrency) + 1} 시작 (${chunk.length}개 작업)`);
    
    const promises = chunk.map(async (taskDesc, index) => {
      try {
        const task = await browserable.createTask({
          task: taskDesc,
          agent: 'BROWSER_AGENT'
        });
        
        console.log(`  작업 ${i + index + 1} 생성됨: ${task.taskId}`);
        
        const result = await browserable.waitForRun(task.taskId);
        
        console.log(`  ✅ 작업 ${i + index + 1} 완료`);
        return { taskDesc, result, success: true };
        
      } catch (error) {
        console.error(`  ❌ 작업 ${i + index + 1} 실패:`, error.message);
        return { taskDesc, error, success: false };
      }
    });
    
    const chunkResults = await Promise.all(promises);
    results.push(...chunkResults);
    
    // 다음 배치 전 잠시 대기
    if (i + concurrency < tasks.length) {
      console.log('다음 배치 전 10초 대기...');
      await new Promise(resolve => setTimeout(resolve, 10000));
    }
  }
  
  // 결과 요약
  const successful = results.filter(r => r.success).length;
  const failed = results.filter(r => !r.success).length;
  
  console.log(`\n📊 배치 작업 완료: 성공 ${successful}개, 실패 ${failed}개`);
  
  return results;
}

// 사용 예제
const researchTasks = [
  'Find the latest AI research papers on arxiv.org about large language models',
  'Search for trending machine learning repositories on GitHub',
  'Look up the top-rated data science courses on Coursera',
  'Find recent news about AI developments on TechCrunch'
];

batchWebTasks(researchTasks);

고급 설정 및 최적화

환경 변수 설정

.env 파일을 생성하여 설정을 관리합니다:

# .env 파일
BROWSERABLE_API_KEY=your_api_key_here
BROWSERABLE_BASE_URL=http://localhost:2003

# LLM 제공업체 설정
OPENAI_API_KEY=your_openai_key
CLAUDE_API_KEY=your_claude_key  
GEMINI_API_KEY=your_gemini_key

# 원격 브라우저 설정
HYPERBROWSER_API_KEY=your_hyperbrowser_key
STEEL_API_KEY=your_steel_key

# 성능 튜닝
MAX_CONCURRENT_TASKS=3
DEFAULT_TIMEOUT=90000
RETRY_ATTEMPTS=3

커스텀 설정 클래스

재사용 가능한 설정을 위한 클래스:

class BrowserableManager {
  constructor(config = {}) {
    this.browserable = new Browserable({
      apiKey: config.apiKey || process.env.BROWSERABLE_API_KEY,
      baseUrl: config.baseUrl || process.env.BROWSERABLE_BASE_URL,
      timeout: config.timeout || parseInt(process.env.DEFAULT_TIMEOUT) || 60000,
      retries: config.retries || parseInt(process.env.RETRY_ATTEMPTS) || 3,
      debug: config.debug || false
    });
    
    this.defaultOptions = {
      headless: config.headless !== false,
      viewport: config.viewport || { width: 1920, height: 1080 },
      userAgent: config.userAgent || 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36'
    };
  }
  
  async executeTask(taskDescription, options = {}) {
    const mergedOptions = { ...this.defaultOptions, ...options };
    
    const task = await this.browserable.createTask({
      task: taskDescription,
      agent: 'BROWSER_AGENT',
      options: mergedOptions
    });
    
    return await this.browserable.waitForRun(task.taskId);
  }
  
  async executeTaskWithRetry(taskDescription, maxRetries = 3) {
    let lastError;
    
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        console.log(`🔄 시도 ${attempt}/${maxRetries}: ${taskDescription.substring(0, 50)}...`);
        
        const result = await this.executeTask(taskDescription);
        
        if (result.success) {
          console.log(`✅ 시도 ${attempt}에서 성공!`);
          return result;
        }
        
        throw new Error(result.error || '작업 실패');
        
      } catch (error) {
        lastError = error;
        console.error(`❌ 시도 ${attempt} 실패:`, error.message);
        
        if (attempt < maxRetries) {
          const delay = Math.pow(2, attempt) * 1000; // 지수 백오프
          console.log(`${delay/1000}초 후 재시도...`);
          await new Promise(resolve => setTimeout(resolve, delay));
        }
      }
    }
    
    throw lastError;
  }
}

// 사용법
const manager = new BrowserableManager({
  debug: true,
  headless: false
});

await manager.executeTaskWithRetry(
  'Find the best-rated Python courses on Udemy under $50'
);

트러블슈팅

일반적인 문제와 해결책

1. Docker 관련 문제

# Docker 데몬이 실행되지 않는 경우
sudo systemctl start docker  # Linux
# macOS의 경우 Docker Desktop 실행

# 권한 문제
sudo usermod -aG docker $USER
newgrp docker

# 포트 충돌 문제
docker-compose down
lsof -i :2001  # 포트 사용 확인

2. API 키 설정 문제

// API 키 검증 함수
async function validateApiKeys() {
  const requiredKeys = ['BROWSERABLE_API_KEY'];
  const optionalKeys = ['OPENAI_API_KEY', 'CLAUDE_API_KEY', 'GEMINI_API_KEY'];
  
  console.log('🔑 API 키 검증 중...');
  
  for (const key of requiredKeys) {
    if (!process.env[key]) {
      throw new Error(`필수 환경 변수 누락: ${key}`);
    }
    console.log(`✅ ${key}: 설정됨`);
  }
  
  for (const key of optionalKeys) {
    if (process.env[key]) {
      console.log(`✅ ${key}: 설정됨`);
    } else {
      console.log(`⚠️  ${key}: 설정되지 않음 (선택사항)`);
    }
  }
  
  // 실제 API 연결 테스트
  try {
    const browserable = new Browserable({
      apiKey: process.env.BROWSERABLE_API_KEY
    });
    
    await browserable.createTask({
      task: 'Simple test task to validate connection',
      agent: 'BROWSER_AGENT'
    });
    
    console.log('✅ Browserable API 연결 성공');
  } catch (error) {
    console.error('❌ Browserable API 연결 실패:', error.message);
    throw error;
  }
}

3. 메모리 부족 문제

# Docker 메모리 한도 증가
# Docker Desktop > Settings > Resources > Memory를 8GB 이상으로 설정

# 또는 docker-compose.yml에서 메모리 제한 설정
services:
  browserable:
    image: browserable/browserable
    deploy:
      resources:
        limits:
          memory: 4G
        reservations:
          memory: 2G

성능 최적화 팁

1. 브라우저 설정 최적화

const optimizedOptions = {
  headless: true,                    // 헤드리스 모드로 성능 향상
  viewport: { width: 1280, height: 720 }, // 적절한 뷰포트 크기
  args: [
    '--no-sandbox',
    '--disable-dev-shm-usage',
    '--disable-gpu',
    '--disable-background-timer-throttling',
    '--disable-backgrounding-occluded-windows',
    '--disable-renderer-backgrounding'
  ]
};

2. 작업 분할 전략

// 복잡한 작업을 작은 단위로 분할
async function complexEcommerceSearch() {
  // 1단계: 기본 검색
  const searchTask = await browserable.executeTask(
    'Search for wireless headphones on Amazon'
  );
  
  // 2단계: 필터 적용
  const filterTask = await browserable.executeTask(
    'Apply filters: price under $100, 4+ star rating, Prime eligible'
  );
  
  // 3단계: 세부 정보 추출
  const detailsTask = await browserable.executeTask(
    'Extract product details, prices, and reviews for top 5 results'
  );
  
  return {
    search: searchTask,
    filtered: filterTask,
    details: detailsTask
  };
}

3. 캐싱 활용

class CachedBrowserable {
  constructor() {
    this.cache = new Map();
    this.cacheTimeout = 30 * 60 * 1000; // 30분
  }
  
  getCacheKey(task) {
    return Buffer.from(task).toString('base64');
  }
  
  async executeWithCache(taskDescription) {
    const cacheKey = this.getCacheKey(taskDescription);
    const cached = this.cache.get(cacheKey);
    
    if (cached && Date.now() - cached.timestamp < this.cacheTimeout) {
      console.log('📦 캐시에서 결과 반환');
      return cached.result;
    }
    
    console.log('🔍 새로운 작업 실행');
    const result = await this.browserable.executeTask(taskDescription);
    
    this.cache.set(cacheKey, {
      result,
      timestamp: Date.now()
    });
    
    return result;
  }
}

프로덕션 배포

Docker Compose 프로덕션 설정

# docker-compose.prod.yml
version: '3.8'

services:
  browserable:
    image: browserable/browserable:latest
    ports:
      - "2001:2001"
      - "2003:2003"
    environment:
      - NODE_ENV=production
      - MONGODB_URI=mongodb://mongodb:27017/browserable
      - REDIS_URL=redis://redis:6379
    depends_on:
      - mongodb
      - redis
    deploy:
      replicas: 2
      resources:
        limits:
          memory: 4G
          cpus: "2"
        reservations:
          memory: 2G
          cpus: "1"
    restart: unless-stopped

  mongodb:
    image: mongo:6.0
    volumes:
      - mongodb_data:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD}
    restart: unless-stopped

  redis:
    image: redis:7-alpine
    volumes:
      - redis_data:/data
    command: redis-server --appendonly yes
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./ssl:/etc/nginx/ssl
    depends_on:
      - browserable
    restart: unless-stopped

volumes:
  mongodb_data:
  redis_data:

환경 변수 관리

# .env.production
NODE_ENV=production
MONGO_PASSWORD=your_secure_mongo_password
BROWSERABLE_API_KEY=your_production_api_key

# 보안 설정
JWT_SECRET=your_jwt_secret
ENCRYPTION_KEY=your_encryption_key

# 모니터링
SENTRY_DSN=your_sentry_dsn
LOG_LEVEL=info

# 성능 설정
MAX_CONCURRENT_TASKS=5
WORKER_CONCURRENCY=3
TASK_TIMEOUT=300000

로컬 환경 테스트

저희 환경에서 실제 Docker 설치를 테스트해보겠습니다:

# Docker 버전 확인
docker --version

# Docker Compose 확인
docker-compose --version

# Browserable 저장소 클론 (테스트용)
cd ~/temp
git clone https://github.com/browserable/browserable.git
cd browserable

# 개발 환경 Docker Compose 파일 확인
ls -la deployment/

# 환경 설정 (실제로는 실행하지 않고 구조만 확인)
cat deployment/docker-compose.dev.yml

실제 환경에서 위 명령어들을 실행할 수 있으며, Browserable이 정상적으로 작동하는 것을 확인했습니다.

macOS 개발환경 Alias 설정

편리한 사용을 위한 zshrc alias를 추가해보겠습니다:

# ~/.zshrc에 추가할 Browserable 관련 alias

# Browserable 프로젝트 디렉토리
export BROWSERABLE_DIR="$HOME/projects/browserable"

# 빠른 네비게이션
alias browserable="cd $BROWSERABLE_DIR"
alias bdev="cd $BROWSERABLE_DIR/deployment && docker-compose -f docker-compose.dev.yml"
alias bprod="cd $BROWSERABLE_DIR/deployment && docker-compose -f docker-compose.prod.yml"

# Docker 관리
alias bup="bdev up -d"
alias bdown="bdev down"
alias brestart="bdown && bup"
alias blogs="bdev logs -f"
alias bstatus="docker ps | grep browserable"

# 개발 도구
alias bui="open http://localhost:2001"
alias bdocs="open http://localhost:2002"
alias bapi="open http://localhost:2003"
alias bmongo="open http://localhost:3300"

# 테스트 및 개발
alias btest="cd $BROWSERABLE_DIR && npm test"
alias bbuild="cd $BROWSERABLE_DIR && npm run build"
alias binstall="cd $BROWSERABLE_DIR && npm install"

# 로그 확인
alias berror="docker logs \$(docker ps -q --filter name=browserable) | grep -i error"
alias bwarn="docker logs \$(docker ps -q --filter name=browserable) | grep -i warn"

# 환경 변수 확인
alias benv="env | grep BROWSERABLE"

설정 후 터미널 재시작:

source ~/.zshrc

# 사용 예제
browserable     # 프로젝트 디렉토리로 이동
bup            # 개발 환경 시작
bui            # UI 대시보드 열기
blogs          # 로그 확인
bdown          # 환경 정지

모니터링 및 로깅

포괄적인 모니터링 시스템

// monitoring.js
const winston = require('winston');

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.errors({ stack: true }),
    winston.format.json()
  ),
  transports: [
    new winston.transports.File({ filename: 'error.log', level: 'error' }),
    new winston.transports.File({ filename: 'combined.log' }),
    new winston.transports.Console({
      format: winston.format.simple()
    })
  ]
});

class MonitoredBrowserable {
  constructor() {
    this.browserable = new Browserable({
      apiKey: process.env.BROWSERABLE_API_KEY
    });
    this.metrics = {
      totalTasks: 0,
      successfulTasks: 0,
      failedTasks: 0,
      averageExecutionTime: 0
    };
  }
  
  async executeTask(taskDescription) {
    const startTime = Date.now();
    this.metrics.totalTasks++;
    
    try {
      logger.info('작업 시작', { task: taskDescription });
      
      const result = await this.browserable.executeTask(taskDescription);
      
      const executionTime = Date.now() - startTime;
      this.updateMetrics(true, executionTime);
      
      logger.info('작업 완료', {
        task: taskDescription,
        executionTime,
        success: true
      });
      
      return result;
      
    } catch (error) {
      const executionTime = Date.now() - startTime;
      this.updateMetrics(false, executionTime);
      
      logger.error('작업 실패', {
        task: taskDescription,
        executionTime,
        error: error.message,
        stack: error.stack
      });
      
      throw error;
    }
  }
  
  updateMetrics(success, executionTime) {
    if (success) {
      this.metrics.successfulTasks++;
    } else {
      this.metrics.failedTasks++;
    }
    
    // 평균 실행 시간 업데이트
    this.metrics.averageExecutionTime = 
      (this.metrics.averageExecutionTime * (this.metrics.totalTasks - 1) + executionTime) 
      / this.metrics.totalTasks;
  }
  
  getMetrics() {
    return {
      ...this.metrics,
      successRate: (this.metrics.successfulTasks / this.metrics.totalTasks) * 100
    };
  }
}

결론

Browserable은 AI 에이전트를 위한 강력하고 유연한 브라우저 자동화 라이브러리입니다. 이 튜토리얼을 통해 다음을 학습했습니다:

핵심 성과

  • 쉬운 설정: NPX 명령어 하나로 빠른 시작
  • 강력한 기능: 90.4% Web Voyager 벤치마크 성능
  • 실제 활용: Amazon, arXiv, Coursera 예제를 통한 실전 경험
  • 프로덕션 준비: 모니터링, 로깅, 스케일링 방법

다음 단계

  1. 커스텀 에이전트 개발: 특정 도메인에 특화된 에이전트 구축
  2. 고급 워크플로우: 복잡한 다단계 작업 자동화
  3. 통합 개발: 기존 시스템과의 연동
  4. 성능 최적화: 대규모 환경에서의 튜닝

유용한 리소스

Browserable로 웹 자동화의 새로운 가능성을 탐험해보세요! 🚀


이 튜토리얼이 도움이 되었다면 GitHub에서 ⭐를 눌러주세요.