๐Ÿงฉ LangChain ์™„์ „ ์ดํ•ด โ‘ข โ€” LangChain์˜ ํ•œ๊ณ„์™€ LangGraph๋กœ์˜ ์ „ํ™˜

okorionยท2025๋…„ 10์›” 4์ผ

โ€œChain์€ ํ๋ฆ„์„ ์—ฐ๊ฒฐํ•œ๋‹ค. ํ•˜์ง€๋งŒ ํ๋ฆ„์„ ์ œ์–ดํ•˜๋ ค๋ฉด ๊ทธ๋ž˜ํ”„๊ฐ€ ํ•„์š”ํ•˜๋‹ค.โ€


๐Ÿ’ก 1. ๋“ค์–ด๊ฐ€๋ฉฐ

์•ž์„  ๋‘ ํŽธ์—์„œ ์šฐ๋ฆฌ๋Š” LangChain์„ ํ†ตํ•ด LLM์„ ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜(Orchestration) ํ•˜๊ณ , Chain โ†’ Memory โ†’ Agent ๊ตฌ์กฐ๋กœ ๋ฐœ์ „์‹œ์ผฐ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๋ฉด ์ด๋Ÿฐ ํ•œ๊ณ„๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค ๐Ÿ‘‡

  • ์กฐ๊ฑด ๋ถ„๊ธฐ(if/else)๋‚˜ ๋ฃจํ”„(loop) ํ‘œํ˜„์ด ๋ณต์žกํ•ด์ง
  • ์ƒํƒœ(State)๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ถ”์ ํ•˜๊ธฐ ์–ด๋ ค์›€
  • ์—ฌ๋Ÿฌ Chain๊ณผ Tool์˜ ๋ณ‘๋ ฌ ์‹คํ–‰ ์ œ์–ด ํ•œ๊ณ„
  • ์‹คํ–‰ ํ๋ฆ„์ด ๊ธธ์–ด์ง€๋ฉด ๋””๋ฒ„๊น…์ด ๋ถˆํŽธํ•จ

์ด์ œ LangChain์˜ ๋‚ด๋ถ€ ์—”์ง„ LCEL (LangChain Expression Language) ์„ ํ†ตํ•ด ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๊ณ , LangGraph๊ฐ€ ์ด ๋ฌธ์ œ๋ฅผ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


โš™๏ธ 2. LangChain์˜ ์‹คํ–‰ ๊ตฌ์กฐ โ€“ LCEL

LangChain์€ ๋‚ด๋ถ€์ ์œผ๋กœ LCEL์ด๋ผ๋Š” ํ‘œํ˜„ ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
LCEL์€ Chain, Tool, Agent ๋“ฑ ๋ชจ๋“  ์‹คํ–‰ ๋‹จ์œ„๋ฅผ ํ•˜๋‚˜์˜ โ€œRunnableโ€๋กœ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

LCEL = LangChain Execution Language
์ฆ‰, LLM ์›Œํฌํ”Œ๋กœ์šฐ์˜ โ€œํ‘œํ˜„์‹โ€์„ ์ฝ”๋“œ๋กœ ์ •์˜ํ•˜๋Š” ์–ธ์–ด์ž…๋‹ˆ๋‹ค.


๐Ÿ”น ์˜ˆ์‹œ: LCEL ๊ธฐ๋ณธ ํ๋ฆ„

from langchain_openai import ChatOpenAI
from langchain.schema.runnable import RunnablePassthrough, RunnableLambda, RunnableBranch

llm = ChatOpenAI(model="gpt-4o-mini")

def is_weather_query(x):
    return "๋‚ ์”จ" in x["query"]

# RunnableBranch = ์กฐ๊ฑด ๋ถ„๊ธฐ
branch = RunnableBranch(
    (is_weather_query, llm.bind()),
    (lambda x: True, RunnablePassthrough())
)

result = branch.invoke({"query": "์˜ค๋Š˜ ๋‚ ์”จ ์•Œ๋ ค์ค˜"})
print(result)

๐Ÿ“˜ ๋™์ž‘ ์š”์•ฝ

  1. ์ž…๋ ฅ์ด โ€œ๋‚ ์”จโ€ ๊ด€๋ จ์ด๋ฉด โ†’ LLM ์‹คํ–‰
  2. ์•„๋‹ˆ๋ผ๋ฉด โ†’ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผ

๐Ÿ“ˆ Mermaid ๋‹ค์ด์–ด๊ทธ๋žจ

์ด๋ ‡๊ฒŒ LCEL์„ ํ†ตํ•ด ์กฐ๊ฑด ๋ถ„๊ธฐ ์ •๋„๋Š” ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒโ€ฆ


โš ๏ธ 3. LCEL๋กœ๋Š” ๋ถ€์กฑํ•œ ์ด์œ 

LCEL์€ ๋›ฐ์–ด๋‚œ ์‹คํ–‰ ์—”์ง„์ด์ง€๋งŒ, โ€œ๋ณต์žกํ•œ ์›Œํฌํ”Œ๋กœ์šฐ ์„ค๊ณ„โ€์—๋Š” ํ•œ๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ์„ค๋ช…์‹ค์ œ ์˜ˆ์‹œ
๋ณต์žกํ•œ ๋ถ„๊ธฐ ์ œ์–ด๋ถ„๊ธฐ ์กฐ๊ฑด์ด ๋Š˜์–ด๋‚ ์ˆ˜๋ก ์ฝ”๋“œ ๊ฐ€๋…์„ฑ ํ•˜๋ฝif/else๊ฐ€ ์ค‘์ฒฉ๋จ
์ƒํƒœ ์ถ”์ ์˜ ๋ชจํ˜ธ์„ฑMemory ์™ธ์— ๋ณ„๋„ ์ƒํƒœ(State) ํ‘œํ˜„ ๋ถˆ๊ฐ€๋Œ€ํ™” + ๊ฒ€์ƒ‰ + ํ‰๊ฐ€ ๋“ฑ ๋ณตํ•ฉ ์ƒํƒœ ๊ด€๋ฆฌ ๋ถˆ๊ฐ€
๋ฃจํ”„/๋ฐ˜๋ณต ์ œ์•ฝ์žฌ๊ท€์  ํ˜ธ์ถœ ์™ธ์—” ๋ฃจํ”„ ๊ตฌํ˜„ ์–ด๋ ค์›€ํ”ผ๋“œ๋ฐฑ ๋ฃจํ”„๋‚˜ ์žฌ์‹œ๋„ ๊ตฌ์กฐ ํ•œ๊ณ„
์‹œ๊ฐํ™” ๋ถ€์žฌ์‹คํ–‰ ๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ๋ฅผ ๋ˆˆ์œผ๋กœ ๋ณด๊ธฐ ์–ด๋ ค์›€๋””๋ฒ„๊น… ์‹œ ์–ด๋ ค์›€
๋Œ€ํ˜• ์‹œ์Šคํ…œ ํ™•์žฅ์„ฑ ๋ถ€์กฑ์ฒด์ธ ๊ฐ„ ์—ฐ๊ฒฐ์ด ํ•˜๋“œ์ฝ”๋”ฉ๋จ์œ ์ง€๋ณด์ˆ˜ ๋น„์šฉ ๊ธ‰์ฆ

๐Ÿ” ์˜ˆ์‹œ๋กœ ๋ณด๋Š” ํ•œ๊ณ„

LangChain์œผ๋กœ RAG ์‹œ์Šคํ…œ์„ ๊ตฌ์„ฑํ•œ๋‹ค๊ณ  ํ•ด๋ด…์‹œ๋‹ค.

Query โ†’ Retriever โ†’ LLM โ†’ Evaluator โ†’ (์žฌ๊ฒ€์ƒ‰ or ์ข…๋ฃŒ)

์ด ๊ณผ์ •์„ ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋ฃจํ”„์‹œํ‚ค๋ ค๋ฉด RunnableBranch์™€ if ์กฐ๊ฑด์„ ๋ฐ˜๋ณต ์ •์˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

# ๊ฐ„๋‹จํžˆ ํ‘œํ˜„ํ•˜๋ฉด ์ด๋Ÿฐ ๊ตฌ์กฐ
while True:
    answer = llm.invoke(query)
    score = evaluator.invoke(answer)
    if score > 0.8:
        break
    else:
        query = retriever.invoke(query)

์ด๊ฑด ๊ฒฐ๊ตญ ๋ช…๋ นํ˜• ๋ฃจํ”„์— ๋ถˆ๊ณผํ•˜๋ฉฐ, LangChain์€ ์ด ๋ฃจํ”„๋ฅผ ๊ตฌ์กฐ์ ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ฆ‰, โ€œ์‹คํ–‰ ํ๋ฆ„(Flow)โ€์„ ๋…ผ๋ฆฌ์ ์œผ๋กœ ์„ค๊ณ„ํ•˜๊ณ  ์‹œ๊ฐํ™”ํ•  ๋ฐฉ๋ฒ•์ด ์—†๋‹ค๋Š” ๊ฒƒ์ด์ฃ .


๐Ÿงฉ 4. LangGraph๊ฐ€ ๋“ฑ์žฅํ•œ ์ด์œ 

์ด ํ•œ๊ณ„๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด LangGraph๊ฐ€ ํƒ„์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
LangGraph๋Š” LangChain์˜ LCEL ์—”์ง„ ์œ„์— ๊ตฌ์ถ•๋œ ๊ทธ๋ž˜ํ”„ ๊ธฐ๋ฐ˜ ์›Œํฌํ”Œ๋กœ์šฐ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.

ํ•ญ๋ชฉLangChainLangGraph
ํ•ต์‹ฌ ๋‹จ์œ„Runnable, ChainNode, Edge, StateGraph
ํ๋ฆ„ ๊ตฌ์กฐ์„ ํ˜•(Sequential)๊ทธ๋ž˜ํ”„(Graph)
์ƒํƒœ ๊ด€๋ฆฌMemory ์ค‘์‹ฌReducer ๊ธฐ๋ฐ˜ ๋ช…์‹œ์  State
๋ฃจํ”„/๋ถ„๊ธฐ์ œํ•œ์ Edge ์กฐ๊ฑด์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฌ์šด ํ‘œํ˜„
์‹œ๊ฐํ™”์–ด๋ ค์›€Mermaid, Gradio ๋“ฑ์œผ๋กœ ๊ทธ๋ž˜ํ”„ ์‹œ๊ฐํ™” ๊ฐ€๋Šฅ

๐Ÿ“ˆ ์‹œ๊ฐ์  ๋น„๊ต

LangGraph๋Š” ๋ฐ”๋กœ ์ด โ€œํ๋ฆ„ ์ œ์–ด์˜ ์ถ”์ƒํ™”โ€๋ฅผ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค.


๐Ÿง  5. LangChain โ†’ LangGraph ์ „ํ™˜ ์‚ฌ๊ณ 

์‚ฌ๊ณ  ๋ฐฉ์‹์„ค๋ช…
Chain ์ค‘์‹ฌ ์‚ฌ๊ณ โ€œLLM์„ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ํ•˜์ž.โ€
Graph ์ค‘์‹ฌ ์‚ฌ๊ณ โ€œLLM์ด ์–ด๋А ์ƒํƒœ์—์„œ ์–ด๋–ค ๋…ธ๋“œ๋ฅผ ๊ฑฐ์ณ์•ผ ํ• ๊นŒ?โ€

LangGraph์—์„œ๋Š” ๋” ์ด์ƒ if/else, loop ๊ฐ™์€ ๋ช…๋ นํ˜• ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ์ž‘์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
๋Œ€์‹  Node(๋™์ž‘) ์™€ Edge(์กฐ๊ฑด) ๋กœ ํ๋ฆ„์„ ์„ ์–ธ์ ์œผ๋กœ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.


โš™๏ธ 6. LangGraph์˜ ๊ตฌ์กฐ ๋ฏธ๋ฆฌ ๋ณด๊ธฐ

from langgraph.graph import StateGraph
from langchain_openai import ChatOpenAI

# ์ƒํƒœ ์ •์˜
class GraphState:
    question: str
    answer: str

# ๋…ธ๋“œ ์ •์˜
def ask_llm(state: GraphState):
    llm = ChatOpenAI(model="gpt-4o-mini")
    response = llm.invoke(f"Q: {state.question}\nA:")
    state.answer = response.content
    return state

# ๊ทธ๋ž˜ํ”„ ์ƒ์„ฑ
graph = StateGraph(GraphState)
graph.add_node("ask_llm", ask_llm)
graph.set_entry_point("ask_llm")
graph.set_finish_point("ask_llm")

app = graph.compile()
result = app.invoke({"question": "LangGraph๋ž€ ๋ฌด์—‡์ธ๊ฐ€?"})
print(result["answer"])

์ด์ œ ์‹คํ–‰ ํ๋ฆ„์€ โ€œํ•จ์ˆ˜ ํ˜ธ์ถœโ€์ด ์•„๋‹ˆ๋ผ โ€œ๊ทธ๋ž˜ํ”„ ํƒ์ƒ‰โ€์œผ๋กœ ์ œ์–ด๋ฉ๋‹ˆ๋‹ค.


๐Ÿ” 7. LangGraph๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ•ต์‹ฌ ํ•ด๊ฒฐ์ฑ…

๋ฌธ์ œ ์ƒํ™ฉLangChain ํ•œ๊ณ„LangGraph์˜ ํ•ด๊ฒฐ ๋ฐฉ์‹
๋ณต์žกํ•œ ์กฐ๊ฑด ๋ถ„๊ธฐRunnableBranch ํ•œ์ •Edge ์กฐ๊ฑด์œผ๋กœ ์„ ์–ธ์  ํ‘œํ˜„
๋ฃจํ”„/์žฌ์‹œ๋„์žฌ๊ท€ ์ฝ”๋“œ ํ•„์š”Edge ๋ฃจํ”„ ๊ตฌ์กฐ๋กœ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฐ˜๋ณต
์ƒํƒœ ๊ด€๋ฆฌ๋‹จ์ผ Memory ๊ฐ์ฒดReducer ๊ธฐ๋ฐ˜ State ํŠธ๋ž˜ํ‚น
๋ณ‘๋ ฌ ์‹คํ–‰RunnableParallel๋กœ ์ œํ•œ์—ฌ๋Ÿฌ Node ๋ณ‘๋ ฌ ์‹คํ–‰ ์ง€์›
๋””๋ฒ„๊น…/์‹œ๊ฐํ™”์–ด๋ ค์›€MermaidยทGradio ์‹œ๊ฐํ™” ๊ฐ€๋Šฅ

๐ŸŽฏ 8. ์ •๋ฆฌ โ€” LangGraph๊ฐ€ ํ•„์š”ํ•œ ์ด์œ 

LangChain์ด โ€œ๊ธฐ๋Šฅ ์ค‘์‹ฌโ€์ด๋ผ๋ฉด, LangGraph๋Š” โ€œํ๋ฆ„ ์ค‘์‹ฌโ€์ž…๋‹ˆ๋‹ค.

๊ตฌ๋ถ„LangChainLangGraph
์ฒ ํ•™๊ธฐ๋Šฅ์„ ์กฐํ•ฉํ•œ๋‹คํ๋ฆ„์„ ์„ค๊ณ„ํ•œ๋‹ค
๋‹จ์œ„ChainNode + Edge
์‚ฌ๊ณ ๋ฐฉ์‹์ ˆ์ฐจ์ ์„ ์–ธ์ 
์ดˆ์ Prompt / Tool์ƒํƒœ / ๊ฒฝ๋กœ / ์กฐ๊ฑด
๊ฒฐ๊ณผ๋ฌผLLM ์ฒด์ธLLM ์‹œ์Šคํ…œ

LangChain์€ ์—ฌ์ „ํžˆ LangGraph์˜ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ฌ์šฉ๋˜๋ฉฐ, LangGraph๋Š” ๊ทธ ์œ„์—์„œ LLM ์‹œ์Šคํ…œ ์„ค๊ณ„์˜ ์ฐจ์›์„ ๋Œ์–ด์˜ฌ๋ฆฝ๋‹ˆ๋‹ค.


๐ŸŽ“ 9. ๋” ๊นŠ์ด ๋ฐฐ์šฐ๊ธฐ ์œ„ํ•œ ๊ณ ๊ธ‰ ํ™•์žฅ ํ•™์Šต ๊ฐ€์ด๋“œ

์ฃผ์ œํ•™์Šต ์ด์œ ์ถ”์ฒœ ๋ฐฉํ–ฅ
LCEL ๊ตฌ์กฐLangGraph์˜ ๋Ÿฐํƒ€์ž„ ๊ธฐ๋ฐ˜Runnable, RunnableBranch ์‹ค์Šต
Reducer ํŒจํ„ด์ƒํƒœ ๋ˆ„์  ๋ฐฉ์‹ ์ดํ•ดPython ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ ๋ณต์Šต
Async Flow ์ œ์–ด๋น„๋™๊ธฐ ๋…ธ๋“œ ์ฒ˜๋ฆฌ ํ•™์Šตasyncio, gather ํ™œ์šฉ
Graph Visualization์‹œ๊ฐ์  ๋””๋ฒ„๊น… ์ตํžˆ๊ธฐMermaid, Gradio ์‹ค์Šต
LangServe / MCPLangGraph ๋ฐฐํฌ์™€ ์„œ๋น„์Šคํ™”LangServe ๋ฌธ์„œ ์‹ค์Šต

๐Ÿ“š 10. ๋งˆ๋ฌด๋ฆฌ ์š”์•ฝ

ํ•ญ๋ชฉ์š”์•ฝ
ํ•ต์‹ฌ ๊ฐœ๋…LCEL์€ ์‹คํ–‰ ์—”์ง„, LangGraph๋Š” ๊ตฌ์กฐ ์„ค๊ณ„ ์—”์ง„
ํ•ต์‹ฌ ์ฝ”๋“œRunnableBranch ์˜ˆ์‹œ, StateGraph ์˜ˆ์‹œ
ํ•ต์‹ฌ ์ธ์‚ฌ์ดํŠธโ€œChain์˜ ํ•œ๊ณ„๋ฅผ ๋„˜์€ ๊ทธ๋ž˜ํ”„ ๊ธฐ๋ฐ˜ ์ œ์–ดโ€
๋‹ค์Œ ํŽธ ์˜ˆ๊ณ ๐Ÿš€ LangGraph ์™„์ „ ์ •๋ณต โ‘  โ€” LangGraph๋ž€ ๋ฌด์—‡์ธ๊ฐ€

๐Ÿงญ ํ•œ ์ค„ ์š”์•ฝ

LangChain์€ โ€œLLM์„ ์‹คํ–‰ํ•˜๋Š” ๋ฒ•โ€์„ ์•Œ๋ ค์ค€๋‹ค.
LangGraph๋Š” โ€œLLM์ด ์–ด๋–ป๊ฒŒ ์‚ฌ๊ณ ํ•˜๊ณ  ํ๋ฆ„์„ ์ œ์–ดํ• ์ง€โ€๋ฅผ ์„ค๊ณ„ํ•˜๊ฒŒ ํ•œ๋‹ค.

profile
okorion's Tech Study Blog.

0๊ฐœ์˜ ๋Œ“๊ธ€