Chrome Built-in AI — 브라우저에서 Word·Excel·PPT·PDF를 AI로 요약하기
브라우저가 AI 요약 엔진이 된다
서버를 쓰지 않고, API 키도 없이, 순수하게 브라우저 안에서 Word·Excel·PPT·PDF 파일을 AI로 요약하는 게 가능해졌다. Chrome의 내장 AI 기능인 Summary API와 오피스 파일 파서 officeParser를 조합하면 된다.
흐름은 단순하다.
사용자가 파일 선택 → officeParser가 텍스트 추출 → Chrome Summary API가 요약
서버 왕복 없이 사용자 기기에서 전부 처리된다. 문서가 외부 서버로 전송되지 않으므로 민감한 업무 문서에도 쓸 수 있다.
[💡 잠깐! 이 용어는?]
Chrome Built-in AI: Chrome 브라우저에 Gemini Nano 모델을 내장한 기능. window.Summarizer, window.LanguageModel 등의 API로 접근한다. 별도 모델 다운로드 없이 브라우저가 설치 시 함께 제공하는 모델을 활용한다.
준비: officeParser 설치
officeParser는 Node.js 기반 라이브러리지만, Vite 등으로 번들하면 브라우저에서도 동작한다.
npm install officeparser지원 포맷:
| 파일 형식 | 확장자 | 파싱 결과 |
|---|---|---|
| Word | .docx | 본문 텍스트 |
| Excel | .xlsx | 시트별 셀 데이터 |
| PowerPoint | .pptx | 슬라이드별 텍스트 |
.pdf | 페이지별 텍스트 |
officeParser가 핵심을 처리해준다. 반환값은 형식에 따라 조금씩 다르다.
Summary API 기본 사용법
Chrome의 window.Summarizer API는 긴 텍스트를 짧게 요약해준다.
async function createSummarizer(context) {
const available = await window.Summarizer.availability();
if (available === 'unavailable') {
throw new Error('Summary API를 사용할 수 없습니다.');
}
const summarizer = await window.Summarizer.create({
type: 'tldr',
length: 'long',
sharedContext: context,
});
return summarizer;
}[💡 잠깐! 이 용어는?] sharedContext: 요약 전에 모델에 전달하는 배경 정보. "이건 회의록이다", "이건 재무 보고서다" 같은 맥락을 주면 요약 품질이 높아진다.
type: 'tldr'는 "너무 길어서 안 읽음(Too Long Didn't Read)" 형식의 요약을 의미한다. 핵심만 뽑아내는 데 적합하다. length: 'long'은 요약문 자체의 길이 옵션이다.
요약 실행:
async function summarizeText(text, summarizer) {
const result = await summarizer.summarize(text, {
context: '이 문서의 핵심 내용을 요약해주세요.',
});
return result;
}파일 형식별 텍스트 추출
Word (.docx)
Word 문서는 가장 단순하다. parseOfficeAsync가 전체 본문을 하나의 문자열로 반환한다.
import { parseOfficeAsync } from 'officeparser';
async function extractFromDocx(file) {
const data = await parseOfficeAsync(file);
return data.toText();
}PDF (.pdf)
PDF도 비슷하다. 페이지 순서대로 텍스트가 이어진다.
import { parseOfficeAsync } from 'officeparser';
async function extractFromPdf(file) {
const data = await parseOfficeAsync(file);
return data.toText();
}PowerPoint (.pptx)
PPT는 슬라이드별로 데이터가 분리된다. 슬라이드 번호를 붙여서 컨텍스트를 살리는 게 좋다.
import { parseOfficeAsync } from 'officeparser';
async function extractFromPptx(file) {
const data = await parseOfficeAsync(file);
const slides = data.slides || [];
return slides
.map((slide, index) => `[슬라이드 ${index + 1}]\n${slide.text}`)
.join('\n\n');
}슬라이드 헤더를 붙이면 Summary API가 "3번째 슬라이드는 결론 파트"라는 구조를 인식하고 요약에 반영한다.
Excel (.xlsx)
Excel은 다른 형식과 다르게 접근해야 한다. 행/열 데이터를 그대로 넘기면 AI가 맥락을 잃는다. CSV 형태로 변환해서 넘기는 게 효과적이다.
import { parseOfficeAsync } from 'officeparser';
async function extractFromXlsx(file) {
const data = await parseOfficeAsync(file);
const sheets = data.sheets || [];
return sheets
.map((sheet) => {
const header = `[시트: ${sheet.name}]`;
const rows = sheet.rows
.map((row) => row.cells.map((cell) => cell.value ?? '').join(','))
.join('\n');
return `${header}\n${rows}`;
})
.join('\n\n');
}비유하면 스프레드시트는 표이고, AI는 이야기를 읽는 독자다. 표를 그대로 주면 독자가 헷갈린다. "1행은 헤더, 2행부터 데이터"라는 구조를 CSV로 명확히 보여줘야 한다.
전체 통합: 파일 타입 자동 감지
import { parseOfficeAsync } from 'officeparser';
const FILE_CONTEXTS = {
docx: 'Word 문서입니다. 본문의 주요 내용을 요약해주세요.',
pdf: 'PDF 문서입니다. 핵심 정보를 추출해주세요.',
pptx: '프레젠테이션 파일입니다. 각 슬라이드의 핵심 메시지를 요약해주세요.',
xlsx: '스프레드시트 데이터입니다. 데이터의 주요 패턴과 인사이트를 요약해주세요.',
};
async function extractText(file) {
const ext = file.name.split('.').pop().toLowerCase();
const data = await parseOfficeAsync(file);
if (ext === 'xlsx') {
const sheets = data.sheets || [];
return sheets
.map((sheet) => {
const rows = sheet.rows
.map((row) => row.cells.map((c) => c.value ?? '').join(','))
.join('\n');
return `[시트: ${sheet.name}]\n${rows}`;
})
.join('\n\n');
}
if (ext === 'pptx') {
const slides = data.slides || [];
return slides
.map((slide, i) => `[슬라이드 ${i + 1}]\n${slide.text}`)
.join('\n\n');
}
return data.toText();
}
async function summarizeDocument(file) {
const ext = file.name.split('.').pop().toLowerCase();
const context = FILE_CONTEXTS[ext] ?? '문서의 핵심 내용을 요약해주세요.';
const text = await extractText(file);
const summarizer = await window.Summarizer.create({
type: 'tldr',
length: 'long',
sharedContext: context,
});
const summary = await summarizer.summarize(text, { context });
return summary;
}파일 확장자에 따라 컨텍스트도 다르게 넘긴다. 같은 문서라도 "이건 프레젠테이션이야"라고 알려주는 것과 그냥 텍스트로 넘기는 건 요약 결과가 다르다.
알려진 제한 사항
| 제한 | 내용 |
|---|---|
| 파일 크기 | 대용량 파일에서 QuotaExceededError 발생. Excel 기준 200행은 OK, 1000행은 실패 |
| 브라우저 지원 | Chrome 138+ 필요. Firefox·Safari 미지원 |
| 언어 | 영어 요약 품질이 가장 높음. 한국어도 동작하나 결과 품질이 상대적으로 낮음 |
| 모델 크기 | Gemini Nano는 경량 모델. 복잡한 수식·도표 해석은 GPT-4급과 차이 있음 |
QuotaExceededError는 컨텍스트 윈도우 초과다. 큰 파일은 청크 단위로 나눠서 각각 요약한 뒤 합치는 방식으로 우회할 수 있다.
async function summarizeInChunks(text, summarizer, chunkSize = 3000) {
const chunks = [];
for (let i = 0; i < text.length; i += chunkSize) {
chunks.push(text.slice(i, i + chunkSize));
}
const chunkSummaries = await Promise.all(
chunks.map((chunk) => summarizer.summarize(chunk))
);
const combined = chunkSummaries.join('\n\n');
return summarizer.summarize(combined, {
context: '아래는 문서의 각 부분을 요약한 내용입니다. 전체를 통합 요약해주세요.',
});
}정리
officeParser로 Word·Excel·PPT·PDF를 텍스트로 추출할 수 있다.- Chrome
window.Summarizer.create()에sharedContext와type: 'tldr'를 설정하면 브라우저 내 AI 요약이 동작한다. - Excel은 CSV 형식으로 변환, PPT는 슬라이드 번호를 붙여 넘기면 요약 품질이 높아진다.
- 대용량 파일은 청크로 나눠 2단계 요약으로 우회한다.
- 서버 없이 브라우저에서 전부 처리되므로 민감한 문서에도 적용할 수 있다.
Summary API는 아직 실험 단계지만, 서버리스 AI 요약이 필요한 내부 도구나 프로토타입에는 충분히 써볼 만하다.
참고:
- Raymond Camden — Summarizing Docs with Built-in AI: https://www.raymondcamden.com/2026/04/17/summarizing-docs-with-built-in-ai
- officeParser GitHub: https://github.com/harshankur/officeParser
- Chrome AI APIs 공식 문서: https://developer.chrome.com/docs/ai/built-in
관심 있을 만한 포스트
Chrome Built-in AI — LanguageModel API로 이미지에서 텍스트 추출하기
Chrome의 LanguageModel API를 사용해 서버 없이 브라우저에서 이미지 OCR을 구현하는 방법과 실제 테스트 결과를 정리한다.
Bun v1.3.12 — 브라우저 자동화와 인프로세스 Cron이 기본 내장됐다
Playwright 없이 브라우저를 제어하는 Bun.WebView, 그리고 서버 재시작 없이 작업을 스케줄링하는 Bun.cron의 기술적 구조.
Zustand 소프트 삭제 — enumerable:false로 컴포넌트 크래시 없이 처리하기
JavaScript property descriptor의 enumerable 플래그를 활용해 삭제된 엔티티를 투명하게 처리하는 Zustand 패턴을 소개한다.
JavaScript 이미지 프리로딩 — 5가지 방법 비교
new Image, link preload, hidden div, Cache API, fetch — 각 프리로딩 방식의 장단점과 상황별 선택 기준을 정리한다.
JavaScript 물리 엔진 만들기 — 100줄로 구현하는 2D 물리 시뮬레이션
벡터 연산, 원 충돌 감지, 충격량 기반 응답까지 순수 JavaScript로 2D 물리 엔진을 직접 만든다.
Native JSON Modules — 번들러 없이 JSON을 import하는 시대
Import Attributes와 함께 표준이 된 native JSON module. 어떻게 동작하고, 기존 번들러 방식과 뭐가 다른지 정리했다.
Babel 7.29.0 — 10년 역사의 마지막 마이너, 그리고 8 RC1
2026년 1월 31일, Babel 7의 마지막 마이너 릴리스가 공개됐다. 이 버전이 갖는 역사적 의미와 Babel 8 RC1의 핵심 변화를 정리한다.
Error.isError() — realm을 넘나드는 안전한 에러 검사 API
instanceof Error가 iframe과 worker에서 실패하는 이유, 그리고 이를 근본적으로 해결하는 Error.isError()의 동작 원리를 정리한다.