Chrome이 `--load-extension`이 왜 더 이상 동작하지 않는지 알려주지만, 당신은 그걸 볼 수 없다.

최종 업데이트: August 17, 2026
Chrome이 `--load-extension`이 왜 더 이상 동작하지 않는지 알려주지만, 당신은 그걸 볼 수 없다.
AI 요약
테스트한 매트릭스에서 기본 제공 Google Chrome 150은 --disable-extensions-except와 --load-extension 조합을 거부한 반면, Chrome for Testing 149와 151은 확장을 로드했습니다. Chrome은 거부 사유를 WARNING 수준으로 기록했습니다: "--load-extension is not allowed in Google Chrome, ignoring." 이 문구는 브랜드 빌드 해석을 지지하고, 3개 버전의 배치는 단순한 단조 제거를 배제하지만, 같은 버전의 교차 빌드 결과나 소스/설정 증거가 없으면 빌드와 버전은 여전히 구분되지 않습니다. 고정된 확장 하네스에는 버전이 명시된 브라우저 실행 파일을 사용하고, 서비스 워커와 콘텐츠 스크립트 마커를 함께 확인하세요.

확장 프로그램 자동화 예제에서는 보통 아래 두 플래그가 자주 등장합니다:

--disable-extensions-except=/path/to/ext  --load-extension=/path/to/ext

예전 자료들을 보면, 설치된 Chrome에 Puppeteer나 Playwright를 연결한 뒤 이 두 플래그를 넘기는 방식이 많이 소개돼 있습니다.

그런데 여기서 테스트한 기본 제공 Google Chrome 150 빌드에서는 브라우저가 명령줄 오류 없이 뜨긴 했지만, 확장 서비스가 이 두 플래그를 전부 무시했습니다. 자동화 연결 자체는 정상적으로 됐는데, 확장 프로그램은 보이지 않았고, 이 테스트 하네스에서는 결국 셀렉터 타임아웃이라는 형태로 증상이 드러났습니다.

Chrome은 이 거부 이유를 한 줄로 남겼지만, stderr 로깅을 켠 뒤에야 확인할 수 있었습니다.

핵심 포인트는 브라우저 빌드 비교에 있습니다. 아래 두 섹션은 분명히 하네스 관련 메모입니다. 하나는 확장 프로그램이 일으키는 다운로드를 다루고, 다른 하나는 file:// 고정 테스트 파일에서의 명령줄 설치 경로를 다룹니다. Thunderbit 자체가 Chrome 확장 프로그램이기 때문에, 이 문제는 우리에게도 직접적으로 중요한 주제입니다. 다만 Thunderbit는 테스트 대상은 아니었습니다.

로그로 드러난 한 줄

--enable-logging=stderr를 추가한 뒤, 아래 플래그로 기본 제공 Chrome을 실행합니다:

공식 참고: Chromium extension service source.

WARNING:chrome/browser/extensions/extension_service.cc:442]
  --disable-extensions-except is not allowed in Google Chrome, ignoring.

--load-extension만 단독으로 넘기면 같은 파일의 다른 줄에서 별도 경고가 뜹니다:

WARNING:chrome/browser/extensions/extension_service.cc:420]
  --load-extension is not allowed in Google Chrome, ignoring.

같은 플래그를 Chrome for Testing에 넘기면 이런 경고는 나오지 않습니다.

"Google Chrome에서는 허용되지 않습니다." 이 경고 문구는 이번 거부가 브랜드가 붙은 빌드의 정책이라는 점을 가장 먼저 떠올리게 합니다. 그리고 기본 제공 Google Chrome 150 빌드가 해당 플래그를 무시했다는 사실과 그 원본 위치도 보여줍니다. 다만 이 문장만으로 해당 버전이 무관하다는 점이나, 구현상 어떤 구분 기준이 쓰였는지는 따로 증명되지는 않습니다.

아래 내용은 그 사실을 확인하고, 그로 인해 생기는 결과를 정리한 것입니다.

동작으로 확인하기

이 테스트를 위해 새로 만든 최소한의 MV3 확장 프로그램은 콘텐츠 스크립트, 팝업, 메시지 왕복, DOM 추출, 그리고 로컬의 세 가지 제품 고정 데이터를 대상으로 한 chrome.downloads 내보내기를 사용합니다. 하네스는 여섯 가지 검사를 번호로 기록합니다. 서비스 워커 등록 여부, 확장 마커에 ID가 붙는지, 콘텐츠 스크립트 마커가 일치하는지, 팝업 버튼이 보이는지, 팝업이 세 행을 돌려주는지, 캡처한 CSV에 기대한 행이 들어 있는지입니다. 별도의 일치성 검사는 독립적인 서비스 워커 신호와 페이지 마커 신호를 비교합니다.

세 개의 빌드는 모두 같은 명시적 확장 플래그, 같은 확장 디렉터리, 같은 HTTP 고정 파일, 같은 헤드리스 persistent-context 모드, 그리고 매 실행마다 새 프로필을 사용했습니다. 기본 제공 브랜치는 channel: 'chrome'를 통해 연결했고, 통과한 두 브랜치는 명시적 실행 파일 경로를 사용했습니다. 버전 문자열은 CDP를 통해 다시 읽었습니다.

빌드브라우저가 보고한 버전확장 서비스 워커콘텐츠 스크립트 주입전체 6단계 검사
Chrome for TestingChrome/149.0.7827.55PASS 6/6
기본 제공 Google ChromeChrome/150.0.7871.187❌ 등록되지 않음0단계에서 실패
Chrome for TestingChrome/151.0.7922.10PASS 6/6

원본 요약: Chrome for Testing 149, 기본 제공 Chrome 150, Chrome for Testing 151, 그리고 stderr 경고.

Chrome for Testing 149를 일부러 넣은 이유는, 비교 대상인 기본 제공 빌드보다 더 오래된 버전이기 때문입니다. 만약 기능이 버전 업으로 사라진 거라면, 더 오래된 빌드는 동작하고 실패한 빌드는 가장 최신이어야 합니다. 그런데 실제로는 실패한 빌드가 정상 동작하는 두 빌드 사이에 끼어 있습니다. 즉, 단순히 버전이 올라가면서 없어졌다고 설명할 수는 없습니다.

하지만 이 표만으로 게이트가 빌드 기반이라고 단정할 수는 없고, 그 이유를 정확히 짚는 게 중요합니다. 실패한 유일한 칸은 동시에 유일한 기본 제공 Chrome 칸이자 유일한 150 버전 칸입니다. 즉, 이 설계에서는 빌드와 버전이 여전히 완전히 얽혀 있습니다. 세 개의 브랜치는 단조로운 제거는 배제하지만, "150에서 되돌아갔다가 151에서 복구됐다"는 가능성은 남겨 둡니다. 행동만으로 이 차이를 가려내려면 이 머신이 만들어낼 수 없는 칸, 즉 Chrome for Testing 150이나 다른 버전의 브랜드 빌드가 필요합니다.

경고 문구는 Google Chrome을 직접 언급하므로 브랜드 빌드 해석을 더 강하게 뒷받침합니다. 반면 세 개 브랜치의 동작 결과는 단순한 단조 제거만 배제합니다. 버전에 무관한 메커니즘을 증명하려면 같은 버전의 교차 빌드 비교나 소스/설정 근거가 여전히 필요합니다.

링크된 각 빌드 아티팩트에는 브랜치별 요약 실행 결과가 들어 있습니다. 다만 이 글은 추가 실행 횟수에 대한 실행별 행렬을 공개하지 않으므로, 그 횟수를 독립 증거로 쓰지는 않습니다.

한 가지 신호만으로는 "로드되지 않았다"고 말할 수 없다

이 테스트의 첫 버전은 확장 프로그램이 로드됐는지를 콘텐츠 스크립트가 페이지에 써 넣는 마커로 판단했고, 콘텐츠 스크립트가 실제로 실행됐는지도 같은 마커로 판단했습니다. 한 번의 읽기를 두 개의 측정치로 쓴 셈입니다. 마커가 없다고 해서 "확장이 아예 로드되지 않았다"와 "로드는 됐지만 콘텐츠 스크립트가 주입되지 않았다"를 구분할 수 없고, 이 둘은 해결 방법도 완전히 다릅니다.

MV3 확장 프로그램은 백그라운드 서비스 워커를 실행하고, Playwright는 서비스 워커를 직접 보여줍니다. 이 신호는 페이지를 전혀 건드리지 않으니, 주입 여부와 헷갈릴 일이 없습니다. 현재 테스트는 두 신호를 모두 읽고, 서로 맞는지 확인합니다.

세 번의 실행 모두에서 두 신호는 일치했습니다. 기본 제공 Chrome에서는 서비스 워커가 아예 등록되지 않았습니다. 이게 가장 강한 의미의 실패입니다. 반면 두 Chrome for Testing 빌드에서는 페이지를 열기 전에 chrome-extension://<id>/background.js에서 워커가 올라왔습니다.

이미 가지고 있을 수도 있는 Chrome for Testing을 쓰자

통과한 두 브랜치는 각각 149.0.7827.55와 151.0.7922.10 버전을 보고하는 Chrome for Testing 실행 파일을 사용했습니다. Playwright의 executablePathchannel: 'chrome'로 기본 제공 Chrome을 찾는 대신, 고정된 실행 파일로 지정하세요. npx playwright install chromium은 Playwright가 관리하는 Chromium 빌드를 설치합니다. 이것도 자동화에는 쓸 수 있지만, 여기서 비교한 두 Chrome for Testing 브랜치와 같은 배포 라벨은 아니며, 이번 비교의 네 번째 브랜치도 아니었습니다.

공식 참고: Chrome for Testing announcement.

관련 리뷰: Chrome extension permissions audit.

이 문제를 넘어서는 부가 이점도 있습니다. 기본 제공 Chrome은 사용자가 모르게 자동 업데이트되므로, 오늘 통과한 테스트가 화요일에는 커밋과 무관한 이유로 실패할 수 있습니다. Chrome for Testing은 고정됩니다. 결과가 몇 달 뒤에도 의미 있어야 한다면, 편의성보다 이 점이 더 중요합니다.

하네스 메모: 확장 내보내기 캡처

스크래핑 확장 프로그램에서 내보내기는 핵심입니다. 필드가 빠지고, 인코딩이 깨지고, 중첩 데이터가 잘못 평탄화되는 지점이 바로 여기입니다. 그런데 이 부분에는 문서화되지 않은 점이 두 가지 있고, 둘 다 문제를 만들 수 있습니다.

내보내기 실행에서 기록된 항목
playwright_download_event_firedfalse
suggestedFilenamenull
확장 프로그램이 요청한 파일명probe-export.csv
디렉터리에 실제로 저장된 파일명download.csv
파일 내용헤더와 세 행이 모두 손상 없이 보존됨

이 MV3/Playwright 1.56.0 하네스에서는 chrome.downloads가 Playwright의 다운로드 이벤트를 발생시키지 않았습니다. 확장 프로그램이 API를 통해 CSV를 내보내는 동안 waitForEvent('download')는 끝나지 않았습니다. 하네스는 CDP 세션을 열고 다운로드 동작을 명시적으로 설정한 뒤 출력 디렉터리를 읽는 방식으로 파일을 캡처했습니다:

const cdp = await ctx.newCDPSession(page);
await cdp.send('Browser.setDownloadBehavior', {
  behavior: 'allow', downloadPath: DIR, eventsEnabled: true,
});

같은 하네스에서 이 CDP 캡처 경로는 요청한 파일명을 그대로 보존하지 않았습니다. 헤더와 세 행은 모두 온전했지만, probe-export.csvdownload.csv로 저장되었습니다. 이건 테스트한 브라우저 빌드와 설정에서 관찰된 동작일 뿐, 모든 확장 다운로드에 대해 항상 성립하는 규칙은 아닙니다. 파일명과 내용은 따로 검증해야 합니다.

하네스 메모: 이 설치 경로의 file:// 동작

이 부분을 검증하는 과정에서, 널리 반복되던 주장 하나가 사실이 아닌 것으로 드러났습니다. 심지어 이 글의 이전 초안에도 들어가 있었습니다. 바로 "확장 프로그램은 기본적으로 파일 접근 권한이 없으므로 file:// 페이지에서 콘텐츠 스크립트가 실행되지 않는다"는 주장입니다.

두 Chrome for Testing 빌드에서 측정한 결과, 확장을 명령줄로 로드한 뒤 file:///…/fixture/index.html로 바로 이동했을 때 콘텐츠 스크립트는 정상적으로 주입됩니다. 마커도 있고, 확장 ID도 맞고, 두 빌드 모두 동일했습니다. 명령줄로 로드된 압축 해제 확장은 파일 접근 권한을 가집니다. 사람들이 흔히 기억하는 "파일 접근 허용" 토글은 다른 설치 경로에 적용됩니다.

그래도 고정 파일은 HTTP로 서빙하는 편이 더 낫습니다. file:// 페이지는 실제 대상과 전혀 다르기 때문입니다. 하지만 이건 현실성의 문제이지 기술적 필수 조건은 아니며, 흔히 그 이유로 설명되는 메커니즘도 틀렸습니다.

이 글이 증명하지 않는 것

  • 게이트 구현은 경고 문자열만큼만 보입니다. Chrome은 이 빌드에서 해당 플래그가 허용되지 않으며, 소스 파일이 어디인지 알려줍니다. 하지만 그것이 빌드 설정인지, 정책 전달인지, 혹은 다른 무엇인지는 소스에서 읽어내지 않았습니다.
  • 이건 한 대의 머신입니다. arm64의 macOS, 하나의 기본 제공 패치 버전, 두 개의 Chrome for Testing 빌드뿐입니다. Chrome은 너무 빨리 바뀌기 때문에, 인용하기보다 다시 확인하는 쪽이 맞습니다.
  • 이것은 --load-extension 경로만 다룹니다. 패키징된 .crx 설치, 개발자 모드 로딩, 엔터프라이즈 allowlist 정책은 테스트하지 않았습니다. 여기서 "기본 제공 Chrome은 확장을 실행할 수 없다"는 결론은 전혀 뒷받침되지 않습니다.
  • 확장 프로그램은 목적에 맞게 만든 스텁입니다. 모든 스크래퍼 확장이 쓰는 메커니즘을 시험하긴 했지만, 실제 확장은 더 크고 스텁으로는 드러나지 않는 방식으로 실패할 수 있습니다.

여기까지 오는 동안 내가 잘못 짚은 것

둘 다 결과만 보면 검토를 통과할 만한 종류의 오류였기 때문에, 분명히 적어 둘 가치가 있습니다.

이 글의 첫 버전은 실패가 조용했다고, 로그 한 줄도 없었다고, 메커니즘은 알 수 없다고 썼습니다. 누군가 그 메커니즘을 안다고 하면 추측일 뿐이라고도 했습니다. 하지만 그 메커니즘은 플래그 하나 차이였고, Chrome은 처음부터 끝까지 WARNING 수준으로 그 내용을 출력하고 있었습니다. "못 찾았다"를 "찾을 수 없다"로 써 버린 셈입니다.

두 번째는 앞서의 file:// 주장입니다. 메모에서 반복된 내용을, 테스트하기도 전에 확신에 찬 메커니즘과 함께 초안으로 옮겼습니다. 단 한 번의 실행으로 그 주장은 반증되었습니다.

두 경우의 패턴은 같습니다. 누구도 반박하지 않을 듯한 그럴듯한 문장을, 확인이 필요 없다고 느낀 채 그대로 밀어붙인 것입니다. 고칠 것은 글투의 신중함이 아니라, 무엇을 사실로 말할 수 있는지에 대한 규칙입니다. 실행 결과로 추적되지 않는 주장은 배포하지 않는다.

로컬 하네스에서 사용한 명령

버전이 붙은 probe, 확장 스텁, 고정 파일, 그리고 원본 요약은 여기 연결되어 있지만, 아직 독립 재현을 위한 단독 공개 번들은 아닙니다. Chrome for Testing 149와 151의 정확한 다운로드 출처와 체크섬은 글에 적혀 있지 않고, 아래 실행 파일 경로도 로컬 입력값이었습니다. 전체 비교를 독립적으로 재현 가능한 형태로 제시하려면, 브라우저 출처와 안정적인 저장소 커밋을 함께 공개해야 합니다.

관련 리뷰: Playwright review.

cd harness
npm install playwright@1.56.0
npx playwright install chromium      # Playwright Chromium; 아래 두 CfT 브랜치와는 다름
(cd fixture && python3 -m http.server 8731 &)

SP=$(pwd) OUT=cft-149.json   LABEL=cft-149   EXE="<Chrome for Testing 149>" node probe_v2.mjs
SP=$(pwd) OUT=stock-150.json LABEL=stock-150 CHANNEL=chrome                 node probe_v2.mjs
SP=$(pwd) OUT=cft-151.json   LABEL=cft-151   EXE="<Chrome for Testing 151>" node probe_v2.mjs

세 브랜치 모두 같은 스크립트와 명시적 확장 인자를 사용했지만, 실행 파일을 해석하는 방식은 달랐습니다. 기본 제공 Chrome에는 CHANNEL=chrome, 두 Chrome for Testing 바이너리에는 EXE를 사용했습니다. 각 출력 파일의 버전은 라벨을 믿은 게 아니라 브라우저에서 직접 읽어온 값입니다.

경고 한 줄만 확인할 때는 하네스가 꼭 필요하진 않습니다:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
  --user-data-dir=/tmp/p --enable-logging=stderr \
  --load-extension=/path/to/ext about:blank 2>&1 | grep "not allowed"

2026-07-28 기준.

웹 데이터 추출을 위해 Thunderbit 사용해 보기

결론과 주의사항

이번 테스트 매트릭스에서 기본 제공 Google Chrome 150은 --disable-extensions-except--load-extension을 거부했고, Chrome for Testing 149와 151은 확장을 로드했습니다. Chrome은 거부 사유를 WARNING 수준으로 남겼습니다: "--load-extension is not allowed in Google Chrome, ignoring." 문구상 브랜드 빌드 해석이 꽤 타당해 보이고, 세 버전 사이의 배치는 단순한 단조 제거를 배제하지만, 같은 버전의 교차 빌드 결과나 소스/설정 증거가 없으면 빌드와 버전은 여전히 분리해서 보기 어렵습니다.

고정된 확장 하네스를 만들려면, 버전이 명시된 브라우저 실행 파일을 사용하고 서비스 워커와 콘텐츠 스크립트 마커를 함께 확인하세요. 이 Playwright 1.56.0 설정에서는 확장 내보내기에 CDP 디렉터리 캡처가 필요했고, 다른 파일명으로 저장됐습니다. 명령줄로 로드한 스텁은 테스트한 file:// 고정 파일에서도 주입됐지만, 다른 설치 경로는 테스트하지 않았습니다.

웹 데이터 추출을 위해 Thunderbit 사용해 보기 Get Started Free

자주 묻는 질문

--load-extension이 Chrome에서 완전히 없어졌나요? 아닙니다. Chrome for Testing 149.0.7827.55와 151.0.7922.10은 둘 다 이 플래그로 압축 해제된 MV3 스텁을 로드했고, 여섯 개의 점수화된 검사를 모두 통과했습니다. 기본 제공 Google Chrome 150.0.7871.187은 이를 거부하고 *"--load-extension is not allowed in Google Chrome, ignoring"*를 기록했습니다. 이는 브랜드 빌드 게이트라는 해석을 뒷받침하지만 증명하진 않습니다. 동작상 단순한 단조 제거는 배제되며, 151에서 복구된 Chrome 150 전용 회귀라는 해석은 여전히 세 브랜치 설계와 양립 가능합니다.

왜 에러가 안 보이고, "아예 로드되지 않음"과 "로드됐지만 조용히 실패함"은 어떻게 구분하나요? 경고는 기본 출력 수준에서는 보이지 않습니다. --enable-logging=stderr를 붙여 실행하면 바로 나타납니다. 그 옵션이 없으면 Chrome은 정상적으로 시작하지만 확장 서비스가 플래그를 무시하고, 하네스에서는 처음 보이는 증상이 셀렉터 타임아웃입니다. “아예 로드되지 않음”과 주입 실패를 구분하려면 두 개의 독립 신호를 써야 합니다. MV3 백그라운드 서비스 워커와 대상 DOM의 콘텐츠 스크립트 마커입니다. 이번 테스트의 기본 제공 Chrome 브랜치에서는 둘 다 나타나지 않았고, 두 Chrome for Testing 브랜치에서는 둘 다 나타났습니다.

그럼 확장 자동화에는 무엇을 써야 하나요? 통과한 브랜치들은 executablePath를 통해 고정된 Chrome for Testing 실행 파일을 사용했습니다. Playwright가 관리하는 Chromium도 또 다른 자동화 바이너리로 쓸 수 있지만, 이번 테스트의 브랜치는 아니었고, 실제로 확인한 실행 파일을 보지 않은 상태에서 같은 배포판이라고 부르면 안 됩니다.

확장이 파일을 내보낼 때 왜 waitForEvent('download')가 끝나지 않나요? 이 MV3/Playwright 1.56.0 설정에서는 이벤트가 끝나지 않았고, 제안된 파일명도 제공되지 않았습니다. 하네스는 CDP 세션을 열고 Browser.setDownloadBehavior를 명시적 디렉터리로 설정한 뒤, 디스크에서 파일을 읽었습니다. 테스트 실행에서는 CSV 바이트는 그대로였지만 probe-export.csvdownload.csv로 도착했습니다. 더 넓은 확장/브라우저 조합은 테스트하지 않았습니다.

file:// URL과 기본 제공 Chrome에 대해, 이 글이 말하지 않는 것은 무엇인가요? 두 방향의 경계가 있습니다. 명령줄로 로드한 확장 프로그램에서는 file:// URL에서도 콘텐츠 스크립트가 실제로 동작합니다. 두 Chrome for Testing 빌드에서 테스트했을 때 스크립트가 정상 주입되었고 확장 ID도 올바르게 보고됐으므로, "확장에 기본 파일 접근 권한이 없어서 안 된다"는 흔한 주장은 이 설치 경로에서는 틀렸습니다. 고정 파일을 HTTP로 서빙하는 것이 더 좋은 관행인 이유는 file://가 주입을 막기 때문이 아니라 실제 대상과 더 비슷하기 때문입니다. 반대 방향으로는, 이 글 어디에도 기본 제공 Chrome이 확장을 실행할 수 없다고 쓰지 않았습니다. 측정한 것은 --load-extension 명령줄 경로뿐입니다. 패키징된 .crx 설치, 개발자 모드 로딩, 엔터프라이즈 allowlist 정책은 테스트하지 않았고, 이에 대한 주장도 하지 않습니다. 범위는 자동화 튜토리얼에서 흔히 안내하는 바로 그 플래그 조합입니다.

Ke
Ke
Thunderbit CTO | 시니어 데이터 사이언티스트 & ML 전문가 머신러닝과 데이터 과학 분야에서 약 10년에 가까운 경험을 쌓아온 Ke Shen은 컬럼비아 대학교 출신이며, 전 Walmart Labs의 시니어 데이터 사이언티스트였습니다. Python, R, Java, 통계 분야에서 동료들에게도 인정받는 깊은 전문성을 바탕으로, 복잡한 AI 알고리즘을 이론에서 실제 운영 수준의 아키텍처로 전환하는 데 필요한 실전 인사이트를 공유합니다.
목차
Thunderbit · AI 웹 데이터 에이전트

1회 클릭 안에 어떤 페이지든 데이터 추출

250,000명+ 사용자가 신뢰
무료 플랜 제공
웹페이지에서 스프레드시트까지
필요한 내용을 설명하세요 — Thunderbit의 AI Agent가 수집하고 Excel, Google Sheets, Airtable, Notion으로 내보냅니다. 시작은 무료입니다.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week