-
LangGraph 조건부 라우팅 — 상태 값으로 다음 노드를 결정하는 방법IT 2026. 6. 27. 22:00
LangGraph로 멀티스텝 AI 파이프라인을 만들다 보면 반드시 마주치는 패턴이 있다. 입력이 어떤 종류인지에 따라 다른 처리 경로로 보내는 것 — 이른바 조건부 라우팅(conditional routing)이다. 단순해 보이지만 실제로 구현하다 보면 "라우팅 함수가 정확히 무엇을 해야 하는가", "add_conditional_edges의 세 번째 인자는 언제 필요한가" 같은 질문에서 한 번씩 막힌다. 이 글은 그 지점을 정확히 짚는다.
라우팅 함수의 역할: 오직 "어디로 갈지"만 결정
LangGraph에서 라우팅 함수를 처음 만들 때 가장 흔한 실수는 이 함수 안에서 무언가를 "생성"하려는 것이다. 라우팅 함수의 역할은 단 하나다 — state를 보고 다음에 실행할 노드의 이름을 문자열로 반환하는 것. 응답 생성도, 상태 변경도, 로그 출력도 이 함수의 책임이 아니다.
# Literal로 반환 가능한 노드 이름을 열거 — 오타 시 타입 에러로 즉시 발견 def route_by_category(state: State) -> Literal["recommend_handler", "info_handler", "general_handler"]: if state["category"] == "recommend": return "recommend_handler" elif state["category"] == "info": return "info_handler" else: return "general_handler"반환 타입에
Literal["recommend_handler", "info_handler", "general_handler"]를 명시한 것에 주목하자. 이것은 단순한 문서화가 아니다. 반환 가능한 값을 타입으로 열거해 둠으로써 오타로"recommend_handlerr"를 반환해도 mypy나 pyright가 즉시 경고를 띄운다. 노드 이름을 바꿀 때도 이 Literal이 변경 범위를 코드에서 명시해 준다 — Literal 수정 없이 노드 이름만 바꾸면 타입 에러가 발생하므로 놓칠 수가 없다.전체 그래프 구조
이 다이어그램은 전체 그래프의 흐름을 보여준다. START에서 classifier 노드로 진입하고, classifier가 상태에 category를 기록하면 라우팅 함수가 그 값을 읽어 세 갈래 중 하나로 분기한다. 각 handler 노드는 자신의 전문 영역에 특화된 system prompt로 응답을 생성한 뒤 END로 향한다. classifier는 응답을 만들지 않는다 — 분류만 한다. 이 경계가 흐려지면 그래프 전체의 책임이 뒤섞이기 시작한다.
add_conditional_edges 연결
그래프를 구성하는 코드에서 핵심은
add_conditional_edges의 세 번째 인자다.graph = StateGraph(State) graph.add_node("classifier", classify_node) graph.add_node("recommend_handler", recommend_node) graph.add_node("info_handler", info_node) graph.add_node("general_handler", general_node) graph.add_edge(START, "classifier") graph.add_conditional_edges("classifier", route_by_category, {"recommend_handler": "recommend_handler", "info_handler": "info_handler", "general_handler": "general_handler"}) app = graph.compile()세 번째 인자인 매핑 딕셔너리는 "라우팅 함수가 이 값을 반환하면 저 노드로 가라"는 명시적 연결표다. 위 예시처럼 반환값과 노드 이름이 동일하면 딕셔너리를 생략해도 자동으로 연결되지만, 명시적으로 두는 편이 낫다 — 나중에 노드 이름을 리팩터링할 때 매핑 딕셔너리를 보면 어디를 함께 수정해야 하는지 한눈에 보인다.
tools_condition처럼 LangGraph가 사전 제공하는 라우팅 함수를 쓸 수도 있고, 위처럼 직접 만들 수도 있다.add_conditional_edges 내부 동작
add_conditional_edges가 실제로 하는 일을 단계별로 풀면 이렇다. classifier 노드가 실행을 마치면 LangGraph 런타임이 route_by_category를 호출한다. 이 함수는 현재 state를 받아 노드 이름 문자열을 반환한다. 런타임은 그 반환값을 세 번째 인자로 넘긴 딕셔너리에서 조회해 실제 노드 이름을 확정하고, 그 노드로 실행을 이동시킨다. 함정: 라우팅 함수가 딕셔너리에 없는 값을 반환하면 런타임 에러가 발생한다. Literal 타입 선언과 딕셔너리 키를 항상 일치시켜야 하는 이유가 여기 있다.
왜 이 패턴인가 — 단일 LLM과의 비교
직관적으로 "LLM 하나에 다 시키면 안 되나?"라는 의문이 생긴다. 구조 차이를 눈으로 보자.
단일 LLM 방식은 "영화 추천도 잘하고, 영화 정보도 잘 알려주고, 일반 대화도 잘해야 한다"는 요구를 한 system prompt에 담아야 한다. 요구사항이 늘수록 prompt가 길어지고 서로 간섭하기 시작한다. 반면 라우터 패턴에서 각 handler의 system prompt는 자신의 역할만 기술한다 — recommend_handler는 "영화 추천 전문가"이고 info_handler는 "영화 정보 전문가"다. 새로운 카테고리(예: "review_handler")를 추가할 때도 기존 handler를 건드리지 않는다. 노드 하나를 추가하고 라우팅 함수에 분기를 하나 더 넣는 것으로 끝난다.
새 카테고리 추가 시 변경 범위
이 다이어그램이 이 패턴의 핵심 장점을 요약한다. 변경이 필요한 곳이 명확하게 다섯 군데로 제한되고, 기존 노드(recommend_handler, info_handler, general_handler)는 손대지 않아도 된다. Literal 타입이 변경 범위를 코드에서 강제하기 때문에 4번(매핑 추가)을 빠뜨리면 3번에서 타입 에러가 발생한다. 함정 하나 더: 5번(add_edge → END)을 빠뜨려도 타입 에러는 없다. 런타임에서 그래프가 새 노드에 도달한 후 다음 엣지를 찾지 못해 중단된다. 노드 추가 후엔 항상 종료 엣지도 함께 확인하는 습관이 필요하다.
정리
LangGraph 조건부 라우팅의 핵심을 세 문장으로 압축하면 이렇다. 라우팅 함수는 state를 읽어 노드 이름 문자열만 반환한다 — 아무것도 생성하지 않는다. Literal 반환 타입은 단순 문서화가 아니라 노드 이름 변경 시 수정 범위를 강제하는 안전장치다. 역할 분리는 각 handler의 system prompt를 단순하게 유지하고 새 카테고리 추가를 기존 코드 변경 없이 가능하게 한다.
파이프라인이 복잡해질수록 이 세 가지 원칙이 그래프를 읽기 쉽고 확장 가능하게 유지하는 토대가 된다.
이 글은 생성형 AI의 도움을 받아 작성되었습니다. 원본 자료를 기반으로 AI가 초안을 생성하고, 작성자가 검토·편집하였습니다.
'IT' 카테고리의 다른 글
RAG 에이전트 완전 조립 — create_agent부터 동작 추적까지 (1) 2026.06.29 검색 결과를 에이전트 도구로 — build_context와 @tool 패턴 (0) 2026.06.28 RAG의 배경과 make_retriever — LLM이 모르는 문서를 검색하는 방법 (0) 2026.06.28 생성과 검증의 분리 — generator 노드와 validator 노드 설계 (0) 2026.06.28 LangGraph 자기 수정 패턴의 State 설계 — 루프를 위한 5가지 필드 (0) 2026.06.27 LLM을 분류기로 쓰기 — SystemMessage와 HumanMessage로 Classifier Node 만들기 (0) 2026.06.27 LangGraph 상태에 메시지 외 필드 추가하기 — RouterState 설계 (0) 2026.06.26 graph.invoke vs graph.stream — 에이전트 실행의 두 가지 방식 (1) 2026.06.26 bind_tools — LLM이 도구를 인식하는 방법 (0) 2026.06.26 LangGraph StateGraph 완전 분해 — 노드·엣지·조건부 라우팅 (1) 2026.06.25