2. LangGraph 기본기
두 곳에서 같은 값을 고치면
이 차시를 마치면
여러 곳에서 같은 값을 고칠 때 Reducer 로 합치는 규칙을 정합니다.
두 Node 가 같은 값을 동시에 고치면 어느 쪽이 남을까요. LangGraph 는 이 질문에 기본값을 주지 않습니다. 만드는 사람이 정해야 합니다.
Reducer 란 무엇인가
개념. Reducer 는 여러 갱신값이 같은 State 키에 들어올 때 덮어쓸지, 이어 붙일지, 합칠지를 정하는 규칙입니다.
왜 필요한가. 병렬 Node 가 같은 키를 갱신하거나, 메시지를 계속 쌓아야 할 때 필요합니다. 여기서 순서가 다르면 결과도 다릅니다. 이 차이를 모르고 설계하면 나중에 원인을 못 찾습니다.
| 같은 키를 고치는 방식 | Reducer 가 없으면 |
|---|---|
| 순서대로 — 앞 Node 다음에 뒤 Node | 나중 값이 앞의 값을 덮어씁니다 |
| 동시에 — 두 Node 가 나란히 | 덮어쓰지 않고 오류로 멈춥니다 |
동시에 고칠 때 나는 오류입니다.
InvalidUpdateError: At key 'value': Can receive only one value per step.
Use an Annotated key to handle multiple values.
위 표와 오류 문구는 2026-08-15 에 langgraph 1.2.11 로 직접 돌려 확인한 것입니다. 같은 키를 두 Node 가 나란히 쓰는 그래프와 순서대로 쓰는 그래프를 각각 실행해 비교했습니다.
역할. 학습팩은 "State key = 변수"가 아니라 "State key + update semantics"까지 함께 설계해야 한다고 봅니다. 즉 이름과 타입만 정하는 것으로는 부족하고, 어떻게 갱신되는지까지 정해야 합니다.
비유
공동 문서를 여럿이 고친다고 해 봅시다. 한 사람씩 차례로 고치면 마지막 사람의 내용만 남습니다. 그런데 둘이 같은 칸을 동시에 고치면 문서가 "누구 것을 남길지 모르겠다"며 저장을 거부합니다. Reducer 는 "이 칸은 덮어쓰고, 저 칸은 아래에 이어 붙인다"고 미리 정해 둔 합치기 규칙입니다. 규칙을 정해 두면 거부당하지 않습니다.
코드로 보기
from typing import Annotated
import operator
from typing_extensions import TypedDict
class ResearchState(TypedDict):
# 덮어쓰기 — 마지막 값만 남는다
query: str
answer: str
retry_count: int
# 이어 붙이기 — 여러 노드가 함께 쌓는다
documents: Annotated[list[str], operator.add]
documents 에 붙은 operator.add 가 Reducer 입니다. 두 Node 가 각각 문서를 찾아 오면 두 결과가 모두 남습니다. 이 자리에서 Annotated 를 떼면 두 결과가 한꺼번에 도착해 오류로 멈춥니다.
State 설계 질문 다섯 가지
새 키를 추가할 때마다 아래를 물어봅니다.
- 이 값은 누가 만드는가?
- 누가 읽는가?
- 덮어쓰기인가 이어 붙이기인가?
- Checkpoint 후 복구되어야 하는가?
- Thread 를 넘어 유지되어야 하는가?
5번이 "그렇다"면 State 가 아니라 Store 에 두어야 합니다. 다음 차시에서 다룹니다.
State 를 담는 세 가지 방법
| 방법 | 특징 |
|---|---|
TypedDict | 가장 가볍습니다. 실행 시점 검사는 하지 않습니다 |
Pydantic BaseModel | 값 검사를 합니다. 잘못된 값이 들어오면 그 자리에서 막힙니다 |
dataclass | 표준 라이브러리만으로 구조를 잡습니다 |
셋 중 무엇을 쓸지는 바꿔도 되는 선택입니다. 다만 바꿀 때는 그 State 를 읽고 쓰는 모든 곳을 함께 고쳐야 합니다.
주의할 점
Reducer 가 붙은 필드를 이해하지 않고 지우면 그 키를 동시에 쓰던 자리에서 실행이 오류로 멈춥니다. 조용히 사라지는 것이 아니라 눈에 띄게 깨집니다. 다만 동시에 쓰는 경로를 실제로 지나는 입력이라야 드러납니다. 그 경로를 타지 않는 입력으로만 시험하면 통과합니다. 병렬 경로를 지나는 입력으로 시험해야 하는 이유가 이것입니다.
이미 Checkpoint 에 저장된 State 가 있는 운영 시스템에서는 schema 변경을 신중히 해야 합니다. 기존 Checkpoint 와 호환되지 않으면 재개가 깨질 수 있습니다.