- Published on
Taillog : 크롬 익스텐션 만들어보기
- Authors

- Name
- Justart
목차
- 시작하기 (Why?)
- 기술 스택과 구조
- 핵심 난관: 스크립트 간 통신 정복하기
- 문제 해결과 성능 최적화 (The Hard Parts)
- 사용자 경험(UX) 개선
- 크롬 웹스토어 배포 여정
- 마치며
1. 시작하기 (Why?)
프론트엔드 개발자라면 공감하실 겁니다. 리액트 환경에서 상태 변화를 추적하려고 console.log를 여기저기 찍어두고, 개발자 도구의 Console 탭과 Network 탭을 바쁘게 오가던 경험을요.
- "이 로그, 대체 어디서 찍힌 거지?"
- "방금 API 요청, 페이로드는 맞게 갔나?"
특히, 호출 되는 API가 많다면 그 중에 어떤 API가 내가 디버깅하기 위한 API인지 찾느라 여러개를 살펴봐야 합니다.
"브라우저 우상단 아이콘만 누르면, 로그와 네트워크 요청만 깔끔하게 보여주는 도구가 있으면 좋겠다!"
그렇게 Taillog 프로젝트가 시작되었습니다.
2. 기술 스택과 구조
빠른 개발과 현대적인 환경을 위해 다음과 같은 스택을 선정했습니다.
taillog/
├── public/
│ ├── manifest.json # 익스텐션 설정 (Manifest V3)
│ ├── background.js # 서비스 워커
│ ├── content.js # 콘텐츠 스크립트
│ └── inject.js # 페이지에 주입되는 스크립트 (핵심)
├── src/ # React 컴포넌트 (팝업 UI)
├── vite.config.js
└── package.json
- Vite: 빌드 도구
- React: 익스텐션 팝업 UI 개발
- Manifest V3: 구글의 최신 익스텐션 보안 표준
3. 핵심 난관: 스크립트 간 통신 정복하기
크롬 익스텐션 개발에서 가장 헷갈리는 부분은 **"격리된 세계 (Isolated World)"**입니다.
실행 컨텍스트의 차이
| 스크립트 | 실행 환경 | 접근 가능 범위 |
|---|---|---|
| inject.js | 웹 페이지 컨텍스트 | window, console, Javascript 객체 |
| content.js | 격리된 컨텍스트 | DOM, Chrome API |
| background.js | 서비스 워커 | Chrome API 전체 |
문제는 console.log나 fetch를 가로채려면 웹 페이지의 window 객체에 접근해야 하는데, content.js는 보안상 여기에 접근할 수 없다는 점입니다.
✅ 해결책: "트로이 목마" 전략 (Script Injection)
이 벽을 넘기 위해 content.js가 inject.js라는 스파이를 웹페이지에 심는 방식을 사용했습니다.
// content.js
const script = document.createElement('script');
script.src = chrome.runtime.getURL('inject.js');
(document.head || document.documentElement).appendChild(script);
3단계 데이터 전달 파이프라인
데이터는 다음과 같이 3단계를 거쳐 팝업창까지 전달됩니다.
1단계: inject.js → content.js (postMessage) 웹페이지 내에서 console.log를 가로채고, postMessage로 던집니다.
// inject.js
const originalLog = console.log;
console.log = function (...args) {
window.postMessage({
source: 'taillog-extension',
type: 'console',
data: { type: 'log', args: safeStringify(args) }
}, '*');
originalLog.apply(console, args); // 원래 로그도 출력
};
2단계: content.js → background.js (chrome.runtime.sendMessage) 메시지를 받아서 백그라운드로 넘깁니다.
// content.js
window.addEventListener('message', (event) => {
if (event.data?.source === 'taillog-extension') {
chrome.runtime.sendMessage(event.data);
}
});
3단계: background.js → Popup UI (chrome.runtime.onConnect)
background.js는 여러 탭에서 오는 데이터를 중앙 관리하며, 팝업이 열릴 때 데이터를 전달해야 합니다. 이를 위해 chrome.runtime.onConnect로 롱 폴링(Long Polling) 연결을 관리합니다.
// background.js
chrome.runtime.onConnect.addListener((port) => {
if (port.name !== "taillog-popup") return;
// 1. 팝업이 열리면 기존에 저장된 로그를 즉시 전송
port.postMessage({ type: 'init', data: storedLogs });
// 2. 포트 저장 (브로드캐스트용)
connections.add(port);
port.onDisconnect.addListener(() => {
connections.delete(port);
});
});
// 새로운 로그가 들어오면 연결된 모든 팝업에 전송
function broadcastLog(log) {
connections.forEach(port => {
try {
port.postMessage({ type: 'new-log', data: log });
} catch (e) {
connections.delete(port);
}
});
}
4. 문제 해결과 성능 최적화 (The Hard Parts)
기능 구현은 20%였고, 나머지 80%는 "사이트가 멈추지 않게" 만드는 최적화 과정이었습니다.
😱 문제 1: 브라우저 프리징 (Freezing)
복잡한 사이트에서 Taillog를 켜면 브라우저가 버벅거리는 현상이 발생했습니다.
원인은 대용량 데이터를 무조건 문자열로 변환하려고 했기 때문입니다. 5MB짜리 JS 파일을 responseText로 읽는 순간 메인 스레드가 멈춰버린 것이죠.
🛠️ 해결책: 사전 검사와 안전장치 만들기
1) Content-Length 사전 검사 데이터를 읽기 전에 헤더를 먼저 검사하여, 100KB가 넘으면 아예 읽지 않도록 수정했습니다.
// inject.js
const contentLength = xhr.getResponseHeader('Content-Length');
if (contentLength && parseInt(contentLength) > 100 * 1024) {
return '[Too Large]'; // 읽지 않고 리턴
}
2) 서킷 브레이커 (Circuit Breaker) 에러가 연속으로 발생하면 잠시 캡처를 중단하는 안전장치를 도입했습니다.
3) 불필요한 파일 블랙리스트 .min.js나 폰트 파일 등 디버깅에 쓸모없는 파일은 정규식으로 필터링하여 부하를 줄였습니다.
🤔 문제 2: 순환 참조 (Circular Reference)
DOM 요소 등을 콘솔에 찍으면 서로를 참조하는 순환 구조 때문에 JSON.stringify가 에러를 뱉습니다. 이를 해결하기 위해 WeakSet을 사용한 안전한 문자열 변환 함수(safeStringify)를 구현했습니다.
5. 사용자 경험(UX) 개선
"이 버튼 코드 위치가 어디지?"
리액트 개발자들의 영원한 숙제입니다. 이를 돕기 위해 Alt 키를 누르고 요소를 클릭하면 VSCode를 열어주는 기능을 추가했습니다. 리액트 파이버(__reactFiber...) 정보를 활용했습니다.
"사이즈 조절이 되었으면 좋겠다."
사용자 입장에서 고정된 팝업은 답답할 때가 많습니다. 하지만 크롬 익스텐션의 기본 팝업은 브라우저 제약으로 인해 자유로운 리사이징이 불가능합니다.
이를 해결하기 위해 "새 창으로 열기" 기능을 추가했습니다. 팝업 대신 독립적인 창(popup type)을 생성하면, 사용자가 원하는 크기와 위치로 자유롭게 조절할 수 있습니다.
// 별도 창으로 열기
const openInWindow = () => {
if (typeof chrome !== 'undefined' && chrome.windows) {
chrome.windows.create({
url: chrome.runtime.getURL('index.html?window=true'),
type: 'popup',
width: 600,
height: 700
});
window.close();
}
};
이 한 줄의 코드로 사용자 경험(UX)이 극적으로 개선되었습니다.
6. 크롬 웹스토어 배포 여정
1. 개발자 등록
크롬 웹스토어에 익스텐션을 배포하려면 먼저 Chrome 개발자 대시보드에서 개발자 등록을 해야 합니다.
(등록비 $5 가 필요하다. 한국은 안되서 미국으로 돌리고 결제했다.)
2. 개인정보처리방침 작성
익스텐션이 사용자 데이터를 다루면 개인정보처리방침이 필요하다고 합니다. 다들 GitHub Pages나 Notion 등에 호스팅한다고 해요. 사용자 데이터를 다루지 않지만 나중을 위해 만들어 놓기는 했습니다.
3. 빌드 및 패키징
npm run build
dist 폴더를 zip으로 압축해서 업로드합니다.
4. 스토어 등록 정보
- 익스텐션 설명
- 스크린샷 (최소 1개, 1280x800 또는 640x400)
- 카테고리 선택
- 언어 설정
5. 심사 대기
제출 후 심사까지 보통 1~3일 정도 걸린다고 해요. 권한이 많거나 민감한 API를 사용하면 더 오래 걸릴 수 있다고 합니다.
2025-12-19
아마 저는 host 권한이 필요해서 심사가 오래걸릴 것 같아요. 첫 심사.
2025-12-23
다행히 반려 안되고 한 번에 성공했다!!

하지만, 슬픈소식은 직접 URL로 접속 가능하지만 검색은 안된다.. 찾아보니 인덱싱 대기 중이라서 그렇단다.
보통 1~3일 후부터 검색 노출 시작된다고 하니 또 기다려봐야지..
2025-12-25
검색 노출 되었다! 새로운 기능도 추가하였고, 아이콘도 변경하였다. 기능 변경에 대해서는 이제 하루정도면 바로 반영이 되는 듯 하다.
2026-01-11
UX 증진을 위한 업데이트 및 성능 최적화를 위한 재배포.
마치며
단순한 호기심에서 시작한 Taillog였지만, 처음 크롬 익스텐션을 만들어보면서 웹 페이지 컨텍스트와 익스텐션 컨텍스트의 분리, 그리고 이들 간의 통신 방식을 이해하는 게 가장 어려웠습니다.
또, 실제 제품으로 만드는 과정에서 "기능 구현"보다 "안정성"과 "예외 처리"가 훨씬 중요하다는 것을 배웠습니다.
디버깅 시간도 단 1초라도 아껴드릴 수 있다면 좋겠습니다.
Chrome Web Store: Taillog 설치하기