[DEVELOP]
- [DEVELOP] DDD, TDD, BDD
- [DEVELOP] 개인정보 보호 웹사이트 구축을 위한
- [DEVELOP] 예제로 이해하는 웹 접근성 (accessibility)
- [DEVELOP] 예제로 보는 이미지 사용법 (Images)
- [DEVELOP] 예제로 보는 반응형 디자인 사용법 (Responsive Design)
- [DEVELOP] PWA 이해하기 (Progressive Web App)
- [DEVELOP] 개발 프로세스 Agile / Waterfall 이란?
- [DEVELOP] 주니어 개발자의 역습
- [DEVELOP] MCP(Model Context Protocol)
- [DEVELOP] MCP claude 적용하고 사용해보기
- [DEVELOP] 실시간 통신 방식 비교 (HTTP, SSE, WebSocket, stdio)
- [DEVELOP] 클로드 코드 50만 줄 소스코드 유출 사건 분석
- [DEVELOP] Claude Code ‘컨텍스트 로트’ 현상과 비용 80% 절감법
- [DEVELOP] 에이전트 하네스 엔지니어링이란? — AI 에이전트를 제대로 다루는 기술
- [DEVELOP] Turborepo 캐시로 CI/CD 빌드 시간을 90% 줄이는 법
- [DEVELOP] 모노레포 빌드 10분 → 2분, Turborepo 캐시와 CI/CD 최적화 실전기
- [DEVELOP] Cursor Agent로 PR 자동 리뷰 시스템 만들기 — Skill 기반 워크플로우 설계
- [DEVELOP] Claude Fable 5·Mythos 5 전면 중단 — 출시 3일 만에 꺼진 AI
- [DEVELOP] 실무 모노레포에 에이전트 하네스를 깔고 운영해본 기록
- [DEVELOP] Chrome DevTools에 생긴 WebMCP — 웹사이트가 에이전트에게 도구를 넘기는 법
- [DEVELOP] Cursor Plugin — .cursor를 패키지로 배포하는 공식 방법
- [DEVELOP] REA, 프론트엔드에서 쓰는 법
소스맵이 없거나, Electron에서 버튼 한 번이 렌더러·preload·메인으로 갈라질 때. 파일을 grep하는 대신 모듈 그래프와 위치를 받아 오는 도구
REA는 Reverse Engineer Anything이다. npm 패키지 이름은 rea-agents다. 저장소는 morluto/rea, 문서는 rea.tools다. 이 글의 명령과 버전은 2026-10-14, npm rea-agents@6.4.0 기준이다.
1. REA가 뭐냐
한 줄 요약: 코딩 에이전트에 붙는 로컬 분석 MCP다. CLI로도 같은 일을 한다.
1
2
3
4
질문 하나 + 로컬 경로
→ REA가 파일 또는 붙잡은 페이지를 읽음
→ 모듈, 호출, 라우트 후보, 소스 위치, 모르는 항목
→ 에이전트가 그 근거로 설명하거나 코드를 고침
분석은 내 컴퓨터에서 돈다. 앱 폴더를 REA 서버로 업로드하는 구조가 아니다. 다만 도구 결과는 에이전트가 받고, 그 모델 제공자의 데이터 정책은 따로다. 번들 안에 토큰이 있으면 결과에도 실릴 수 있다.
네이티브 바이너리는 Hopper, Ghidra, IDA가 필요하다. JavaScript, Electron, 웹 페이지는 Node.js만으로 시작한다. 프론트 업무는 거의 이쪽이다.
Node.js는 22.19+, 24.11+, 또는 26+ 다. 배포판에 들어 있는 nodejs는 22.19보다 낮은 경우가 많다. node -v부터 본다.
1
2
node -v
npm -v
공식 README는 20만 GitHub 스타를 넘겼다고 적혀 있다. 2026-10-14 저장소 페이지는 그 위였다. npm 패키지는 2026-07-12에 처음 올라왔고, 2026-10-11에 6.4.0이 나왔다. 저장소 생성은 2026-04다. 릴리스가 빠르니, 글을 따라 할 때는 @latest가 아니라 이 글의 버전을 고정하는 편이 낫다.
2. 왜 지금 뜨냐
DevTools와 소스맵이 이미 있다. REA가 그 자리를 대체해서 뜬 건 아니다.
에이전트에게 “이 버튼이 어디를 호출하냐”고 물으면, 보통은 저장소를 grep한다. 소스가 있고 이름이 그대로면 그걸로 끝난다. 아래부터는 grep이 약하다.
1
2
3
4
프로덕션 dist만 있고 소스맵이 빠졌을 때
청크가 갈라져서 라우트 문자열이 여러 파일에 있을 때
Electron에서 렌더러 API와 메인 핸들러의 채널 이름이 다를 때
지난 빌드와 이번 빌드의 export 모양이 바뀌었는지 볼 때
REA는 이 질문을 그래프로 받는다. 문자열, 라우트, API 이름, IPC 채널, 모듈 경로 중 하나를 시드로 주면, 그 문자열이 닿는 모듈과 위치를 돌려준다. 정규식이나 “대충 비슷한 함수”는 시드가 아니다. 리터럴이거나 부분 문자열이다.
못 찾은 것은 빈 결과가 아니라 unknown으로 남는다. 동적 import(), 계산된 채널 이름, 해석 못 한 간선은 없다고 단정하지 않는다. 이 구분이 스타가 쌓인 이유에 가깝다. 에이전트가 추측으로 기능을 베끼기 전에, 근거와 한계가 결과에 같이 온다.
프론트에서 Ghidra를 깔 이유는 이 글의 범위에 없다. 정적 JS 분석은 네이티브 엔진 없이 된다.
3. 프론트가 만지는 표면
REA 문서에는 펌웨어, 패킷, APK, 네이티브 의사코드가 같이 있다. 프론트 작업에서 여는 명령은 셋으로 줄인다.
| 대상 | 명령 | 받는 것 |
|---|---|---|
Vite/webpack dist, Electron 폴더, app.asar |
analyze-javascript-application |
모듈, import, 소스맵, 라우트, IPC |
| 이미 만든 그래프 | trace-application-feature |
문자열·라우트·채널에서 이어진 경로 |
| 로컬 Chrome에 떠 있는 내 페이지 | inspect-web-page, analyze-web-bundle |
DOM/스크립트 메타, 청크·라우트 후보, 클릭 동안의 요청 |
두 빌드를 견줄 때는 compare-application-versions, 소스 트리와 번들을 견줄 때는 compare-source-to-bundle이다. export 반환 모양만 보려면 compare-javascript-export-shapes다. 전부 정적이다. 자바스크립트를 실행해서 값을 확정하지 않는다.
브라우저 쪽은 Chrome DevTools Protocol에 붙기만 한다. REA가 브라우저를 띄우거나 클릭을 대신하지 않는다. 클릭은 내가 하고, 관찰 창 동안의 요청·내비게이션을 기록한다.
4. Cursor에 붙이기
에이전트 설정이 목적이다. 터미널에서 한 번 돌리고 끝낼 거면 5절로 가도 된다.
1
npx rea-agents@latest setup
클라이언트로 Cursor를 고르고, 바꿔 쓰겠다는 계획을 확인한 뒤 승인한다. 기존 설정은 백업된다. MCP 서버와 조사용 스킬이 들어간다. 창만 닫지 말고 Cursor를 완전히 종료했다가 다시 연다. macOS는 ⌘Q다.
버전을 고정하려면 setup이 적어 준 args를 rea-agents@6.4.0처럼 박거나, 수동으로 .cursor/mcp.json에 넣는다.
1
2
3
4
5
6
7
8
{
"mcpServers": {
"rea": {
"command": "npx",
"args": ["-y", "rea-agents@6.4.0", "mcp"]
}
}
}
공식 레지스트리 이름은 io.github.morluto/rea다. 수동 설정은 위 JSON과 같다. @latest는 다음에 에이전트가 뜰 때 다른 버전이 될 수 있다.
CLI를 셸에 두고 싶으면 전역 설치다. 프로젝트에 npm install rea-agents만 하면 node_modules/.bin 안에만 생기고, rea는 PATH에 없다.
1
2
npm install --global rea-agents@6.4.0
rea --help
갱신은 rea update 다음에, 출력된 setup을 다시 실행하고 Cursor를 재시작한다. npx만 쓰는 경우에는 npx rea-agents@latest setup을 다시 돌린다.
5. 자기 dist부터
대상은 내가 빌드한 산출물이다. 회사 앱의 프로덕션 번들, 내가 유지하는 Electron, 로컬 dist. 남의 서비스를 붙잡아서 기능을 복사하는 절차는 이 글에 없다. 프로젝트 입장도 같다. 소유했거나 분석이 허용된 소프트웨어, 연구·교육·상호운용·보존이다.
앱을 실행하지 않고 폴더만 읽는다.
1
2
npx -y rea-agents@6.4.0 analyze-javascript-application \
/absolute/path/to/dist --json > application-evidence.json
Electron이면 폴더 또는 app.asar 경로다. macOS는 .app 우클릭 → 패키지 내용 보기 → Contents/Resources/app.asar. 결과의 normalized_result에 앱 식별, 모듈 사이 연결, 소스 위치가 있다. 해석 못 한 연결은 표시된다.
이어서 라우트나 API 경로처럼 코드에 리터럴로 있는 문자열을 시드로 추적한다.
1
2
3
4
5
6
7
8
9
node --input-type=module -e '
import { readFileSync, writeFileSync } from "node:fs";
const application = JSON.parse(readFileSync("application-evidence.json", "utf8"));
writeFileSync("trace-input.json", JSON.stringify({
application,
seed: { kind: "string", value: "/api/posts" },
direction: "both"
}));'
npx -y rea-agents@6.4.0 trace-application-feature ./trace-input.json --json
kind는 string, route, api, ipc, module 중 질문과 맞는 것을 고른다. direction은 그 시드에서 앞으로, 뒤로, 또는 양쪽이다. 반환은 맞은 시드, 닿는 부분 그래프, 끝 경로, 그리고 모르는 항목이다.
Cursor에 REA가 붙어 있으면 같은 일을 말로 시킨다. 경로는 절대 경로다.
1
2
3
/absolute/path/to/dist 를 REA로 분석해서
"/api/posts" 문자열이 어느 모듈에서 만들어지는지 소스 위치와 함께 보여 줘.
못 푼 간선은 unknown으로 남겨.
질문 하나에 동작 하나. “이 앱 전체 아키텍처를 설명해”는 그래프가 커지고, 에이전트가 중간을 생략한다.
소스맵이 있으면 번들 위치가 원본 경로로 이어진다. 없어도 모듈 그래프와 문자열 위치는 나온다. 그게 소스맵 유실 빌드를 볼 때의 쓰임이다.
두 빌드를 비교할 때는 각 디렉터리를 따로 분석한 Evidence를 compare-application-versions에 넣는다. 같은 파일은 내용 다이제스트, 소스맵 신원, 구조 지문 순으로 짝을 맞춘다. minify된 변수명이 같다고 같은 모듈로 보지 않는다. 한쪽 그래프가 잘려 있으면 빠짐은 removed가 아니라 unknown이다.
6. Electron이면 채널 이름
렌더러에 보이는 함수 이름과, 메인이 받는 채널 이름은 다른 파일에 있다. 공식 가이드의 Notes 예는 이 경로다.
1
2
3
renderer.js window.notes.exportCsv()
preload.js contextBridge → ipcRenderer.invoke("notes:export")
main.js ipcMain.handle("notes:export") → csv.js → notes.csv
REA는 리터럴 채널 notes:export를 preload의 invoke와 main의 handle에 짝짓는다. 가이드에 적힌 4.1.0 예제 요약은 채널 1개, 렌더러 전송 1개, 메인 핸들러 1개, 짝 1개, 애매한 전송 0개다. 위치는 normalized_result.graph에 있다.
1
2
npx -y rea-agents@6.4.0 analyze-javascript-application \
/absolute/path/to/notes-example --json
예제 압축은 JavaScript 가이드에서 받는다. Electron을 설치하지 않아도 정적 분석은 된다.
내 앱에서는 채널 문자열을 시드로 준다.
1
2
3
이 Electron 앱에서 "notes:export" 채널의 핸들러를 찾아.
CSV를 쓰는 함수까지 소스 위치를 보여 주고,
채널 이름이 계산식이라 짝을 못 찾은 것은 unknown으로 남겨.
contextBridge로 노출된 API 이름도 시드가 된다. 계산된 채널(ipcMain.handle(channelName))은 정적 분석이 짝을 못 맞출 수 있다. 그때는 7절처럼 실제로 한 번 실행해서 관찰한다. 정적 결과가 비었다고 핸들러가 없는 것은 아니다.
7. 로컬 페이지에서 클릭 하나
개발 서버가 http://127.0.0.1:3000에 떠 있다고 하자. Chrome은 전용 프로필과 루프백 디버그 포트로 연다. 기본 프로필에 --remote-debugging-port를 주면 Chrome 136부터 그 플래그가 무시된다. 포트를 랜에 열지 않는다.
1
2
3
4
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 \
--user-data-dir=/tmp/rea-browser-profile \
http://127.0.0.1:3000
REA는 이 브라우저를 띄우지도, 끄지도 않는다. 조사가 끝나면 내가 종료한다.
1
npx -y rea-agents@6.4.0 list-browser-targets http://127.0.0.1:9222 --json
나온 targetId로 페이지를 집는다. 기본 관찰은 1초다. 클릭을 보려면 창을 늘리고, 명령을 먼저 건 다음 버튼을 누른다.
1
2
3
npx -y rea-agents@6.4.0 observe-web-session \
http://127.0.0.1:9222 TARGET_ID \
--observation-ms 10000 --json
청크와 라우트 후보만 보려면 클릭 없이 번들 분석이다.
1
2
npx -y rea-agents@6.4.0 analyze-web-bundle \
http://127.0.0.1:9222 TARGET_ID --json
기본 캡처는 요청 메서드, 상태, MIME, 크기, initiator 위치, URL이다. 쿠키와 Authorization 헤더는 남기지 않는다. 응답 본문은 기본으로 읽지 않는다. JSON 모양만 필요하면 --include-json-body-shapes를 켠다. 그때도 값과 예시는 빠지고 경로·타입·개수만 남는다.
스크립트 원문이 필요하면 --include-script-sources다. 원문에는 키가 들어 있을 수 있고, 그 텍스트는 Evidence에 포함된다. 스토리지 값은 키 이름을 켜도 가린다. 프론트 질문(“이 클릭이 어느 스크립트에서 fetch했나”)에는 본문 값까지 필요 없는 경우가 많다. 옵션은 그 질문에 필요한 것만 켠다.
엔드포인트는 http://127.0.0.1:포트만 된다. localhost, 사설 IP, HTTPS CDP 주소는 거절된다. 페이지 URL의 localhost와 디버그 포트 주소는 별개다.
에이전트에게 맡길 때의 말은 이렇다.
1
2
3
4
5
Chrome이 http://127.0.0.1:9222 에 떠 있다.
list-browser-targets로 127.0.0.1:3000 페이지를 고르고,
10초 관찰을 시작해. 내가 Export를 누를 테니
그 클릭의 요청 URL, 상태, initiator 스크립트 위치만 보여 줘.
응답 본문 값과 쿠키는 가져오지 마.
페이지가 WebMCP 도구를 등록해 두었다면 discover-webmcp-tools로 선언을 읽을 수 있다. 호출은 하지 않는다. 도구를 페이지가 어떻게 넘기는지는 WebMCP 글 쪽이다.
8. 이렇게 물어야 한다
잘 되는 질문은 경로, 동작 하나, 근거, 한계가 있다.
1
2
3
4
/Users/me/app/dist 를 분석해서
라우트 "/settings" 를 등록하는 모듈과
그 라우트가 호출하는 fetch URL 리터럴을 찾아.
각 주장의 파일 위치와, 동적 import라서 못 푼 부분을 같이 적어.
약한 질문은 시드가 없다.
1
2
3
이 사이트 클론해 줘.
로그인 우회되는 코드 찾아.
프로덕션에서 토큰 어디 저장하는지 뽑아.
앞의 것은 분석이 허용된 대상이 아니고, 뒤의 둘은 이 도구로 할 일이 아니다. 프론트에서 REA로 줄이는 시간은 “어느 청크의 어느 위치인가”다.
결과를 코드에 반영할 때는 에이전트 설명을 그대로 병합하지 않는다. Evidence의 소스 위치를 열고, 그 줄이 시드와 연결되는지 본다. resolved는 정적 분석에서 대상이 하나로 좁혀졌다는 뜻이지, 그 경로가 실행됐다는 뜻이 아니다.
9. 한계
정적 그래프는 실행 추적이 아니다. 관찰 창에 안 들어온 클릭은 없는 기능이 아니다. 소스맵이 한 칸이라도 어긋나면 원본 경로는 추론이다.
compare-source-to-bundle은 커밋된 소스 그래프와 번들 그래프를 경로·다이제스트로 짝짓는다. 파일 이름이 같다고 같은 내용으로 확정하지 않고, 런타임에 그 모듈이 로드된다고 말하지도 않는다.
브라우저 관찰은 붙잡은 그 탭, 그 오리진, 그 몇 초다. 다른 환경의 동작에 대한 증거가 되지 않는다.
버전은 자주 바뀐다. 4.1.0 이전 MCP는 Evidence 전체를 인라인으로 넣어야 하고, 그 이후 세션은 반환된 evidence_id를 같은 연결에서 재사용할 수 있다. 스킬만 새로 깔았다고 서버 스키마가 바뀌지는 않는다. 이 글의 명령이 거절되면 rea-agents@6.4.0으로 맞춘 뒤 CLI 문서의 플래그를 본다.
개발 중이고 소스맵이 살아 있으면 브레이크포인트가 더 빠르다. REA를 꺼내는 자리는 소스맵이 없는 산출물, 프로세스가 갈라진 Electron, 두 빌드의 차이, 에이전트에게 위치를 넘기고 싶을 때다.