크롬 내장 LLM(Prompt API)은 공짜 API 가 아니다
방문자 GPU 에서 도는, 결과를 믿을 수 없는 연산 — 그래서 보조에만 쓴다
크롬에서 이 두 줄이 돈다.
const session = await LanguageModel.create()
const answer = await session.prompt("이 댓글 악플이야?")
API 키도 서버도 없다. 처음 보면 공짜 GPT API 처럼 보인다. 결론은 아니다. 이건 방문자 GPU 에서 도는, 결과를 믿을 수 없는 클라이언트 연산이다. 이 글을 읽으면 이 API 를 어디에 써도 되고 어디에 쓰면 안 되는지 한 가지 기준으로 가를 수 있다.
시작은 코딩애플 영상(크롬이 몰래 설치하는 2GB 파일)이었다. 직접 확인해 보려고 JS 테스트 모음 앱에 섹션 하나를 붙였다.
다운로드와 사용은 따로 논다
availability() 는 아무것도 받지 않는다. 페이지 로드 때 불러도 안전하다. 실제 준비는 create() 에서 시작되고, 받아야 할 게 있으면 사용자 클릭(user activation) 안에서 불러야 한다. 세션은 대화 이력을 들고 있어서, 판정처럼 매번 독립이어야 하는 작업은 clone() 으로 갈라 쓴다. responseConstraint 에 JSON Schema 를 주면 출력 형식을 강제할 수 있다.
직접 재 보니 (2026-09-19, macOS · Chrome 152 · 플래그 없음)
모델은 이미 몇 달 전에 깔려 있었다. 프로필 폴더의 OptGuideOnDeviceModel/ 아래 weights.bin 이 4.27GB, 날짜는 2026-04-27. 내가 버튼을 누르기 5개월 전이다. 영상에서 말한 「몰래 설치되는 파일」이 이거였다.
downloadable 이 「파일이 없다」는 뜻이 아니었다. 페이지 로드 때는 downloadable 이었는데, create() 뒤에도 weights.bin 날짜는 그대로였다. 받은 게 없다. 대신 create() 가 18초 걸렸고 그 순간 program_cache.bin 이 생겼다. 다운로드가 아니라 GPU 용 준비(셰이더 컴파일)로 보인다.
일반 Chrome 에서 나온 건 Gemini Nano 였다. manifest 의 모델 이름이 v3Nano. 영상에서 말한 Gemma 4(약 2GB)는 내 환경에선 확인 못 했다. 그러니 「Gemma 4 로 한국어가 좋아졌다」는 이 글에서 쓰지 않는다.
번역은 영어를 거친다. ko→ja 를 요청했더니 받은 건 en_ko(75MB)와 en_ja(76MB) 두 쌍이었다. 한국어 → 영어 → 일본어 2단 번역이다. 언어 N 개에 모델 2N 개로 끝나는 건 좋은데, 존댓말 단계처럼 영어에 없는 뉘앙스가 중간에서 날아갈 수 있다. 한일 번역엔 불리한 구조다.
악플 판정은 이유와 결론이 엇갈렸다.
{"toxic": true, "category": "insult",
"reason": "특정 대상에게 외모에 대한 긍정적인 표현을 사용하여 모욕적인 의미를 내포하고 있습니다."}
「긍정적인 표현」인데 「모욕」이다. responseConstraint 덕에 JSON 형식은 지켰지만 내용은 못 믿는다. 이게 아래 기준의 실례다.
서버 API 와 비교하면
| 서버 LLM API | 크롬 내장 LLM | |
|---|---|---|
| 비용 | 내가 낸다 (토큰 과금) | 0 원. 대신 방문자의 전기 · GPU · 디스크가 낸다 |
| 입력 데이터 | 서버로 나간다 | 기기 밖으로 안 나간다 |
| 결과 신뢰 | 서버에서 나온 값 | DevTools 로 얼마든지 바꿀 수 있다 |
| 쓸 수 있는 사람 | 브라우저 무관 | 데스크톱 Chrome, 사양 조건 있음. Android · iOS 는 아직 미지원 |
| 모델 | 고를 수 있다 | 브라우저가 준 것 (내 환경은 Gemini Nano) |
사양 조건이 꽤 높다. 공식 문서 기준으로 프로필이 있는 디스크에 22GB 여유, VRAM 4GB 초과가 필요하다. 방문자 전원이 쓸 수 있다고 생각하면 안 된다.
기준 하나 — 클라이언트가 결과를 속여도 아무도 손해 안 보는가
| 쓰는 곳 | 판정 | 이유 |
|---|---|---|
| 댓글 보내기 전 「좀 세지 않아요?」 경고 | 쓴다 | 속이면 본인만 손해. 서버 검증은 따로 둔다 |
| 악플 차단 (서버 검증 대신) | 안 쓴다 | JS 한 줄로 우회된다 |
| 블로그 이미지 alt 추천 | 쓴다 | 틀려도 작성자가 고친다 |
| 읽는 사람용 번역 · 요약 | 쓴다 | 결과를 보는 사람이 곧 연산한 사람 |
| 포인트 · 랭킹 · 과금 판정 | 안 쓴다 | 속이면 이득 |
| 기능 전체를 여기에 기대기 | 안 쓴다 | Chrome 밖 사용자는 아무것도 못 쓴다. 폴백 필수 |
한 줄로 줄이면 보조(assist)에는 쓰고, 판정(enforce)에는 쓰지 않는다.
판정이 필요하면 서버 쪽에서 해야 한다. 서버에서 빠르고 싸게 판정만 하는 모델로는 Jev 같은 선택지도 있다.
기능을 브라우저 안에서 돌릴지 서버로 보낼지 정하는 단계라면 negabaro 에 상담해도 된다. 이런 경계를 정하는 일을 실제 서비스에서 해 왔다.
핵심 개념
페이지 로드 때는 availability() 만 — 아무것도 받지 않는다
create() 는 사용자 클릭 안에서 — downloadable 이면 여기서 준비가 시작된다
판정처럼 매번 독립이어야 하면 clone() 으로 세션을 갈라 쓴다
형식은 responseConstraint 로 지키고, 내용은 믿지 않는다
Chrome 밖 사용자를 위한 폴백을 꼭 둔다
장점
- ✓ 추론 비용 0 — 서버 · API 키 · 과금이 없다
- ✓ 입력이 기기 밖으로 나가지 않는다
단점
- ✗ 결과를 믿을 수 없다 — 클라이언트가 바꿀 수 있다
- ✗ 데스크톱 Chrome 과 높은 사양에서만 돈다
- ✗ 번역이 영어를 거쳐 존댓말 같은 뉘앙스가 빠질 수 있다