Skip to content
aidevkshPublic

About

지금 보고 있는 사이트가 유발하는 CPU·메모리·네트워크 부하를 실시간 그래프로 보여주는 Chrome 확장 (MV3)

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Latest commit

 

History

7 Commits

Folders and files

Repository files navigation

webWorks

한국어 · English

지금 보고 있는 사이트가 내 컴퓨터를 얼마나 쓰고 있는지, 우측 상단 패널에서 실시간으로 보여주는 Chrome 확장.

CPU(메인 스레드 점유), 메모리(JS 힙), 네트워크 수신·송신 — 네 가지를 1초 간격으로 샘플링해 그래프로 그립니다. 브라우저 작업 관리자를 열지 않아도, 어떤 탭이 팬을 돌리고 있는지 바로 보입니다.

다크 테마 패널 — CPU 34.9%, JS 힙 4.1MB, 네트워크 수신·송신 그래프    라이트 테마 패널 — 같은 지표를 라이트 표면에서

다크 · 라이트 (실제 측정 화면. 스모크 테스트가 헤드리스 Chrome에서 캡처한 것)

확장을 설치하기 전에 디자인만 보고 싶다면 design/panel-mockup.html을 브라우저로 열면 됩니다. 가짜 데이터로 도는 목업이라 시스템/라이트/다크·접힘·표 보기를 전부 눌러 볼 수 있습니다.


✨ 기능

실시간 그래프 4종 CPU 점유, JS 힙, 네트워크 수신·송신. 네트워크는 단위가 같으므로 한 차트에 두 계열로 겹쳐 그립니다 (축은 항상 하나)
시간 범위 3종 1분(1초 간격) · 5분(5초 평균) · 1시간(1분 평균). 세 버퍼가 동시에 쌓이므로 범위를 바꿔도 이력이 유지됩니다
테마 3종 시스템 / 라이트 / 다크. 시스템은 OS 설정을 실시간으로 따라갑니다
언어 2종 한국어 / English, 그리고 브라우저 언어를 따르는 시스템. 바꾸면 열려 있는 패널이 그 자리에서 다시 그려집니다 — 새로고침이 필요 없고 측정 이력도 끊기지 않습니다
크로스헤어 툴팁 그래프 위를 지나가면 세 차트가 같은 시점을 가리킵니다. 키보드 포커스 + 방향키로도 같은 값을 읽을 수 있습니다
표 보기 그래프에 그려진 모든 샘플(현재 범위 기준 최대 60개)과 최소·평균·최고를 표로. 색을 구분하기 어려운 환경에서도 모든 값에 접근할 수 있습니다 (예시)
CSV / JSON 내보내기 푸터 ⤓. CSV는 현재 범위의 샘플만 담은 순수한 표, JSON은 세 범위 전부와 누적 합계·측정 조건까지. 저장 전에 미리보기가 나옵니다
접힌 상태 CPU 12% · 148MB · 34.2KB/s 한 줄 필로 축소. 페이지를 거의 가리지 않습니다
드래그 이동 헤더를 잡아 옮기면 위치가 저장돼 다음 페이지에서도 유지됩니다
끄면 진짜 끕니다 패널을 닫으면 옵저버·타이머를 해제하고, 측정 중인 탭이 하나도 없으면 webRequest 리스너까지 전부 떼어냅니다

🚀 설치

아직 웹스토어에 올리지 않았으므로 개발자 모드로 로드합니다.

git clone <이 저장소> webWorks
cd webWorks
node scripts/make-icons.mjs   # 아이콘 PNG 생성 (저장소에 이미 있으면 생략 가능)
  1. Chrome에서 chrome://extensions 열기
  2. 우측 상단 개발자 모드 켜기
  3. 압축해제된 확장 프로그램을 로드 → 이 폴더 선택

Chrome 116 이상이 필요합니다 (adoptedStyleSheets, deliveryType, MV3 서비스 워커).


🎛 사용법

툴바의 webWorks 아이콘을 누르면 팝업이 열립니다.

조작 하는 일
팝업 → 이 탭에서 측정 현재 탭에 패널을 띄우고 측정을 시작합니다
Alt+Shift+W 현재 탭에서 패널 켜기/끄기
팝업 → 언어 시스템 / 한국어 / English. 열려 있는 패널에 즉시 반영됩니다
팝업 ✕ 팝업 닫기 (Esc·바깥 클릭으로도 닫힙니다)
팝업 → 모든 사이트에서 자동 시작 새 페이지가 로드될 때마다 알아서 켭니다
패널 ⏸ 화면을 얼립니다. 수집은 계속되므로 재개하면 이력이 이어집니다
패널 ⌄ 한 줄 필로 접기
패널 ✕ 이 탭의 측정 종료
패널 ⤓ CSV / JSON 내보내기. Esc·바깥 클릭으로도 닫힙니다
그래프 위 포인터 / ←→ 해당 시점의 값 읽기

측정은 켠 순간부터 셉니다. 페이지를 이동하면 카운터가 초기화됩니다.

내보내기

두 형식이 담는 것이 다릅니다.

담는 것 왜
CSV 현재 범위의 샘플만 — timestamp,cpu_pct,mem_mb,in_kbs,out_kbs 헤더 한 줄 + 데이터 행뿐인 순수한 표라야 스프레드시트·pandas·R에 그대로 들어갑니다. 모양이 다른 누적 합계를 같은 파일에 섞지 않습니다. 앞에 BOM을 붙여 Excel이 UTF-8로 엽니다
JSON 전부 — 메타데이터·단위·세 범위의 샘플·누적 합계 중첩이 되므로 잘라낼 이유가 없습니다

시각은 샘플에 기록된 값입니다. 마지막 샘플에서 간격을 역산하지 않습니다 — 백그라운드 탭에서 버려진 구간이 있으면 실제와 어긋나기 때문입니다. units 항목에 "송신은 과대 추정"이라는 사실까지 적어 내보냅니다.

저장은 Blob + <a download>로 합니다. chrome.downloads를 쓰면 권한이 하나 더 붙는데, 그만한 값어치가 없습니다.


📐 측정 방식

무엇을 어떻게 세는지, 그리고 무엇을 못 세는지를 분명히 적어 둡니다. 브라우저 확장이 접근할 수 있는 범위에는 실제 한계가 있고, 그 한계를 숨기는 계측기는 믿을 수 없습니다.

지표 데이터 소스 실제로 세는 값 한계
CPU PerformanceObserver('longtask') + 타이머 지연 프로브(100ms) 두 신호 중 큰 값을 샘플 구간으로 나눈 메인 스레드 점유율(%) 프로세스 CPU 사용률이 아닙니다. Web Worker·GPU·컴포지터·다른 프레임의 부하는 빠집니다. longtask는 50ms 이상만 잡히므로, 짧은 작업이 촘촘한 경우는 타이머 지연 쪽이 실제에 가깝습니다
메모리 performance.memory.usedJSHeapSize (없으면 performance.measureUserAgentSpecificMemory()) 렌더러의 JS 힙 사용량(MB) DOM 노드·이미지·GPU 텍스처는 포함되지 않습니다. 같은 프로세스를 공유하는 다른 프레임의 힙이 섞이며, 값은 100KB 단위로 양자화됩니다
네트워크 수신 Resource Timing transferSize (+ chrome.webRequest의 Content-Length 보충) 헤더까지 포함한 실제 수신 바이트 캐시 적중은 0으로 셉니다(맞는 동작). Timing-Allow-Origin이 없는 교차 출처 응답은 transferSize가 가려지므로 서비스 워커가 본 Content-Length로 보충하는데, 이건 압축 후 본문 크기 추정치입니다
네트워크 송신 chrome.webRequest — 요청 라인 + 요청 헤더 + requestBody 업로드 바이트 근사치 HTTP/2·3의 헤더 압축(HPACK/QPACK)과 TLS·TCP 오버헤드를 반영하지 않아 실제보다 크게 나옵니다. Resource Timing에는 업로드 크기가 아예 없어서 이 방법이 유일한 창구입니다
요청 수 / 실패 / 캐시 chrome.webRequest onCompleted · onErrorOccurred 탭에 귀속된 요청 건수 페이지의 Service Worker가 낸 요청은 tabId가 없어 빠질 수 있습니다

데이터 흐름

flowchart LR
  subgraph PAGE["측정 대상 페이지 (탭 최상위 프레임)"]
    RT["PerformanceObserver<br/>resource · navigation"]
    LT["PerformanceObserver<br/>longtask"]
    LAG["타이머 지연 프로브<br/>100ms"]
    MEM["performance.memory"]
  end

  subgraph CS["콘텐츠 스크립트 · isolated world"]
    MET["metrics.js<br/>1초 샘플러 · 링 버퍼 3개"]
    PAN["panel.js<br/>Shadow DOM 패널"]
    CH["charts.js<br/>SVG 렌더러"]
  end

  SW["service-worker.js<br/>chrome.webRequest 탭별 집계"]
  POP["popup<br/>on/off · 테마 · 기본 범위"]

  RT --> MET
  LT --> MET
  LAG --> MET
  MEM --> MET
  MET <-->|"1초 틱 — 송신 바이트 · 요청 수<br/>opaque URL 바이트 보충"| SW
  MET -->|onSample| PAN
  PAN --> CH
  POP -->|ww:setEnabled| SW
  SW -->|"탭 메시지"| PAN
Loading

왜 이 조합인가

프로세스 단위의 정확한 CPU·메모리를 주는 chrome.processes API는 확장에서 쓸 수 없습니다. 남은 선택지는 두 개였습니다.

  • chrome.debugger(CDP Performance.getMetrics) — 정확하지만 탭마다 "디버깅 중" 배너가 붙고 권한이 무거워집니다.
  • 웹 표준 API — 정확도를 조금 내주는 대신, 조용하고 가볍고 언제든 껐다 켤 수 있습니다.

webWorks는 후자를 골랐습니다. 그래서 CPU 지표의 이름이 그냥 "CPU"가 아니라 "CPU · 메인 스레드" 이고, 패널 안에서 그 라벨에 마우스를 올리면 추정 방식이 그대로 나옵니다.


🎨 디자인

계열 색

색약(1형·2형·3형) 시뮬레이션 검증을 통과한 조합입니다. 라이트와 다크는 자동 반전이 아니라 각 표면에 맞춰 따로 고른 단계입니다.

슬롯 지표 라이트 다크
1 CPU #2a78d6 #3987e5
2 메모리 #eb6834 #d95926
3 네트워크 수신 #1baf7a #199e70
4 네트워크 송신 #eda100 #c98500
  • 인접쌍 최악 CVD ΔE 9.1(라이트) / 8.4(다크) — 기준 8 이상
  • 일반 시야 최악 ΔE 22.9 / 19.8 — 기준 15 이상
  • 라이트 모드의 청록·노랑은 표면 대비가 3:1 미만입니다. 그래서 범례에 현재값을 직접 찍고 표 보기를 항상 제공합니다 — 색만으로 읽어야 하는 상황을 만들지 않습니다.
  • 상태색(정상 #0ca30c / 주의 #fab219 / 과부하 #d03b3b)은 CPU 배지 전용으로 예약되어 있고, 계열 색으로 재사용하지 않습니다. 배지는 언제나 아이콘 + 글자를 함께 달고 나옵니다.

마크 규격

선 2px round · 면 채움은 계열 색 10% · 끝점 마커 r4에 표면색 2px 링 · 그리드와 축은 표면에서 한 단계 벗어난 회색 실선 hairline. 데이터만 진하고, 나머지는 전부 물러납니다.

테마 3종

:host                                   { /* 라이트 — 기본값 */ }
@media (prefers-color-scheme: dark) {
  :host(:not([data-theme="light"])) { /* 시스템이 다크일 때 */ }
}
:host([data-theme="dark"])              { /* 사용자가 다크를 고정했을 때 */ }

시스템을 고르면 data-theme 속성을 지워 CSS가 OS 설정을 직접 따르게 하고, 라이트/다크는 OS와 반대여도 선택이 이깁니다. 팝업도 같은 규칙을 씁니다.

언어

문자열은 chrome.i18n이 아니라 common/i18n.js의 자체 표를 씁니다. chrome.i18n은 브라우저 UI 언어를 따르고 런타임에 바꿀 수 없어서, 팝업의 언어 선택을 구현할 수 없기 때문입니다. 테마와 똑같이 시스템 / 한국어 / English 세 값을 storage.sync에 두고, 패널은 storage.onChanged로 받아 마크업을 다시 만듭니다.

예외는 매니페스트에 박히는 문자열입니다 — 확장 설명, 툴바 툴팁, 단축키 설명은 크롬이 직접 그리므로 _locales/와 __MSG_*__를 쓸 수밖에 없고, 브라우저 언어를 따릅니다. 팝업에서 언어를 바꿔도 이 세 개는 바뀌지 않습니다.

숫자도 함께 따라갑니다 — toLocaleString에 넘기는 로케일이 ko-KR/en-US로 갈리므로 자릿수 구분자와 소수점이 언어에 맞습니다.

페이지와 섞이지 않기

패널은 닫힌 Shadow DOM 안에 있습니다. 스타일은 panel.css를 fetch해 CSSStyleSheet.replaceSync()로 adopt합니다 — CSSOM 삽입이라 페이지의 style-src CSP와 무관하고, 페이지 CSS는 shadow 경계를 넘어오지 못합니다. connect-src가 확장 리소스 fetch까지 막는 사이트에서는 서비스 워커가 CSS 텍스트를 대신 전달합니다. 상속으로 새어 들어오는 폰트·색은 :host { all: initial }로 끊습니다.


🔐 권한

권한 왜 필요한가
webRequest 업로드 바이트와 요청 수를 세는 유일한 창구입니다. 요청을 차단하거나 수정하지 않습니다 (MV3에서는 관측만 가능)
storage 테마·언어·기본 범위·자동 시작·패널 위치 저장
scripting 확장을 설치·리로드하기 전부터 열려 있던 탭에 계측 스크립트를 그 자리에서 넣기 위해 씁니다. 넣는 파일은 패키지 안 네 개로 고정이고, 원격 코드는 실행하지 않습니다
host_permissions: <all_urls> 어느 사이트에서든 측정하려면 필요합니다. 사용할 사이트가 정해져 있다면 이 항목을 원하는 도메인으로 좁혀 쓰는 걸 권합니다

밖으로 나가는 데이터는 없습니다. 네트워크 요청도, 원격 전송도, 애널리틱스도 없습니다. 모든 수치는 탭 메모리에만 있고 패널을 닫으면 사라집니다. 저장하는 것은 위 표의 설정값뿐입니다.


🗂 파일 구조

webWorks/
├─ manifest.json               MV3 매니페스트
├─ _locales/{ko,en}/           매니페스트 문자열만 (설명 · 툴팁 · 단축키 설명)
├─ common/
│  └─ i18n.js                  UI 문자열 표 · 언어 해석 · 로케일 숫자 포맷
├─ background/
│  └─ service-worker.js        탭별 webRequest 집계 · on/off 중계 · 배지
├─ content/
│  ├─ metrics.js               1초 샘플러 · 링 버퍼 · 서비스 워커 동기화
│  ├─ charts.js                SVG 렌더러 (마크 규격이 여기 다 들어 있음)
│  ├─ export.js                CSV / JSON 직렬화 · 파일명 · 저장
│  ├─ panel.js                 Shadow DOM 패널 · 상호작용 · 테마/언어 적용
│  └─ panel.css                패널 토큰과 스타일 (테마 3종)
├─ popup/
│  ├─ popup.html / .css / .js  on/off · 언어 · 테마 · 기본 범위 · 자동 시작
├─ icons/                      16 / 32 / 48 / 128 PNG (생성물)
├─ scripts/
│  ├─ lib/harness.mjs          테스트 공용 — Chrome 실행 · CDP · shadow DOM 순회
│  ├─ make-icons.mjs           zlib만으로 PNG를 직접 인코딩하는 아이콘 생성기
│  ├─ smoke-test.mjs           헤드리스 Chrome에 실제로 로드해 보는 종단 테스트
│  ├─ test-injection.mjs       확장보다 먼저 열려 있던 탭에 주입되는지
│  ├─ test-table.mjs           표 보기가 버퍼 전체를 싣는지
│  ├─ test-i18n.mjs            한/영 전환
│  ├─ test-export.mjs          CSV / JSON 내보내기
│  └─ test-all.mjs             전부 차례로 실행
└─ design/
   ├─ panel-mockup.html        가짜 데이터로 도는 디자인 목업
   ├─ roadmap-mockup.html      「앞으로」 5개 항목이 들어간 목업 (아직 구현 전)
   └─ screenshots/             스모크 테스트가 캡처한 실제 화면

🧪 개발

node scripts/test-all.mjs                   # 테스트 전부 차례로
node scripts/smoke-test.mjs                 # 종단 확인 + 스크린샷만
node scripts/make-icons.mjs                 # 아이콘 재생성 (도형은 스크립트 상단 상수)
node --check content/metrics.js             # 문법 검사 (빌드 과정이 없습니다)
start design/panel-mockup.html              # 디자인만 빠르게 확인

빌드 단계도 번들러도 없습니다. 파일을 고치고 chrome://extensions에서 새로고침하면 끝입니다. 콘텐츠 스크립트를 고쳤다면 대상 페이지도 새로고침해야 합니다 — 이미 주입된 구 버전이 남아 있어서, 다시 켜는 것만으로는 새 코드가 올라가지 않습니다.

테스트

모두 진짜 Chrome을 헤드리스로 띄우고 CDP로 조작합니다. 목 객체가 없고, 외부 네트워크도 쓰지 않습니다. 공용 부분(Chrome 실행·CDP 클라이언트·닫힌 shadow root 순회)은 scripts/lib/harness.mjs에 있습니다.

파일 무엇을 지키는가
smoke-test.mjs 종단. 샘플이 쌓이는지, 수신·송신·요청 수가 채워지는지, 패널이 Shadow DOM에 마운트됐는지, 테마 전환이 열려 있는 패널에 반영되는지. 다크·라이트·표 보기 스크린샷을 남깁니다
test-injection.mjs 페이지를 먼저 열고 확장을 나중에 로드합니다. 선언형 content_scripts는 소급 주입되지 않으므로, chrome.scripting 폴백이 없으면 여기서 실패합니다. 중복 주입해도 패널이 하나인지도 함께 봅니다
test-table.mjs 표가 버퍼 전체를 싣는지. 25초를 모아 옛 상한(14행)을 넘기고, 요약의 '최고'가 표 안에 실제로 있는지까지 확인합니다
test-i18n.mjs 한/영 전환. 표 보기를 켜 둔 채 언어를 바꿔 상태가 살아남는지, 마크업을 다시 만든 뒤에도 클릭이 붙어 있는지, 숫자 로케일이 함께 바뀌는지
test-export.mjs CSV가 실제로 파싱 가능한 표인지(헤더·열 수·시간순), 시각이 역산이 아니라 샘플에 기록된 값인지, JSON에 세 범위와 합계가 다 들어 있는지. 다운로드 경로를 지정해 파일이 진짜 떨어지는 것까지 확인합니다

smoke-test.mjs가 10초만 돌고 확장을 항상 먼저 로드하는 탓에 놓치는 회귀가 있어서, 나머지를 따로 두었습니다.

Chrome 151부터 --load-extension 커맨드라인 플래그가 무시됩니다. 그래서 테스트는 --enable-unsafe-extension-debugging과 CDP Extensions.loadUnpacked를 씁니다.

콘텐츠 스크립트들은 같은 isolated world를 공유하며 globalThis.WW 한 곳에서만 만납니다 (WW.I18n → WW.Metrics → WW.Charts → WW.Panel, 이 순서로 주입됩니다). 각 파일은 IIFE로 감싸 최상위 이름을 만들지 않고, 선두의 재진입 가드 덕분에 두 번 주입돼도 안전합니다. common/i18n.js는 팝업에서도 같은 파일을 그대로 불러 씁니다.


⚠️ 알려진 한계

  • 백그라운드 탭은 그래프가 멈춥니다. Chrome이 보이지 않는 탭의 타이머를 강하게 조이기 때문입니다. 샘플 간격이 3배 이상 벌어지면 그 구간을 버립니다 — 쓰레기 값으로 그래프를 채우는 것보다 낫습니다.
  • 송신 바이트는 과대 추정입니다. HTTP/2 헤더 압축을 계산에 넣지 못합니다. 절대량보다 추이를 보는 용도로 쓰세요.
  • iframe은 포함되지 않습니다. 최상위 프레임만 계측합니다.
  • 서비스 워커가 잠들면 그 순간의 네트워크 카운트가 어긋날 수 있습니다. 패널이 열려 있는 동안은 1초 틱이 워커를 깨워 두므로 실사용에서는 드뭅니다.
  • chrome://, Chrome 웹스토어, 다른 확장 페이지에서는 동작하지 않습니다 (브라우저가 스크립트 주입을 금지).

🗺 앞으로

  • CSV / JSON 내보내기
  • 정확한 프로세스 CPU·메모리를 원하는 사용자를 위한 chrome.debugger 선택 모드
  • 사이트별 이력 저장과 세션 비교
  • 임계값 알림 (CPU가 N초 이상 X%를 넘으면 알림)
  • 요청 상위 도메인 분해 (무엇이 대역폭을 먹는지)

남은 네 항목의 화면 설계는 design/roadmap-mockup.html에 목업으로 만들어 두었습니다 — 도메인 분해는 패널의 세 번째 뷰로, 이력·세션 비교는 새 표면(대시보드)으로 들어갑니다. 내보내기는 그 목업대로 푸터 ⤓에 붙었고, 여러 세션을 한꺼번에 내보내는 것만 대시보드 몫으로 남았습니다.


라이선스

MIT

About

지금 보고 있는 사이트가 유발하는 CPU·메모리·네트워크 부하를 실시간 그래프로 보여주는 Chrome 확장 (MV3)

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages