Callback와 로깅 — LangChain 내부 들여다보기

📌 3줄 요약
· 콜백(Callback)은 LangChain이 실행되는 중간중간 특정 시점마다 내가 지정한 함수를 자동으로 불러 주는 장치예요.
· 이 콜백을 이용하면 체인이 언제 시작하고, 어떤 프롬프트가 들어가고, 어떤 답이 나왔는지를 실시간으로 들여다보고 로깅할 수 있습니다.
· 덕분에 "왜 이런 답이 나왔지?"를 추적하는 디버깅과 모니터링이 훨씬 쉬워집니다.
🚀 체인 시작 → 🔔 이벤트 발생 → 🪝 콜백 호출 → 📝 기록·로깅

체인을 만들어 실행하면, 겉으로는 질문을 넣으면 답이 나오는 단순한 상자처럼 보입니다. 하지만 그 상자 안에서는 프롬프트가 조립되고, 모델이 호출되고, 출력이 정리되는 여러 단계가 순서대로 벌어지고 있어요. 이 내부 과정을 밖에서 들여다볼 수 있게 해 주는 장치가 바로 콜백(Callback)입니다. 오늘은 콜백이 무엇이고 왜 유용한지, 그리고 이를 활용한 로깅이 어떻게 이뤄지는지 차근차근 살펴보겠습니다.


🤔 콜백이 왜 필요할까

체인이 예상과 다른 답을 내놓았을 때, 우리가 볼 수 있는 건 보통 최종 결과 하나뿐입니다. 그런데 문제의 원인은 대개 중간에 있어요. 실제로 어떤 프롬프트가 모델에 들어갔는지, 모델이 어떤 원문을 돌려줬는지를 모르면 원인을 짚기 어렵습니다.

콜백은 이 보이지 않던 중간 과정을 밖으로 꺼내 주는 역할을 합니다. 체인이 시작될 때, 모델이 호출될 때, 답이 완성될 때처럼 중요한 순간마다 내가 등록해 둔 함수가 자동으로 실행되죠. 그래서 실행 흐름을 기록하고 추적하는 일이 한결 수월해집니다.

콜백의 핵심은 "실행 중간의 특정 순간마다, 내가 원하는 코드를 끼워 넣을 수 있게 하자"는 것입니다.

🔔 콜백은 언제 불릴까 — 이벤트라는 개념

콜백을 이해하는 열쇠는 이벤트(event)라는 개념입니다. 체인이 실행되는 동안 "지금 막 시작했다", "모델 호출이 끝났다", "오류가 났다" 같은 특정 순간들이 생기는데, 이런 순간 하나하나를 이벤트라고 부릅니다.

LangChain은 이 이벤트가 발생할 때마다 정해진 이름의 함수를 찾아 호출합니다. 예를 들어 체인이 시작되면 시작 담당 함수를, 모델 호출이 끝나면 종료 담당 함수를 부르는 식이죠. 우리는 그저 어떤 이벤트에서 무엇을 할지만 미리 적어 두면 됩니다.


📝 로깅과 콜백은 어떻게 연결되나

로깅(logging)은 실행 중에 일어난 일을 기록으로 남기는 것을 말합니다. 콜백과 로깅은 궁합이 아주 좋아요. 콜백이 "지금 이 순간에 이런 일이 벌어졌다"고 알려 주면, 그 정보를 파일이나 콘솔에 적어 두는 것이 곧 로깅이기 때문입니다.

🪝 콜백
"어떤 일이 벌어졌는지"를 알려 주는 장치입니다. 순간을 포착하는 역할이에요.
📝 로깅
그 정보를 기록으로 남기는 일입니다. 포착한 순간을 보존하는 역할이에요.

즉 콜백으로 순간을 잡고, 그 안에서 로깅으로 남긴다고 생각하면 이해가 쉽습니다. 이렇게 쌓인 기록은 나중에 문제를 되짚어 볼 때 든든한 단서가 됩니다.


💻 코드로 보는 콜백 핸들러

콜백을 담는 그릇을 콜백 핸들러(handler)라고 부릅니다. 처리하고 싶은 이벤트에 해당하는 메서드만 골라 채워 넣으면 되고, 실행할 때 이 핸들러를 함께 넘겨 주면 됩니다.

from langchain_core.callbacks import BaseCallbackHandler

class MyLogger(BaseCallbackHandler):
    # 체인이 시작되는 순간
    def on_chain_start(self, serialized, inputs, **kwargs):
        print("▶ 체인 시작, 입력:", inputs)

    # 모델 호출이 끝난 순간
    def on_llm_end(self, response, **kwargs):
        print("■ 모델 응답 도착")

    # 오류가 난 순간
    def on_chain_error(self, error, **kwargs):
        print("✕ 오류 발생:", error)

# 실행할 때 콜백 핸들러를 함께 넘긴다
chain.invoke({"topic": "콜백"}, config={"callbacks": [MyLogger()]})

포인트는 필요한 이벤트 메서드만 정의하면 된다는 점입니다. 위에서는 시작·응답·오류 세 순간만 다뤘지만, 관심 없는 이벤트는 그냥 비워 두면 됩니다. 실행 시 config에 핸들러를 넣어 주면, 해당 순간마다 우리가 적은 코드가 자동으로 호출됩니다.


🗂️ 대표적인 콜백 이벤트 한눈에

콜백 핸들러가 다룰 수 있는 이벤트는 여러 가지가 있습니다. 자주 쓰이는 것들을 정리하면 이렇습니다.

이벤트 메서드 언제 불리나
on_chain_start체인 실행이 막 시작될 때
on_llm_start모델(LLM) 호출이 시작될 때
on_llm_end모델이 응답을 다 돌려줬을 때
on_chain_end체인 실행이 정상적으로 끝났을 때
on_chain_error실행 도중 오류가 발생했을 때

이 이벤트들을 조합하면 시작부터 끝까지의 흐름을 촘촘히 기록할 수 있습니다. 어떤 이벤트를 볼지는 목적에 따라 골라 쓰면 됩니다.


🔧 실무 팁

  • 처음엔 콘솔 출력부터 — 복잡한 로깅 설정 없이 print로 흐름만 찍어 봐도 내부가 훤히 보이기 시작합니다.
  • 필요한 이벤트만 다뤄라 — 모든 이벤트를 채울 필요는 없습니다. 지금 궁금한 순간에 해당하는 메서드만 정의하세요.
  • 민감한 정보에 주의 — 프롬프트나 입력에 개인정보가 담길 수 있으니, 로그로 남길 때 무엇을 기록할지 신경 쓰는 게 좋습니다.
  • 오류 이벤트를 꼭 챙겨라 — on_chain_error 같은 오류 콜백을 남겨 두면, 실패 원인을 나중에 되짚기가 훨씬 수월합니다.

❓ 자주 묻는 질문 (FAQ)

Q. 콜백을 쓰면 실행이 많이 느려지나요?
A. 콜백 안에서 하는 일이 가벼우면(예: 짧은 로그 출력) 부담은 크지 않습니다. 다만 콜백 안에서 무거운 작업을 하면 그만큼 전체 실행에 영향을 줄 수 있어요.

Q. 콜백과 로깅은 같은 말인가요?
A. 아닙니다. 콜백은 특정 순간에 코드를 불러 주는 장치이고, 로깅은 정보를 기록으로 남기는 일입니다. 흔히 콜백 안에서 로깅을 하기 때문에 함께 묶여 설명될 뿐입니다.

Q. 콜백은 디버깅에만 쓰나요?
A. 디버깅이 대표적이지만, 실행 흐름을 모니터링하거나 진행 상황을 화면에 실시간 표시하는 등 다양한 용도로 활용할 수 있습니다.


마무리

정리하면, 콜백은 LangChain 실행 중간의 특정 순간마다 내가 지정한 코드를 자동으로 불러 주는 장치이고, 그 안에서 정보를 남기면 곧 로깅이 됩니다. 이 둘을 함께 쓰면 겉으로만 보이던 체인의 내부를 훤히 들여다보며 디버깅하고 모니터링할 수 있어요. 다음 편에서는 여러 도구를 스스로 골라 쓰는 에이전트(Agent)와 툴(Tool) 이야기로 이어가 보겠습니다.

댓글

이 블로그의 인기 게시물

지하철 노선도로 이해하는 LangGraph (분기·반복 설계)

LangGraph State란 무엇인가? 상태 관리 개념 쉽게 이해하기

LangGraph Node와 Edge 개념 쉽게 이해하기 (초보자 완전 정리)