Streaming — 답변을 실시간으로 흘려보내기
· 스트리밍(Streaming)은 모델의 답변을 다 만들어진 뒤 한꺼번에 주는 대신, 생성되는 대로 조금씩 흘려보내는 방식이에요.
· LangChain에서는
invoke 대신 stream을 쓰면, 답변이 토큰 단위로 순서대로 도착합니다(타이핑 효과).· 체감 대기 시간이 크게 줄어 챗봇처럼 즉각 반응하는 UX를 만들 때 특히 유용합니다.
챗봇에 질문을 던졌을 때, 답이 한 글자씩 타이핑되듯 나타나는 화면을 본 적 있을 거예요. 이건 답변이 다 완성될 때까지 기다렸다가 한꺼번에 보여 주는 게 아니라, 모델이 글자를 만들어 내는 즉시 화면으로 흘려보내기 때문에 가능한 일입니다. 이 방식을 스트리밍(Streaming)이라고 부릅니다. 오늘은 LangChain에서 스트리밍이 무엇이고 왜 유용한지, 그리고 어떻게 쓰는지 차근차근 살펴보겠습니다.
🤔 스트리밍이 왜 필요할까
LLM은 답변을 앞에서부터 순서대로 한 조각씩 생성합니다. 긴 답변일수록 전체가 완성되기까지 시간이 걸리는데, 완성될 때까지 화면에 아무것도 안 보이면 사용자는 "멈춘 건가?" 하고 답답해집니다.
스트리밍은 이 기다리는 시간을 체감상 크게 줄여 줍니다. 첫 글자가 나오기 시작하는 순간부터 화면에 하나씩 채워지니, 실제 전체 생성 시간이 같아도 훨씬 빠르게 느껴지죠. 특히 대화형 챗봇처럼 반응 속도가 중요한 서비스에서는 거의 필수적인 요소입니다.
스트리밍의 핵심은 "답을 다 만들 때까지 기다리지 말고, 만들어지는 대로 바로 보여 주자"는 것입니다.
⚖️ 한 번에 vs 조금씩, 무엇이 다른가
두 방식의 차이는 결과를 언제 받느냐에 있습니다. 일반 실행(invoke)은 답변이 완전히 끝난 뒤 통째로 돌려줍니다. 반면 스트리밍(stream)은 생성되는 조각들을 순서대로 계속 내보냅니다.
정리하면 최종 결과만 필요하면 invoke, 반응 속도와 실시간 표시가 중요하면 stream을 고르면 됩니다.
🧩 토큰 단위로 흘려보낸다는 것
모델이 답변을 만들 때의 최소 단위를 흔히 토큰(token)이라고 부릅니다. 토큰은 단어 하나일 수도, 단어의 일부 조각일 수도 있어요. 스트리밍은 이 토큰이 하나 생성될 때마다 바로 내보내는 방식입니다.
그래서 스트리밍의 결과는 완성된 문장 하나가 아니라, 조각(청크, chunk)들이 순서대로 이어지는 흐름입니다. 받는 쪽에서는 이 조각들을 도착하는 대로 화면에 이어 붙이면 자연스러운 타이핑 효과가 됩니다. 마지막 조각까지 모두 이으면 invoke로 받은 것과 같은 전체 답변이 되는 셈이죠.
💻 코드로 보는 stream
LCEL로 만든 체인이라면 stream은 특별히 새로 짤 게 없습니다. invoke를 stream으로 바꾸고 도착하는 조각을 반복해서 받으면 끝입니다.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template("{topic}를 짧게 설명해줘")
model = ... # 사용할 LLM
parser = StrOutputParser()
chain = prompt | model | parser
# invoke 대신 stream — 조각을 하나씩 반복해서 받는다
for chunk in chain.stream({"topic": "스트리밍"}):
print(chunk, end="", flush=True) # 도착하는 대로 이어 붙여 출력
포인트는 for chunk in chain.stream(...) 한 줄입니다. 조각이 도착할 때마다 반복문이 한 번씩 돌고, print(..., end="", flush=True)로 줄바꿈 없이 곧바로 이어 출력하면 타이핑되는 것처럼 보입니다. 웹 서비스라면 이 조각들을 화면으로 하나씩 밀어 주면 됩니다.
🔀 stream vs invoke vs batch
LCEL 체인은 같은 체인 하나로 실행 방식만 골라 쓸 수 있습니다. 상황에 맞는 실행 방법을 정리하면 이렇습니다.
| 실행 방식 | 언제 쓰나 |
|---|---|
| invoke | 완성된 답변 전체가 필요할 때(가장 기본) |
| stream | 답변을 실시간으로 조금씩 보여 주고 싶을 때 |
| batch | 여러 입력을 한 번에 묶어 처리할 때 |
| astream | 비동기 환경에서 스트리밍이 필요할 때 |
즉 체인을 한 번 |로 이어 두면, 스트리밍을 위해 로직을 새로 짤 필요가 없습니다. invoke를 stream으로 바꾸는 것만으로 실시간 출력이 되는 것이 LCEL의 큰 장점이에요.
🔧 실무 팁
- 조각은 이어 붙여라 — 스트리밍은 조각들의 흐름이므로, 화면이든 변수든 도착하는 대로 누적해서 다뤄야 합니다.
- 출력 버퍼를 비워라 — 콘솔에서는
flush=True를 줘야 조각이 곧바로 보입니다. 안 그러면 몰아서 나올 수 있어요. - 웹은 스트리밍 응답으로 — 서버라면 한 번에 반환하지 말고, 조각을 계속 밀어 보내는 방식(예: 서버센트이벤트)으로 전달하세요.
- 중간 취소도 고려 — 사용자가 답변을 멈추고 싶을 수 있으니, 스트림을 중간에 끊는 처리도 함께 설계해 두면 좋습니다.
❓ 자주 묻는 질문 (FAQ)
Q. 스트리밍이 답변을 더 빨리 만들어 주나요?
A. 전체 생성 시간 자체가 줄어드는 건 아닙니다. 다만 첫 조각부터 바로 보여 주기 때문에 기다리는 느낌이 크게 줄어 체감 속도가 빨라집니다.
Q. 스트리밍으로 받아도 전체 답변을 얻을 수 있나요?
A. 네. 도착한 조각을 순서대로 모두 이어 붙이면 invoke로 받은 것과 같은 완성 답변이 됩니다.
Q. 모든 단계가 스트리밍되나요?
A. 텍스트를 만들어 내는 모델 부분이 스트리밍의 핵심입니다. 중간에 한꺼번에 결과를 내는 단계가 끼면 그 지점에서는 조각 흐름이 잠깐 끊길 수 있어요.
마무리
정리하면, 스트리밍은 모델의 답변을 다 기다리지 않고 생성되는 대로 조각씩 흘려보내 실시간으로 보여 주는 방식입니다. LangChain에서는 invoke를 stream으로 바꾸는 것만으로 손쉽게 얻을 수 있고, 체감 대기 시간을 줄여 즉각 반응하는 챗봇 UX를 만드는 데 큰 도움이 됩니다. 다음 편에서는 LangChain 내부에서 무슨 일이 벌어지는지 들여다보는 콜백(Callback)과 로깅 이야기로 이어가 보겠습니다.
댓글
댓글 쓰기