WSL2는 별도 VM 네트워크라 Windows의 localhost:9222에 직접 접근할 수 없습니다. 이 가이드에서는 Windows PowerShell에서 node.exe와 puppeteer-core를 경유하는 우회 방식으로 Chrome을 원격 제어하는 방법을 설명하고, Claude Code Skill로 등록하여 편리하게 사용하는 방법까지 다룹니다.

TL;DR (3줄 요약)

  1. WSL2는 별도 VM 네트워크라 Windows localhost:9222에 직접 접근 불가
  2. Windows PowerShell -> node.exe -> puppeteer-core 우회 방식으로 Chrome 원격 제어
  3. Claude Code Skill(/chrome)로 등록하면 매번 긴 명령어 안 쳐도 됨

이 가이드가 필요한 이유

WSL2 환경에서 Claude Code를 사용할 때, 웹 애플리케이션 화면을 직접 확인해야 할 때가 있다. 예를 들어 JSP 화면이 제대로 렌더링되는지, 버튼 클릭이 동작하는지 등을 확인할 때.

그런데 WSL2에서 이런 시도를 하면 무조건 실패한다:

# 이건 절대 안 된다!
curl localhost:9222          # WSL2 → Windows localhost 접근 불가
curl 127.0.0.1:9222         # 마찬가지로 불가

왜 안 되는가? WSL2는 Hyper-V 기반 가상머신이라 Windows와 별도의 네트워크 스택을 사용한다. WSL2에서 localhost는 WSL2 자체의 localhost이지, Windows의 localhost가 아니다.

이 문제를 해결하는 방법은 Windows 측의 node.exe를 PowerShell 경유로 실행하는 것이다. Windows 내부에서는 127.0.0.1:9222에 정상 접근이 가능하다.

┌──────────────────┐         ┌──────────────────────────────────┐
│     WSL2         │         │          Windows                  │
│                  │         │                                    │
│  Claude Code     │         │  Chrome (--remote-debugging-port  │
│       │          │         │          =9222)                    │
│       ▼          │         │       ▲                            │
│  powershell.exe ─┼────────>│  node.exe + puppeteer-core        │
│  -Command "..."  │         │       │                            │
│                  │         │       └── 127.0.0.1:9222 ──────┘  │
└──────────────────┘         └──────────────────────────────────┘

STEP 1. Windows 사전 준비

1-1. 실행 중인 Chrome 모두 종료

디버깅 모드로 Chrome을 새로 시작하려면, 기존 Chrome 프로세스를 모두 종료해야 한다. 기존 Chrome이 떠 있으면 디버깅 포트가 열리지 않는다.

Windows PowerShell 또는 CMD에서 실행:

taskkill /F /IM chrome.exe /T
옵션 의미
/F 강제 종료 (Force)
/IM chrome.exe 이미지 이름으로 지정
/T 하위 프로세스도 함께 종료 (Tree kill)

WSL2에서 실행하려면:

powershell.exe -Command "taskkill /F /IM chrome.exe /T"

1-2. Chrome을 디버깅 모드로 실행

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="C:\temp\chrome-debug"
옵션 의미
--remote-debugging-port=9222 CDP(Chrome DevTools Protocol) 포트 열기
--user-data-dir="C:\temp\chrome-debug" 별도 프로필 디렉토리 사용 (기존 프로필과 충돌 방지)

Chrome 설치 경로는 PC마다 다를 수 있다

  • 64bit: C:\Program Files\Google\Chrome\Application\chrome.exe
  • 32bit: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

WSL2에서 한 번에 실행하려면:

powershell.exe -Command "& 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe' --remote-debugging-port=9222 --user-data-dir='C:\temp\chrome-debug'"

1-3. Node.js + puppeteer-core 설치

주의: Windows에 Node.js가 설치되어 있어야 한다. (WSL2의 Node.js가 아님!)

Windows PowerShell에서 실행:

mkdir C:\temp\chrome-ctrl
cd C:\temp\chrome-ctrl
npm init -y
npm install puppeteer-core

WSL2에서 실행하려면:

powershell.exe -Command "mkdir -Force C:\temp\chrome-ctrl; cd C:\temp\chrome-ctrl; npm init -y; npm install puppeteer-core"

STEP 2. Claude Code에서 Chrome 제어

기본 패턴

모든 명령은 이 패턴으로 실행한다:

/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "node -e \"<JS_CODE>\""
  • WSL2에서 Windows PowerShell을 호출 -> PowerShell이 Windows node.exe를 실행
  • node.exe는 Windows 네트워크를 사용하므로 127.0.0.1:9222에 접근 가능

연결 확인 + 탭 목록 보기

/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "node -e \"const p=require('C:/temp/chrome-ctrl/node_modules/puppeteer-core');(async()=>{const b=await p.connect({browserURL:'http://127.0.0.1:9222'});const pages=await b.pages();console.log('Connected! Tabs:');pages.forEach((pg,i)=>console.log(i+': '+pg.url()));b.disconnect()})().catch(e=>console.error('Error:',e.message))\""

출력 예시:

Connected! Tabs:
0: http://localhost:8082/login
1: https://www.naver.com/

페이지 이동

/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "node -e \"const p=require('C:/temp/chrome-ctrl/node_modules/puppeteer-core');(async()=>{const b=await p.connect({browserURL:'http://127.0.0.1:9222'});const[page]=await b.pages();await page.goto('https://www.naver.com',{waitUntil:'networkidle2',timeout:30000});console.log('Navigated to: '+page.url());b.disconnect()})().catch(e=>console.error('Error:',e.message))\""

스크린샷 촬영

/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "node -e \"const p=require('C:/temp/chrome-ctrl/node_modules/puppeteer-core');(async()=>{const b=await p.connect({browserURL:'http://127.0.0.1:9222'});const[page]=await b.pages();await page.screenshot({path:'C:/temp/chrome-ctrl/screenshot.png',fullPage:false});console.log('Screenshot saved');b.disconnect()})().catch(e=>console.error('Error:',e.message))\""

촬영한 스크린샷은 WSL2에서 이 경로로 확인:

/mnt/c/temp/chrome-ctrl/screenshot.png

요소 클릭

/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "node -e \"const p=require('C:/temp/chrome-ctrl/node_modules/puppeteer-core');(async()=>{const b=await p.connect({browserURL:'http://127.0.0.1:9222'});const[page]=await b.pages();await page.click('#loginBtn');console.log('Clicked');b.disconnect()})().catch(e=>console.error('Error:',e.message))\""

텍스트 입력

/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "node -e \"const p=require('C:/temp/chrome-ctrl/node_modules/puppeteer-core');(async()=>{const b=await p.connect({browserURL:'http://127.0.0.1:9222'});const[page]=await b.pages();await page.type('#userId','admin');console.log('Typed');b.disconnect()})().catch(e=>console.error('Error:',e.message))\""

JavaScript 실행

/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -Command "node -e \"const p=require('C:/temp/chrome-ctrl/node_modules/puppeteer-core');(async()=>{const b=await p.connect({browserURL:'http://127.0.0.1:9222'});const[page]=await b.pages();const result=await page.evaluate(()=>{return document.title});console.log('Result:',result);b.disconnect()})().catch(e=>console.error('Error:',e.message))\""

STEP 3. Claude Code Skill로 등록 (/chrome)

위 과정을 매번 타이핑하기 번거로우므로, Claude Code Skill로 등록하면 /chrome 커맨드로 바로 사용할 수 있다.

Skill 파일 위치: .claude/skills/chrome-remote/SKILL.md

등록 후 사용법:

커맨드 동작
/chrome 연결 확인 + 열린 탭 목록
/chrome goto <URL> 페이지 이동
/chrome screenshot 스크린샷 촬영
/chrome click <selector> CSS 셀렉터로 요소 클릭
/chrome type <selector> <text> 텍스트 입력
/chrome eval <js> JavaScript 실행

자주 하는 실수 & 해결법

증상 원인 해결
connect ECONNREFUSED Chrome이 디버깅 모드가 아님 STEP 1-1, 1-2 다시 실행
Cannot find module 'puppeteer-core' Windows에 puppeteer-core 미설치 STEP 1-3 실행
timeout 에러 페이지 로딩이 느림 timeout 값을 60000으로 증가
기존 Chrome 프로필 사용하고 싶음 --user-data-dir 제거 단, 기존 Chrome 모두 종료 필수
PowerShell에서 따옴표 에러 이스케이프 문제 \" 사용, 긴 코드는 .js 파일로 분리

실전 팁: 긴 스크립트 실행

인라인 코드가 너무 길면 Windows 측에 .js 파일을 작성하고 실행하는 게 편하다:

# 1. WSL2에서 스크립트 파일 작성
cat > /mnt/c/temp/chrome-ctrl/script.js << 'EOF'
const puppeteer = require('C:/temp/chrome-ctrl/node_modules/puppeteer-core');

(async () => {
    const browser = await puppeteer.connect({
        browserURL: 'http://127.0.0.1:9222'
    });
    const [page] = await browser.pages();

    // 여기에 원하는 작업 작성
    await page.goto('http://localhost:8082/login');
    await page.type('#userId', 'admin');
    await page.type('#userPw', 'password');
    await page.click('#loginBtn');

    await page.waitForNavigation();
    console.log('Current URL:', page.url());

    browser.disconnect();
})().catch(e => console.error('Error:', e.message));
EOF

# 2. Windows node.exe로 실행
powershell.exe -Command "node C:/temp/chrome-ctrl/script.js"

핵심 요약

  1. WSL2에서 curl localhost:9222는 절대 안 된다 (별도 VM 네트워크)
  2. Windows PowerShell -> node.exe -> puppeteer-core 경유로 우회
  3. Chrome은 --remote-debugging-port=9222로 실행해야 함
  4. /chrome Skill을 등록하면 편리하게 사용 가능