DEVELOPMENT

LangChain 써야 하나요? — 에이전트 자동화를 밑바닥부터 이해하기

에이전트 루프, MCP, LangGraph까지. '모델을 쓰는 쪽' 개발자가 알아야 할 딱 그만큼의 지도.

T
Taewoong2026.07.23 · 11분 읽기

왜 LangChain을 공부하게 됐나

요즘 LangChain이라는 이름을 자주 듣습니다. "이건 쓸 줄 알아야 한다"는 이야기도 있고, 관련 교육 과정도 눈에 띕니다. 어떤 회사의 구인 공고에서는 아예 LangChain 사용 경험을 역량 항목에 넣어두기도 합니다. 그러다 보니 자연스럽게 이런 생각이 들었습니다.

"에이전트 자동화를 하려면, 나도 이걸 꼭 알아야 하는 건가?"

그래서 직접 파봤습니다. 결론부터 말하면 LangChain이 필수는 아니었습니다. 여러 선택지 중 하나일 뿐이고, 정작 중요한 건 그 아래에 깔린 "에이전트가 대체 무엇인지"였습니다. 이 글에서는 제가 그 질문에서 출발해 밑바닥부터 감을 잡은 과정을 순서대로 정리해 보겠습니다.

1. 에이전트 = LLM + 툴 + 루프

LLM 하나만 놓고 보면 할 수 있는 건 텍스트를 받아 텍스트를 내보내는 것 딱 하나입니다. 날씨를 확인하거나 파일을 읽을 수도 없고, API를 직접 호출할 수도 없습니다.

여기서 툴(tool) 이 등장합니다. 모델에게 "이런 함수를 쓸 수 있어"라고 알려주면, 모델은 직접 답하는 대신 "getWeather('서울')을 호출하고 싶어"라고 말할 수 있습니다. 그런데 처음 접할 때 놓치기 쉬운 핵심이 하나 있습니다.

모델은 함수를 직접 실행하지 않습니다. 그저 "호출하고 싶다"고 말할 뿐입니다. 실제로 함수를 실행하고, 그 결과를 다시 모델에게 넘기는 일은 내 코드가 해야 합니다.

그리고 이 과정을 반복(loop) 으로 감싼 것이 바로 에이전트입니다.

let messages = [{ role: "user", content: "서울 날씨 알려줘" }]
 
while (true) {
  const res = await model.call({ messages, tools: [getWeather] })
 
  if (res.type === "text") return res.text          // 끝
 
  const result = await getWeather(res.toolInput)     // 실행은 내 코드가
  messages.push(res, { role: "tool", content: result }) // 결과 되먹임 → 반복
}

이 루프가 에이전트 자동화의 핵심입니다. LangChain이든 LangGraph든, 결국 이 루프를 얼마나 대신 감싸주느냐의 차이일 뿐입니다.

2. 추상화 사다리 — 누가 뭘 쓰는가

모든 도구는 "얼마나 대신 해주느냐 ↔ 얼마나 직접 통제할 수 있느냐"라는 하나의 축 위에 놓여 있습니다.

▲ 직접 제어원본 HTTP API — 루프를 전부 직접공식 SDK (Anthropic · OpenAI)에이전트 SDK (Vercel AI SDK)프레임워크 (LangChain)그래프 오케스트레이션 (LangGraph)▼ 자동화
  • 공식 SDK — HTTP를 감싼 얇은 껍데기입니다. 인증·재시도·타입은 처리해 주지만, 루프는 직접 작성해야 합니다. 동작 원리를 세밀하게 통제하고 싶을 때 잘 맞습니다.
  • 에이전트 SDK (Vercel AI SDK) — 직접 작성해야 할 루프가 사라집니다. generateText({ tools, maxSteps }) 한 줄이면 툴 호출 루프를 대신 실행해 주고, UI 스트리밍까지 처리할 수 있습니다. 프론트엔드에서 AI 기능을 붙일 때 가장 자연스러운 선택입니다.
  • LangChain — 메모리, RAG, 수십 가지 연동까지 미리 준비된 부품 상자입니다. 빠르게 조립할 수 있지만 추상화가 두꺼워 "내부가 잘 보이지 않는다"는 불만도 자주 나옵니다.
  • LangGraph — 흐름을 그래프(상태 기계) 로 직접 그리는 방식입니다. 분기·재시도·사람의 승인이 섞인 복잡한 에이전트에 강합니다.

3. 워크플로우 vs 에이전트 — 순서를 누가 정하는가

여기서 제가 크게 헷갈렸던 지점이 있습니다. "스크랩 → 차트 → 분석 → LLM 종합"처럼 순서를 직접 고정했다면, 사실 에이전트보다는 워크플로우(파이프라인) 에 가깝습니다. 둘의 차이는 딱 하나입니다.

  • 워크플로우 — 코드로 실행 순서를 정해 둡니다. 여기서 LLM은 여러 단계 중 하나일 뿐입니다.
  • 에이전트 — LLM에게 툴과 목표를 주면, 모델이 그때그때 판단해 어떤 툴을 어떤 순서로 사용할지 스스로 정합니다.

그래서 결론은 다음과 같습니다.

순서가 매번 똑같다면 → 그냥 워크플로우(스크립트)가 낫습니다. 에이전트도 LangChain도 필요하지 않습니다. 상황을 보고 "다음에 무엇을 할지"를 매번 다르게 판단해야 한다면 → 그때 에이전트가 필요합니다.

에이전트 프레임워크부터 설치했는데, 정작 만든 것이 고정 순서의 파이프라인이라 프레임워크가 짐만 되는 경우도 정말 많습니다. 그래서 "이 작업의 순서는 고정인가, 아니면 모델이 판단해야 하는가?" 를 항상 먼저 물어보는 것이 좋습니다.

4. MCP는 경쟁자가 아니라 다른 층입니다

MCP를 다뤄본 분이라면 "그럼 MCP와는 무엇이 다르지?"라는 질문이 자연스럽게 나올 수 있습니다. 여기서 알아둘 핵심은 다음과 같습니다.

MCP는 LangChain·SDK와 경쟁하는 도구가 아닙니다. 서로 역할이 다른 층에 있습니다.

  • 프레임워크 / SDK = 지휘자입니다. 에이전트의 루프 자체를 담당합니다.
  • MCP = 툴을 연결하는 표준 규격입니다. 지휘자가 툴을 찾고 호출하는 방식을 표준화한 것으로, USB-C와 비슷한 역할입니다.

앞에서 살펴본 루프의 "도구 실행" 부분을 떠올려 보겠습니다. MCP가 없다면 그 자리에서 직접 작성한 로컬 함수를 호출하고, MCP가 있다면 MCP 서버로 호출을 보냅니다. 어느 쪽이든 루프는 그대로입니다. 그래서 MCP 없이도 에이전트를 만들 수 있고, 직접 에이전트를 만들지 않아도 MCP를 사용할 수 있습니다. Claude Desktop 자체가 이미 에이전트이기 때문입니다. 즉, 둘은 완전히 다른 축에 있습니다.

5. 두 개의 자리 — 쓰는 사람과 만드는 사람

"그럼 이건 누가 사용하는 걸까?"라는 점도 한동안 헷갈렸습니다. 알고 보니 역할이 두 가지여서 그랬습니다.

  • 자리 ① — 이미 만들어진 에이전트를 사용하는 사람입니다. Claude Desktop·ChatGPT는 그 자체가 에이전트입니다. 루프가 내장되어 있어 코드 한 줄 없이 MCP 서버만 연결해 사용할 수 있습니다. 꼭 개발자일 필요도 없습니다.
  • 자리 ② — 자기 제품 안에 에이전트를 만드는 사람입니다. 자신의 앱·UI·툴을 활용해 직접 에이전트를 만듭니다. 이때 SDK·LangGraph가 등장하며, 이 부분이 개발자의 역할입니다.

챗봇 서비스를 예로 들면 구조가 조금 더 분명해집니다. 서비스는 프론트엔드(챗 UI)백엔드로 나뉩니다. 백엔드에서는 에이전트 루프(LangGraph 등)가 실행되며 LLM API를 호출하고 툴을 사용합니다. 이 전체 흐름을 API로 프론트엔드와 연결합니다. 그리고 툴 자리에는 직접 만든 MCP 서버를 그대로 연결할 수 있습니다.

6. 코드로 보는 차이 — 직접 루프 vs LangGraph

같은 에이전트를 두 가지 방식으로 작성해 보면, 프레임워크가 무엇을 대신해 주는지 확실히 드러납니다. 먼저 루프를 직접 작성하는 방식입니다.

def run_agent_manual(user_msg: str) -> str:
    messages = [{"role": "user", "content": user_msg}]
    while True:                              # ← 이 루프가 에이전트의 심장
        res = client.chat.completions.create(
            model="gpt-4o", messages=messages, tools=TOOL_SCHEMA)
        msg = res.choices[0].message
        messages.append(msg)
        if not msg.tool_calls:               # 툴 안 부르면 → 최종 답변
            return msg.content
        for call in msg.tool_calls:          # 부른 툴을 내 코드가 실행
            result = TOOLS[call.function.name](**json.loads(call.function.arguments))
            messages.append({"role": "tool", "tool_call_id": call.id, "content": result})

같은 작업을 LangGraph로 작성하면 다음과 같습니다.

from langgraph.prebuilt import create_react_agent
 
agent = create_react_agent(model, tools=[scrape_news, fetch_chart, analyze])
result = agent.invoke({"messages": [("user", "오늘 삼성전자 장 예측해줘")]})

while 루프, 툴 디스패치, messages 관리가 통째로 사라졌습니다. 이것이 프레임워크가 대신 처리해 준다는 말의 실체입니다. 다만 루프가 눈앞에서 사라진 만큼, "왜 여기서 멈췄지?"를 디버깅할 때 내부 흐름을 파악하기 어려울 수 있습니다. 그래서 처음에는 루프를 직접 한 번 작성해 보는 것을 추천합니다.

LangGraph의 진짜 가치는 create_react_agent 같은 기성 기능보다, 흐름이 복잡해졌을 때 노드와 엣지로 그래프를 직접 그릴 수 있다는 점에 있습니다.

builder.add_node("call_model", call_model)
builder.add_node("run_tools", run_tools)
builder.add_node("human_review", human_review)     # 사람 승인 노드
builder.add_conditional_edges("call_model", route) # 조건 분기
builder.add_edge("run_tools", "call_model")        # 툴 결과 → 다시 모델 (루프)

노드는 각 단계를, 엣지는 단계 사이의 흐름을 나타냅니다. 이 그래프를 직접 설계하는 것이 LangGraph의 핵심입니다.

마무리 — 그래서 LangChain, 알아야 할까

처음의 질문("나도 꼭 알아야 하나?")으로 돌아오면, 이제 답은 분명합니다. LangChain·LangGraph는 "반드시 배워야 하는 필수 기술"이라기보다, 에이전트를 만드는 여러 방법 중 하나입니다. 이번에 직접 살펴보며 얻은 가장 큰 소득도 프레임워크 사용법 자체는 아니었습니다. 그보다 에이전트가 어떤 원리로 동작하는지 이해하게 된 것이 더 중요했습니다.

LLM에 툴을 쥐여주고 루프를 실행한다는 것, 그 루프를 직접 작성할 수도 있고 LangGraph 같은 프레임워크에 맡길 수도 있다는 것, 그리고 그 선택은 "흐름이 얼마나 복잡한가"에 달려 있다는 것을 알게 됐습니다. 이 감을 잡고 나니 프레임워크도 목적에 따라 고르는 하나의 도구로 보였습니다.

여기서 한 발 더 나아갈 방향도 보입니다. 이 에이전트의 "툴" 자리에 MCP 서버를 연결하면, 직접 만든 도구를 표준 규격으로 붙여 얼마든지 확장할 수 있습니다. 한 번 만든 MCP 서버는 커스텀 에이전트뿐 아니라 Claude Desktop 같은 기성 에이전트에서도 그대로 재사용할 수 있습니다. 다음에는 백엔드에 MCP를 실제로 연결해 보는 것도 좋은 경험이 될 것 같습니다.

결국 이 모든 것은 "LLM + 툴 + 루프"라는 하나의 아이디어 위에 놓여 있었습니다. 원리를 이해하고 만드는 것과 정해진 방법만 따라 만드는 것은 결과가 비슷해 보여도 분명한 차이가 있습니다. 그래서 특정 프레임워크를 외우기 전에, 그 아래에서 어떤 일이 벌어지는지 먼저 이해해 보는 것이 중요하다고 생각합니다.

#AI#LLM#Agent#MCP#LangGraph
T
Taewoong소개

프론트엔드 개발자이자 여행·기록·경제에 관심이 많은 사람. 직접 겪은 것만 씁니다.

함께 읽으면 좋은 글