LangGraph 공부 로드맵: Part 1. LangGraph의 이해와 시작 (기초 편)

Part 1 배너

AI 에이전트를 개발할 때, 단순한 일자형 파이프라인(Chain)을 넘어 사용자의 피드백을 받거나 에러 발생 시 이전 단계로 되돌아가는 등의 순환(Loop) 구조가 필수적인 순간이 옵니다.

이번 글에서는 LangChain 생태계의 대표적인 순환 그래프 설계 도구인 LangGraph의 도입 배경부터 핵심 개념, 그리고 간단한 첫 번째 에이전트를 직접 코딩해보는 실습까지 알아보겠습니다.


1. 에이전트 아키텍처의 진화

1.1 Linear Chain(LangChain Express)의 한계

초기의 LLM 애플리케이션은 사용자 입력 -> 프롬프트 템플릿 -> LLM 호출 -> 파싱 -> 출력의 단방향 흐름(Linear Chain)으로 구성되었습니다.
하지만 실제 비즈니스 로직이나 정교한 에이전트를 구축할 때는 다음과 같은 한계에 부딪힙니다.

  • 의사 결정에 따른 분기 불가능: LLM의 응답에 따라 다른 도구를 실행하거나 이전 단계로 분기하기 어렵습니다.
  • 예외 처리 부재: API 호출이 실패하거나 올바르지 않은 형식이 반환되었을 때, 다시 LLM에게 수정 요청을 보내는 루프를 돌릴 수 없습니다.

1.2 왜 순환(Loops/Cycles) 구조가 필요한가?

진정한 에이전트는 바둑을 두거나 코드를 수정하듯이 **”행동(Action) -> 관찰(Observation) -> 재판단(Reasoning)”**의 과정을 반복할 수 있어야 합니다.
비순환 그래프(DAG, Directed Acyclic Graph) 프레임워크로는 이러한 반복 루프(Cycle)를 구현하기 매우 까다롭습니다. LangGraph는 바로 이 순환 루프를 일급 시민(First-class citizen)으로 지원하기 위해 탄생했습니다.


2. LangGraph 핵심 3대 개념

LangGraph의 아키텍처는 크게 State, Nodes, Edges 세 가지로 이루어집니다.

1
2
3
4
5
6
graph TD
START((START)) --> NodeA[Node A: State 읽기 & 쓰기]
NodeA --> Edge{Conditional Edge}
Edge -- 조건 만족 --> NodeB[Node B]
Edge -- 재시도 필요 --> NodeA
NodeB --> END((END))

2.1 State (상태)

그래프의 노드들이 서로 공유하는 전역 데이터베이스입니다.

  • Python의 TypedDictPydantic 클래스로 스키마를 정의합니다.
  • 각 노드는 실행을 마치고 이 State에 새로운 값을 쓰거나 업데이트(Reducer를 통해 기존 메시지에 병합)할 수 있습니다.

2.2 Nodes (노드)

실제 작업을 담당하는 독립적인 함수입니다.

  • 함수는 인자로 현재 State를 전달받고, 수행 결과를 dict 형태로 리턴하여 State를 갱신합니다.
  • 예: LLM에게 질문하는 노드, 검색 API를 호출하는 노드 등.

2.3 Edges (엣지)

노드와 노드 사이의 실행 흐름 경로를 정의합니다.

  • Normal Edge (일반 엣지): A 노드가 끝나면 무조건 B 노드로 이동합니다. (graph.add_edge("A", "B"))
  • Conditional Edge (조건부 엣지): LLM의 판단이나 특정 조건 함수(Router)의 반환 결과에 따라 다음 노드를 결정합니다.

3. Hello LangGraph! 첫 에이전트 구현

가장 단순하면서도 상태 관리가 동작하는 챗봇 그래프를 직접 작성해 보겠습니다.

3.1 필수 라이브러리 설치

1
pip install langgraph langchain-openai python-dotenv

3.2 챗봇 코드 구현 (simple_agent.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import os
from typing import Annotated
from typing_extensions import TypedDict
from dotenv import load_dotenv
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langchain_openai import ChatOpenAI

# .env 파일 로드 (OpenAI 및 Google API 키 등 설정)
load_dotenv()

# 1. State(상태) 정의
class State(TypedDict):
# add_messages는 새로운 메시지 객체를 기존 메시지 리스트에 누적(append)해주는 리듀서입니다.
messages: Annotated[list, add_messages]

# 2. Graph 빌더 생성
graph_builder = StateGraph(State)

# 3. LLM 선언 및 챗봇 노드(Node) 함수 정의
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

def chatbot(state: State):
# 현재 상태에 축적된 메시지들을 전달해 LLM을 호출하고, 결과를 반환합니다.
response = llm.invoke(state["messages"])
return {"messages": [response]}

# 그래프 빌더에 노드 등록
graph_builder.add_node("chatbot", chatbot)

# 4. 흐름(Edge) 정의
# START에서 출발하여 chatbot 노드로 향하고, chatbot 노드가 끝나면 END(종료)로 향합니다.
graph_builder.add_edge(START, "chatbot")
graph_builder.add_edge("chatbot", END)

# 5. 그래프 컴파일
graph = graph_builder.compile()
print("✔ 그래프 컴파일 완료!")

3.3 에이전트 실행 및 대화 나누기

1
2
3
4
5
6
7
8
# 스트리밍 실행 예제
initial_input = {"messages": [("user", "안녕하세요! LangGraph의 핵심 특징 3가지만 알려주세요.")]}

events = graph.stream(initial_input)
for event in events:
for value in event.values():
print("\n[Assistant]")
print(value["messages"][-1].content)

4. 그래프 시각화하기 (Graph Visualization)

작성한 그래프 아키텍처가 의도한 흐름대로 매핑되었는지 시각적으로 확인할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
from IPython.display import Image, display

try:
# Mermaid.js API를 통해 그래프 구조를 PNG 이미지로 생성 및 출력합니다.
png_data = graph.get_graph().draw_mermaid_png()
with open("graph_structure.png", "wb") as f:
f.write(png_data)
print("✔ 그래프 구조를 'graph_structure.png'로 저장했습니다.")
except Exception as e:
print("시각화 도구 실행 실패:", e)

(참고: 로컬 환경에 따라 pygraphviz 혹은 grandalf 라이브러리 추가 설치가 요구될 수 있습니다.)


맺음말

이번 파트에서는 에이전트 개발에서 순환 구조의 필요성과 LangGraph의 기초 빌딩 블록(State, Node, Edge)에 대해 알아보고, 간단한 에이전트를 성공적으로 구동했습니다.

다음 Part 2에서는 외부 API 검색 도구(Tool)를 연동하고, 에이전트가 상황에 따라 도구를 호출하거나 대화를 마칠지 스스로 결정하는 조건부 엣지(Conditional Edges) 적용법을 자세히 알아보겠습니다!

공유하기