
LangGraph 설명서들의 문제점
내가 본 대부분의 튜토리얼은 LangGraph를 어떻게 사용하는지는 설명한다.
하지만 그것이 왜 존재하는지를 설명하는 것은 거의 없다.
그래서 우리 중 많은 사람들이 코드를 외우려고 하고, 노드와 엣지를 복사 붙여넣기 하고, 2주 후에 전부 잊어버린다.
이 글은 그것들을 고치고자 한다.
끝까지 읽으면:
- LangGraph가 실제로 무엇인지 이해한다
- LangChain이 이미 존재하는데도 왜 LangGraph가 존재하는지 안다
- 프롬프트가 아닌 그래프로 생각하는 법을 익힌다
- LangGraph를 머릿속에 영구적으로 각인시킬 미니 프로젝트를 만든다
처음부터 시작해보자.
LangGraph는 어떤 문제를 해결하는가?
일반적인 LLM 앱을 보자.
이렇게 생겼다.
사용자 → 프롬프트 → LLM → 답변
간단한 챗봇에는 충분하다. 하지만 실제 앱은 지저분하다.
실제 AI 앱은 이렇게 생겼다:
- 질문을 한다
- 무엇을 할지 결정한다
- 아마도 검색한다
- 아마도 추가 질문을 한다
- 아마도 확신이 생길 때까지 반복한다
- 아마도 다음번에는 다르게 분기한다
예시:
"질문이 불명확하면 → 명확화 요청
질문에 데이터가 필요하면 → 검색
답변이 좋으면 → 종료
아니면 → 답변을 개선하고 다시 시도"
이것은 직선이 아니다. 이것은 논리 + 결정 + 반복이다.
💡 초보자 설명: 일반 프로그래밍으로 이런 복잡한 흐름을 만들면 if-else가 수십 개 중첩되고, 루프가 뒤엉켜서 관리하기 어려워진다. LangGraph는 이런 복잡한 흐름을 그래프라는 시각적이고 직관적인 구조로 표현할 수 있게 해준다.
바로 여기서 LangGraph의 존재 이유가 있다.
한 줄 정의 (이것만 기억하면 된다)
LangGraph는 LLM 워크플로우를 선형 체인 대신 그래프로 구축할 수 있게 해주는 프레임워크다.
그게 전부다. 이것보다 더 나은 정의는 없을 것 같다.
Chain vs Graph (핵심 멘탈 모델)
LangChain (체인 방식)
1단계 → 2단계 → 3단계 → 완료
- 고정된 순서
- 반복이나 분기 없음
- 상태 로직 메모리 없음
LangGraph (그래프 방식)
┌─> 툴 사용
시작 ────┤
└─> 다시 질문 ──┐
↓
개선 → 종료
- 반복하거나 다른 분기를 가질 수 있음
- 다음 단계를 결정할 수 있음
- 조건부로 종료할 수 있음
💡 초보자 비유: LangChain이 컨베이어 벨트(항상 같은 순서로 진행)라면, LangGraph는 신호등이 있는 도로 교통 시스템이다. 상황에 따라 직진할 수도, 좌회전할 수도, 유턴할 수도 있다.
LangChain이 파이프라인이라면, LangGraph는 지능이 있는 순서도(flowchart)다.
핵심 개념: 상태 (State)
이것이 가장 중요한 개념이다.
상태란 무엇인가?
상태(State) = 단계들 사이에 전달되는 공유 메모리
예시 상태:
{
"question": "RAG가 무엇인가요?",
"answer": "",
"confidence": 0.4,
"tries": 1
}
모든 단계는:
- 상태를 읽는다
- 상태를 업데이트한다
- 다음 단계로 전달한다
💡 초보자 설명: 상태는 마치 사무실에서 돌아다니는 클립보드와 같다. 각 직원(노드)은 클립보드를 받아서 내용을 확인하고, 자기 작업 결과를 적고, 다음 직원에게 넘긴다. LangGraph가 이 클립보드를 자동으로 관리해준다.
- 👉 변수를 수동으로 전달하지 않는다
- 👉 LangGraph가 상태 흐름을 관리한다
LangGraph에서 기억해야 할 5가지
1️⃣ 상태 (State) — 두뇌
데이터가 어떻게 흐르는지 정의하는 Python 클래스다.
from typing import TypedDict
class GraphState(TypedDict):
question: str # 사용자 질문
answer: str # 생성된 답변
tries: int # 시도 횟수
💡 초보자 설명: TypedDict는 딕셔너리인데 각 키의 타입을 미리 지정한 것이다. str은 문자열, int는 정수를 의미한다. 이렇게 하면 어떤 데이터가 흐르는지 명확하게 알 수 있고, 오타로 인한 버그를 방지할 수 있다.
생각해볼 질문: "모든 단계가 어떤 정보를 봐야 하는가?"
2️⃣ 노드 (Nodes) — 일꾼
노드는 그냥 함수다.
def generate_answer(state):
# state에서 필요한 정보를 읽고
# 작업을 수행한 후
# 변경된 부분만 반환한다
return {
"answer": "여기에 LLM 출력",
"tries": state["tries"] + 1 # 시도 횟수를 1 증가
}
중요하게 기억할 것:
- 입력 = 현재 상태 전체
- 출력 = 변경된 상태의 일부 (전부 반환할 필요 없음)
💡 초보자 설명: 함수가 상태 전체를 반환하지 않아도 된다. 변경한 키만 반환하면 LangGraph가 나머지는 그대로 유지한다. 마치 문서에서 수정한 부분만 표시하는 것과 같다.
3️⃣ 엣지 (Edges) — 경로
엣지는 다음에 어디로 갈지 결정한다.
graph.add_edge("generate", "evaluate")
# "generate" 노드가 끝나면 "evaluate" 노드로 간다
의미:
"generate 이후에 evaluate로 가라"
💡 초보자 설명: 엣지는 순서도의 화살표다. A → B로 가는 화살표를 그리는 것과 같다. 조건 없이 항상 같은 방향으로 이동한다.
4️⃣ 조건부 엣지 (Conditional Edges) — 의사결정자
이것이 LangGraph가 강력해지는 지점이다.
def should_continue(state):
if state["tries"] >= 2:
return "end" # 2번 시도했으면 종료
return "generate" # 아직이면 다시 생성
이제 그래프가 생각할 수 있다. 조건에 따라 다음 단계를 결정한다.
💡 초보자 설명: 일반 엣지가 "항상 A→B"라면, 조건부 엣지는 "상황에 따라 A→B 또는 A→C"다. 이 함수가 반환하는 문자열이 다음에 이동할 노드의 이름이 된다. 이것이 LangGraph를 단순한 파이프라인과 다르게 만드는 핵심이다.
5️⃣ 진입점 & END — 시작과 끝
graph.set_entry_point("generate") # 어디서 시작할지 지정
END의 의미:
"실행을 멈춰라"
💡 초보자 설명: END는 LangGraph에서 제공하는 특별한 상수다. 조건부 엣지에서 END를 반환하면 그래프 실행이 완전히 종료된다. 무한 루프를 방지하는 안전장치 역할도 한다.
LangGraph가 실제로 실행되는 방법 (중요한 직관)
LangGraph는 내부적으로 이렇게 동작한다.
진입 노드에서 시작
↓
상태를 노드에 전달
↓
상태 업데이트
↓
다음 엣지 결정
↓
반복
↓
END에서 종료
💡 초보자 설명: 이것은 **상태 기계(state machine)**의 개념이다. 현재 상태를 보고, 다음 행동을 결정하고, 상태를 업데이트하는 과정을 반복한다. 자판기를 생각해보자. "동전 투입 → 상품 선택 → 상품 제공 → 거스름돈 반환" 같은 흐름이 상태 기계다.
이것은 말 그대로 LLM을 위한 상태 기계다.
미니 프로젝트: 자기 개선 답변 봇 (매우 간단)
이 프로젝트 하나가 LangGraph를 완전히 이해하게 만들어줄 것이다.
목표
- 답변을 생성한다
- 충분히 "좋은지" 확인한다
- 아니라면 → 개선한다
- 2번 시도 후 종료한다
💡 전체 흐름 미리보기:
시작 ↓ generate (답변 생성) ↓ decide (판단) ├── tries < 2 → generate로 돌아가서 다시 시도 └── tries >= 2 → END (종료)
Step 1: 상태 정의
from typing import TypedDict
class GraphState(TypedDict):
question: str # 사용자 질문
answer: str # 현재 생성된 답변
tries: int # 지금까지 시도한 횟수
Step 2: 답변 생성 노드
def generate(state):
# 실제로는 여기서 LLM API를 호출한다
# 지금은 간단하게 텍스트로 시뮬레이션
answer = f"시도 {state['tries']}: 이것은 간단한 답변입니다."
return {
"answer": answer,
"tries": state["tries"] + 1 # 시도 횟수 1 증가
}
# question은 변경하지 않으므로 반환할 필요 없음
# LangGraph가 기존 값을 그대로 유지한다
Step 3: 계속할지 결정하는 함수
def decide(state):
if state["tries"] >= 2:
return "end" # 2번 이상 시도했으면 종료
return "generate" # 아직이면 다시 생성 노드로
💡 초보자 설명: 이 함수는 노드가 아니다. 조건부 엣지에 사용되는 라우팅 함수다. 반환하는 문자열이 다음에 이동할 노드를 결정한다. "end"는 아래 Step 4에서 END에 매핑된다.
Step 4: 그래프 구성하기
from langgraph.graph import StateGraph, END
# 1. GraphState를 사용하는 그래프 생성
graph = StateGraph(GraphState)
# 2. 노드 등록: 이름과 실행할 함수를 연결
graph.add_node("generate", generate)
# 3. 진입점 설정: 어디서 시작할지
graph.set_entry_point("generate")
# 4. 조건부 엣지 추가
graph.add_conditional_edges(
"generate", # 어떤 노드에서 출발하는가
decide, # 어떤 함수로 결정하는가
{
"generate": "generate", # decide가 "generate"를 반환하면 → generate 노드로
"end": END # decide가 "end"를 반환하면 → 종료
}
)
# 5. 그래프 컴파일 (실행 가능한 앱으로 변환)
app = graph.compile()
💡 초보자 설명: add_conditional_edges의 마지막 딕셔너리는 "매핑표"다. decide 함수가 반환하는 문자열을 키로 받아서, 실제로 이동할 노드(또는 END)로 변환해준다.
Step 5: 실행하기
# invoke: 그래프를 실행하는 메서드
# 초기 상태를 딕셔너리로 전달한다
result = app.invoke({
"question": "LangGraph가 무엇인가요?",
"answer": "", # 처음엔 빈 답변
"tries": 0 # 시도 횟수 0부터 시작
})
print(result)
# 출력 예시:
# {
# "question": "LangGraph가 무엇인가요?",
# "answer": "시도 2: 이것은 간단한 답변입니다.",
# "tries": 2
# }
방금 무슨 일이 일어났는가 (중요)
1. 그래프가 generate에서 시작
↓
2. 상태 업데이트 (tries: 0 → 1, answer 생성)
↓
3. decide가 tries=1 확인 → "generate" 반환
↓
4. 다시 generate 실행
↓
5. 상태 업데이트 (tries: 1 → 2, answer 개선)
↓
6. decide가 tries=2 확인 → "end" 반환
↓
7. 자동으로 종료
- 루프를 직접 작성하지 않았다
- 복잡한 if-else도 없었다
- 그래프가 논리를 처리했다
💡 핵심 깨달음: 직접 for i in range(2)나 while tries < 2 같은 루프를 쓰지 않았다. 조건부 엣지가 그 역할을 대신했다. 코드가 더 읽기 쉽고, 각 단계가 명확하게 분리되어 있다.
LangGraph가 GenAI 엔지니어들에게 사랑받는 이유
LangGraph는 다음이 필요할 때 사용된다.
| 다단계 추론 | 여러 번의 생각과 검증이 필요한 복잡한 질문 처리 |
| 툴 + LLM 조율 | 검색, 계산기, API 등 외부 툴과 LLM을 함께 사용 |
| 에이전트 루프 | AI가 스스로 판단하며 반복적으로 작업을 수행 |
| 재시도 로직 | 답변 품질이 낮으면 자동으로 다시 시도 |
| 결정 기반 워크플로우 | 상황에 따라 다른 경로로 분기하는 복잡한 흐름 |
💡 언제 LangGraph를 쓰고 언제 쓰지 않는가?
쓰는 경우:
- AI가 스스로 판단하고 반복해야 할 때
- 여러 툴을 조건에 따라 다르게 사용해야 할 때
- 복잡한 다단계 워크플로우가 필요할 때
쓰지 않아도 되는 경우:
- 단순한 질문→답변 챗봇
- 항상 같은 순서로 처리하는 파이프라인
- LangChain만으로 충분한 경우
이것이 데모가 아닌 프로덕션 AI다.
마무리: 핵심 요약
💡 5가지만 기억한다:
- State = 모든 단계가 공유하는 데이터 (클립보드)
- Node = 상태를 받아서 업데이트하는 함수 (일꾼)
- Edge = 항상 같은 방향으로 이동하는 경로 (고정 화살표)
- Conditional Edge = 조건에 따라 다르게 이동 (신호등)
- Entry Point & END = 시작과 끝 (출발점과 목적지)
이 5가지로 어떤 복잡한 AI 워크플로우도 표현할 수 있다.
이제 LangGraph에 대해서 더 이상 검색할 필요가 없다. 이제 직접 만들어 보면 된다.
'최신 AI' 카테고리의 다른 글
| 천만 건 문서의 RAG 파이프라인 설계 방법 (할루시네이션 없이) (0) | 2026.06.01 |
|---|---|
| 에이전틱 AI 입문 가이드: 제로에서 첫 번째 AI 에이전트까지 (0) | 2026.05.18 |
| 개발자가 알아야 할 AI 개념들 (1) | 2026.05.15 |
| AI 엔지니어라면 반드시 이해해야 할 9가지 RAG 아키텍처 (0) | 2026.05.14 |
| RAG 청킹 제대로 하기 (0) | 2026.05.11 |