이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • LangChain과 LlamaIndex 같은 framework에서 백엔드 개발자가 배울 실전 패턴은 무엇인가?
  • framework 추상화를 어디까지 믿고 어디부터 직접 경계를 잡아야 하는가?
  • framework를 도입할 때 버전 변화와 lock-in을 어떻게 다루는가?

개요

2026-06-30 GitHub API로 확인한 기준에서 langchain-ai/langchain은 140k+ stars, run-llama/llama_index는 50k+ stars이고 둘 다 당일 업데이트가 있는 활발한 프로젝트다. 이 정도 생태계는 학습 가치가 크다.

다만 목표는 사용법을 외우는 것이 아니다. 백엔드 개발자가 framework에서 배울 것은 “LLM 앱을 어떤 관심사로 나누는가”다.

배울 패턴

LLM app framework는 보통 다음 관심사를 분리한다.

  • model client
  • prompt template
  • output parser
  • retriever
  • document loader
  • memory
  • tool
  • agent/workflow
  • callback/tracing
  • evaluation

이 분리는 백엔드 설계에도 그대로 유용하다. 다만 framework 객체를 API 계층까지 끌고 올라오면 애플리케이션 경계가 흐려진다.

백엔드 적용 기준

좋은 적용 방식:

  • framework를 adapter 내부에 둔다.
  • 도메인 서비스는 내부 DTO와 포트에 의존한다.
  • prompt version과 model profile은 별도 관리한다.
  • trace id를 framework callback과 연결한다.
  • 핵심 chain은 integration test를 둔다.

나쁜 적용 방식:

  • Controller에서 framework chain을 바로 실행한다.
  • framework exception을 API 응답에 그대로 노출한다.
  • prompt와 retriever 설정이 코드 곳곳에 흩어진다.
  • token usage와 retrieval source를 기록하지 않는다.

코드로 이해하기

framework 사용부를 adapter로 숨기는 구조다.

class RagAnswerPort:
    def answer(self, *, question: str, tenant_id: str) -> dict:
        raise NotImplementedError
 
 
class LlamaIndexRagAdapter(RagAnswerPort):
    def __init__(self, query_engine, usage_recorder):
        self.query_engine = query_engine
        self.usage_recorder = usage_recorder
 
    def answer(self, *, question: str, tenant_id: str) -> dict:
        response = self.query_engine.query(question)
        source_ids = [
            node.metadata["source_id"]
            for node in response.source_nodes
        ]
        self.usage_recorder.record(
            feature="document_qa",
            tenant_id=tenant_id,
            source_ids=source_ids,
        )
        return {
            "answer": str(response),
            "source_ids": source_ids,
        }

프레임워크는 내부 구현이다. API 응답은 내부 계약으로 반환한다.

실전 팁

  • framework 문서의 quickstart는 운영 기준이 아니다.
  • framework callback을 내부 trace와 연결한다.
  • framework 버전 업데이트는 retriever, parser, agent behavior를 바꿀 수 있으므로 eval을 돌린다.
  • lock-in을 완전히 피하려 하기보다 핵심 경계만 포트로 감싼다.

위험 신호!

  • framework 객체가 Controller 응답으로 직접 노출된다.
  • chain 내부에서 어떤 prompt가 쓰이는지 모른다.
  • retrieval source를 추적하지 않는다.
  • framework upgrade 후 eval을 돌리지 않는다.

확인 질문

확인 질문

  • LLM app framework를 백엔드에 도입할 때 가장 중요한 경계는 무엇인가?
    • framework를 adapter 뒤에 두고 서비스 계층은 내부 계약에 의존하게 하는 것이다.
  • framework 추상화가 디버깅을 어렵게 만드는 이유는 무엇인가?
    • prompt, retrieval, tool, model 호출 흐름이 한 체인 안에 숨어 실패 원인을 보기 어려울 수 있기 때문이다.

참고 문서