Chrome Built-in AI — LanguageModel API로 이미지에서 텍스트 추출하기

9 min read
Chrome Built-in AILanguageModelOCRJavaScript브라우저 AI
Chrome Built-in AI — LanguageModel API로 이미지에서 텍스트 추출하기

브라우저에서 이미지 속 글자를 읽는다

Tesseract.js나 Google Vision API 없이, 브라우저 자체 AI로 이미지에서 텍스트를 뽑아낼 수 있다. Chrome의 LanguageModel API는 이미지를 입력으로 받아 텍스트를 출력할 수 있는 멀티모달 API다.

외부 서버가 없다. API 키도 없다. Chrome 브라우저와 플래그 설정 하나로 동작한다.

[💡 잠깐! 이 용어는?] LanguageModel API (Prompt API): window.LanguageModel로 접근하는 Chrome Built-in AI API. Gemini Nano를 직접 호출해 텍스트 생성, 분류, 추출 작업을 수행한다. 이미지 입력도 지원해 멀티모달 추론이 가능하다.


준비: Chrome 플래그 활성화

LanguageModel API는 아직 실험 단계다. Chrome 주소창에서 아래 플래그를 활성화해야 한다.

chrome://flags/#prompt-api-for-gemini-nano

Enabled로 설정 후 Chrome 재시작. 이미지 입력을 쓰려면 추가로:

chrome://flags/#optimization-guide-on-device-model

Enabled로 설정한다. 모델이 로컬에 없으면 첫 실행 시 자동 다운로드된다.


기본 세션 생성

이미지를 입력으로 사용하려면 세션 생성 시 expectedInputsimage 타입을 명시해야 한다.

src/ocr-session.js — 이미지 지원 LanguageModel 세션 생성
async function createOcrSession() {
  const available = await LanguageModel.availability({
    expectedInputs: [{ type: 'image' }],
  });
 
  if (available === 'unavailable') {
    throw new Error('이미지 지원 LanguageModel을 사용할 수 없습니다.');
  }
 
  const session = await LanguageModel.create({
    expectedInputs: [{ type: 'image' }],
    initialInputs: [
      {
        role: 'system',
        content:
          '당신은 이미지에서 텍스트를 추출하는 OCR 전문가입니다. 이미지에 보이는 텍스트를 정확하게 읽어주세요.',
      },
    ],
  });
 
  return session;
}

initialInputs의 system 메시지가 중요하다. "OCR 전문가"라는 역할을 명확히 주면 모델이 이미지 묘사보다 텍스트 추출에 집중한다.


텍스트만 추출하는 방법

가장 단순한 OCR이다. 이미지를 주고 텍스트만 받아온다.

src/ocr.js — 이미지에서 텍스트 추출
async function extractText(imageFile) {
  const session = await createOcrSession();
 
  const result = await session.prompt([
    {
      role: 'user',
      content: [
        {
          type: 'image',
          value: imageFile,
        },
        {
          type: 'text',
          value: '이 이미지에서 텍스트를 전부 추출해주세요. 텍스트만 출력하고 다른 설명은 필요 없습니다.',
        },
      ],
    },
  ]);
 
  return result;
}

session.prompt()의 content 배열에 이미지(type: 'image')와 텍스트 지시(type: 'text')를 함께 넣는다. 이미지 파일은 File 객체나 Blob을 그대로 넘기면 된다.


JSON 스키마로 구조화된 응답 받기

텍스트만 추출하는 것 외에, **바운딩 박스(위치 정보)**를 함께 받을 수도 있다. responseConstraint에 JSON 스키마를 넘기면 구조화된 응답이 나온다.

src/ocr-structured.js — 텍스트와 위치 정보를 JSON으로 추출
const ocrSchema = {
  type: 'object',
  properties: {
    text_blocks: {
      type: 'array',
      items: {
        type: 'object',
        properties: {
          text: { type: 'string' },
          bounding_box: {
            type: 'object',
            properties: {
              x: { type: 'number' },
              y: { type: 'number' },
              width: { type: 'number' },
              height: { type: 'number' },
            },
            required: ['x', 'y', 'width', 'height'],
          },
        },
        required: ['text', 'bounding_box'],
      },
    },
  },
  required: ['text_blocks'],
};
 
async function extractTextWithBounds(imageFile) {
  const session = await createOcrSession();
 
  const result = await session.prompt(
    [
      {
        role: 'user',
        content: [
          { type: 'image', value: imageFile },
          {
            type: 'text',
            value: '이미지에서 텍스트와 각 텍스트의 위치(바운딩 박스)를 JSON으로 반환해주세요.',
          },
        ],
      },
    ],
    {
      responseConstraint: ocrSchema,
    }
  );
 
  return JSON.parse(result);
}

[💡 잠깐! 이 용어는?] responseConstraint: LanguageModel API의 출력 형식을 JSON 스키마로 제약하는 옵션. 이 옵션이 있으면 모델이 반드시 지정된 스키마에 맞는 JSON을 반환한다. 자유 텍스트 대신 파싱 가능한 구조화된 데이터를 받을 수 있다.


실제 테스트 결과

두 가지 방식을 비교한 테스트 결과다.

항목텍스트만 추출JSON + 바운딩 박스
텍스트 정확도높음중간
위치 정보없음부정확 (근사값)
처리 속도빠름느림
간판·표지판잘 동작동작하나 위치 오차 큼
밀집된 텍스트누락 발생더 많은 누락

바운딩 박스 테스트에서 중요한 점이 있다. 텍스트 자체는 잘 읽히는데, 좌표값이 실제 이미지 좌표와 다르다. Gemini Nano가 공간 추론에 최적화된 모델이 아니기 때문이다.

비유하면 책을 읽을 수 있는 사람에게 "이 페이지의 3번째 단어가 몇 번째 줄 몇 번째 칸에 있어?"라고 물어보는 것과 같다. 단어 자체는 읽지만 정확한 픽셀 좌표를 계산하는 건 다른 능력이다.

실용적으로는 텍스트만 추출하는 방식이 더 신뢰할 수 있다.


적합한 케이스

잘 동작하는 경우:

  • 간판, 표지판, 현수막처럼 큰 글씨
  • 스크린샷에 있는 UI 텍스트
  • 명함, 영수증처럼 구조가 단순한 문서
  • 단일 언어로 작성된 이미지

동작이 불안정한 경우:

  • 손글씨
  • 텍스트가 배경에 묻히는 경우 (저콘트라스트)
  • 이미지 내 표·그래프 속 숫자
  • 여러 언어가 섞인 이미지

브라우저 지원 현황

브라우저지원 여부비고
Chrome 127+✅ (플래그 필요)실험적 기능
Chrome Stable🔜안정화 진행 중
Firefox미지원
Safari미지원
Edge🔜Chromium 기반, 향후 지원 예정

현재는 Chrome 개발자 도구를 쓰는 개발자나 프로토타이핑 단계에 적합하다. 프로덕션 서비스에 쓰려면 플래그 없이도 동작하는 stable 릴리즈를 기다려야 한다.


정리

  • Chrome LanguageModel.create({ expectedInputs: [{ type: 'image' }] })로 이미지 입력이 가능한 세션을 만들 수 있다.
  • session.prompt()에 이미지 파일과 텍스트 지시를 함께 넣으면 OCR이 동작한다.
  • responseConstraint로 JSON 스키마를 지정하면 구조화된 응답을 받을 수 있다.
  • 텍스트 추출 자체는 간판·표지판 수준에서 잘 동작한다. 바운딩 박스 좌표는 부정확하다.
  • 현재는 Chrome 플래그 활성화가 필요한 실험 기능이다.

서버 없이 브라우저에서만 동작하는 OCR이 필요한 상황 — 내부 도구, 오프라인 앱, 프라이버시가 중요한 서비스 — 에서 충분히 실험해볼 만하다.


참고:

관심 있을 만한 포스트

Chrome Built-in AI — 브라우저에서 Word·Excel·PPT·PDF를 AI로 요약하기

officeParser로 오피스 문서를 텍스트로 파싱하고, Chrome Summary API로 브라우저 내에서 AI 요약을 생성하는 방법을 다룬다.

Chrome Built-in AISummary API

Bun v1.3.12 — 브라우저 자동화와 인프로세스 Cron이 기본 내장됐다

Playwright 없이 브라우저를 제어하는 Bun.WebView, 그리고 서버 재시작 없이 작업을 스케줄링하는 Bun.cron의 기술적 구조.

Bun브라우저 자동화

Zustand 소프트 삭제 — enumerable:false로 컴포넌트 크래시 없이 처리하기

JavaScript property descriptor의 enumerable 플래그를 활용해 삭제된 엔티티를 투명하게 처리하는 Zustand 패턴을 소개한다.

Zustand상태 관리

JavaScript 이미지 프리로딩 — 5가지 방법 비교

new Image, link preload, hidden div, Cache API, fetch — 각 프리로딩 방식의 장단점과 상황별 선택 기준을 정리한다.

JavaScript성능

JavaScript 물리 엔진 만들기 — 100줄로 구현하는 2D 물리 시뮬레이션

벡터 연산, 원 충돌 감지, 충격량 기반 응답까지 순수 JavaScript로 2D 물리 엔진을 직접 만든다.

Physics EngineCanvas

Native JSON Modules — 번들러 없이 JSON을 import하는 시대

Import Attributes와 함께 표준이 된 native JSON module. 어떻게 동작하고, 기존 번들러 방식과 뭐가 다른지 정리했다.

JavaScriptESM

Babel 7.29.0 — 10년 역사의 마지막 마이너, 그리고 8 RC1

2026년 1월 31일, Babel 7의 마지막 마이너 릴리스가 공개됐다. 이 버전이 갖는 역사적 의미와 Babel 8 RC1의 핵심 변화를 정리한다.

BabelJavaScript

Error.isError() — realm을 넘나드는 안전한 에러 검사 API

instanceof Error가 iframe과 worker에서 실패하는 이유, 그리고 이를 근본적으로 해결하는 Error.isError()의 동작 원리를 정리한다.

JavaScriptError.isError