Build an AI YouTube Video Summarizer with LangChain and Next.js

A working YouTube summarizer built with LangChain and Next.js: fetch the transcript, chunk it with a text splitter, summarize each chunk, then combine the results into one final summary - full code for every step.

00:08:00 · SEP 26, 2026

빠른 답변

호스팅된 API로 자막을 가져오고, LangChain의 text splitter로 청크로 나눈 뒤, 각 청크를 요약하고, 청크 요약들을 하나의 최종 요약으로 합칩니다 - 5분짜리 영상뿐 아니라 3시간짜리 영상에서도 계속 작동하는 동일한 map-reduce 패턴입니다.
01

LangChain 설치하기

이 튜토리얼은 현재의 LangChain JS 패키지 분리 구조를 사용합니다 - 핵심 프리미티브, OpenAI 연동, text splitter가 각각 자신만의 패키지에 있습니다:

npm install langchain @langchain/core @langchain/openai @langchain/textsplitters
02

자막 가져오기

대시보드에서 키를 발급받고(100 무료 크레딧, 카드 불필요), Next.js 라우트 핸들러에서 자막 엔드포인트를 호출합니다:

// app/api/summarize/route.ts
import { NextResponse } from 'next/server';

async function fetchTranscript(videoId: string) {
  const res = await fetch(
    `https://getyoutubetranscript.com/api/v1/transcript?v=${videoId}`,
    { headers: { Authorization: `Bearer ${process.env.GETYOUTUBETRANSCRIPT_API_KEY}` } }
  );
  const json = await res.json();
  if (!json.success) throw new Error(json.message);
  // json.data.transcript is the full plain-text transcript,
  // json.data.word_count is its length in words.
  return json.data as { transcript: string; word_count: number; title: string };
}

transcript는 전체 순수 텍스트 자막이고 word_count는 그 길이입니다 - 짧은 영상에서 청크 분할 단계를 아예 건너뛸지 결정할 때 유용합니다.

03

자막을 청크로 나누기

긴 자막은 하나의 프롬프트로 LLM에 보내기 편안한 분량을 쉽게 넘어서므로 먼저 나눕니다. RecursiveCharacterTextSplitter는 문단, 그다음 문장, 그다음 단어 순으로 끊으려 시도해서, 문장 중간에서 잘리지 않고 청크가 읽기 좋게 유지됩니다:

import { RecursiveCharacterTextSplitter } from '@langchain/textsplitters';

const splitter = new RecursiveCharacterTextSplitter({
  chunkSize: 4000,
  chunkOverlap: 200,
});

const chunks = await splitter.splitText(transcript);
// A 20-minute video is usually 1-2 chunks; a 2-hour one might be 8-10.

chunkOverlap은 한 청크의 끝부분 맥락을 조금 다음 청크의 시작 부분에도 남겨서, 경계에서 나뉜 문장이 어느 쪽에서도 손실되지 않게 합니다.

04

각 청크 요약하기

ChatPromptTemplate + 모델 + StringOutputParser를 파이프로 연결하는 것(LangChain의 표현 언어, LCEL)이 체인을 만드는 현재의 안정적인 방법입니다 - 각 청크는 병렬로 자신만의 짧은 요약을 얻습니다:

import { ChatOpenAI } from '@langchain/openai';
import { ChatPromptTemplate } from '@langchain/core/prompts';
import { StringOutputParser } from '@langchain/core/output_parsers';

const model = new ChatOpenAI({ model: 'gpt-4.1-mini', temperature: 0 });

const chunkPrompt = ChatPromptTemplate.fromTemplate(
  'Summarize this part of a video transcript in 3-4 sentences, keeping any concrete facts, numbers, or names:\n\n{chunk}'
);
const chunkChain = chunkPrompt.pipe(model).pipe(new StringOutputParser());

const chunkSummaries = await Promise.all(
  chunks.map((chunk) => chunkChain.invoke({ chunk }))
);

Promise.all로 병렬 실행하는 것은 지연 시간에 중요합니다: 10개 청크짜리 자막을 하나씩 요약하면 10개를 동시에 요약하는 것보다 10배 더 오래 걸립니다.

05

청크 요약들을 하나로 합치기

청크 요약들은 이제 하나의 프롬프트에 함께 들어갈 만큼 짧아졌으므로, 두 번째 체인이 이를 최종 결과로 합칩니다:

const finalPrompt = ChatPromptTemplate.fromTemplate(
  'These are summaries of consecutive parts of the same video transcript, titled "{title}".\n' +
  'Combine them into one coherent summary (a short paragraph, then 3-5 key takeaways as bullet points):\n\n{summaries}'
);
const finalChain = finalPrompt.pipe(model).pipe(new StringOutputParser());

const summary = await finalChain.invoke({
  title,
  summaries: chunkSummaries.join('\n\n'),
});

return NextResponse.json({ summary });

이 2단계 구조(먼저 청크를 요약하고, 그다음 요약들을 요약)는 LangChain 자체의 map-reduce 패턴이 내부적으로 하는 일과 정확히 같습니다 - 레거시 헬퍼 체인을 쓰는 대신 여기서는 명시적으로 풀어썼는데, summarization chain 헬퍼 API가 LangChain 버전마다 바뀌어 왔고 순수한 LCEL 프리미티브가 더 안정적인 기반이기 때문입니다.

06

직접 스크래핑하는 대신 API로 자막을 가져오는 이유

위의 자막 가져오기 단계는 이 파이프라인에서 프로덕션 환경에서 실제로 더 어려워지는 유일한 부분입니다 - 자체 호스팅한 스크래핑 라이브러리는 클라우드 서버에 배포하는 순간 차단되는 경우가 흔합니다. 호스팅된 YouTube 자막 API는 바로 이 단계를 계속 작동하게 만들도록 설계되어 있어, 파이프라인의 나머지 부분이 이를 신경 쓸 필요가 없습니다.

전체 엔드포인트 참조, 인증, 속도 제한은 API 문서에 있습니다. 처음부터 끝까지 테스트하려면 대시보드에서 100 무료 크레딧이 포함된 키를 발급받으세요.

LangChain 요약기 자주 묻는 질문

Q01

짧은 영상도 청크로 나눠야 하나요?

꼭 그렇지는 않습니다 - 먼저 자막 응답의 word_count를 확인하세요. 10분짜리 영상의 자막은 보통 최신 LLM의 단일 프롬프트가 처리할 수 있는 양보다 훨씬 적으므로, 청크 분할/합치기 단계를 완전히 건너뛰고 바로 요약 호출 한 번으로 갈 수 있습니다.

Q02

고정 문자 수 분할 대신 RecursiveCharacterTextSplitter를 쓰는 이유는?

고정 분할은 문자 수가 딱 맞아떨어지는 곳에서 자르기 때문에 문장이나 단어 중간에서 잘릴 수 있습니다. RecursiveCharacterTextSplitter는 먼저 문단 구분을, 그다음 문장을, 그다음 단어를 시도하고, 어쩔 수 없을 때만 강제로 자릅니다 - 그래서 청크가 모델이 요약하기에 일관성 있게 유지됩니다.

Q03

OpenAI 대신 다른 LLM 제공업체를 사용할 수 있나요?

네 - LangChain의 채팅 모델 클래스는 동일한 인터페이스를 공유하므로, ChatOpenAI를 다른 제공업체의 LangChain 연동(Anthropic, Google 등)으로 바꿔도 파이프라인의 다른 부분은 바뀌지 않습니다.

Q04

LLMChain 대신 프롬프트, 모델, 출력 파서를 파이프로 연결하는 이유는?

LLMChain은 LangChain의 예전 체인 구성 API입니다. 파이프/LCEL 스타일(prompt.pipe(model).pipe(parser))이 현재 권장되는 패턴이며, 더 예측 가능하게 조합되고 LangChain의 최신 문서도 이를 기반으로 작성됩니다.

Q05

이걸 실행하는 데 비용이 얼마나 드나요?

두 가지 비용이 있습니다: LLM 호출(청크당 한 번, 합치기에 한 번 더 - 모델 제공업체가 청구), 그리고 API에서 자막을 가져올 때마다 1 크레딧입니다. 테스트하는 동안 가져오기 쪽은 100 무료 크레딧으로 충당됩니다.

Q06

영상에 자막이 전혀 없으면 어떻게 되나요?

자막 엔드포인트는 빈 문자열 대신 명확한 오류 코드(TRANSCRIPT_DISABLED 또는 TRANSCRIPT_NOT_FOUND)를 반환합니다 - text splitter에 무언가를 넘기기 전에 success: false를 확인하세요. 빈 자막을 요약하면 LLM 호출을 헛되이 낭비하게 됩니다.

관련 콘텐츠