본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 12. · 12 Views
LangGraph 입문 완벽 가이드
LLM 애플리케이션 개발의 새로운 패러다임, LangGraph를 처음 접하는 개발자를 위한 완벽 가이드입니다. 설치부터 핵심 개념까지 실무에 바로 적용할 수 있는 내용을 담았습니다.
목차
- LangGraph란_무엇인가
- LangChain_vs_LangGraph_차이점
- 저수준_오케스트레이션_개념
- pip_install_langgraph_설치
- Google_Pregel_Apache_Beam_영감
- LangSmith_통합_디버깅
1. LangGraph란 무엇인가
어느 날 김개발 씨는 LangChain으로 챗봇을 만들다가 벽에 부딪혔습니다. "사용자 입력에 따라 다른 AI 에이전트로 분기하고 싶은데, 어떻게 해야 하지?" 선배 박시니어 씨가 다가와 말했습니다.
"그럴 때는 LangGraph를 써봐요."
LangGraph는 LLM 애플리케이션을 그래프 구조로 설계하고 실행하는 프레임워크입니다. 마치 순서도를 그리듯이 AI 에이전트의 흐름을 정의할 수 있습니다.
복잡한 대화 흐름이나 다중 에이전트 시스템을 구축할 때 진가를 발휘합니다.
다음 코드를 살펴봅시다.
from langgraph.graph import StateGraph, END
from typing import TypedDict
# 상태 정의: 대화의 현재 상태를 저장합니다
class AgentState(TypedDict):
messages: list
next_step: str
# 그래프 생성: AI 워크플로우의 뼈대입니다
workflow = StateGraph(AgentState)
# 노드 추가: 각 단계의 작업을 정의합니다
workflow.add_node("analyze", analyze_user_input)
workflow.add_node("respond", generate_response)
# 엣지 추가: 노드 간의 흐름을 연결합니다
workflow.add_edge("analyze", "respond")
workflow.add_edge("respond", END)
김개발 씨는 입사 6개월 차 AI 개발자입니다. 최근 회사에서 고객 상담 챗봇 프로젝트를 맡게 되었습니다.
간단한 질문은 FAQ 봇이 답하고, 복잡한 문의는 전문 상담 에이전트로 연결해야 합니다. LangChain으로 시작했지만, 조건에 따라 다른 경로로 분기하는 로직을 구현하기가 쉽지 않았습니다.
선배 개발자 박시니어 씨가 코드를 보더니 말합니다. "LangChain은 좋은 도구지만, 복잡한 흐름 제어에는 한계가 있어요.
LangGraph를 써보는 게 어떨까요?" 그렇다면 LangGraph란 정확히 무엇일까요? 쉽게 비유하자면, LangGraph는 마치 프로그램 순서도를 코드로 구현하는 것과 같습니다.
우리가 업무 프로세스를 설명할 때 순서도를 그리듯이, AI 에이전트의 동작 흐름도 그래프로 표현할 수 있습니다. 각 단계는 노드가 되고, 단계 간 연결은 엣지가 됩니다.
이처럼 LangGraph는 상태 기반 그래프를 통해 LLM 애플리케이션의 복잡한 흐름을 관리합니다. LangGraph가 없던 시절에는 어땠을까요?
개발자들은 복잡한 조건문과 반복문으로 대화 흐름을 관리해야 했습니다. 사용자가 A를 선택하면 B로, C를 선택하면 D로 가는 로직을 if-else로 빼곡히 작성했습니다.
코드가 길어지고, 새로운 분기를 추가할 때마다 전체 구조를 다시 검토해야 했습니다. 더 큰 문제는 상태 관리였습니다.
대화의 어느 단계에 있는지, 이전에 어떤 정보를 받았는지 추적하는 것이 매우 번거로웠습니다. 바로 이런 문제를 해결하기 위해 LangGraph가 등장했습니다.
LangGraph를 사용하면 선언적으로 워크플로우를 정의할 수 있습니다. 각 노드가 무엇을 하는지만 명시하면, 프레임워크가 알아서 흐름을 관리합니다.
또한 상태가 자동으로 관리되어 이전 대화 내용이나 컨텍스트를 쉽게 추적할 수 있습니다. 무엇보다 시각적으로 이해하기 쉽다는 큰 이점이 있습니다.
코드를 보면 전체 흐름이 한눈에 들어옵니다. 위의 코드를 한 줄씩 살펴보겠습니다.
먼저 StateGraph를 생성하는 부분이 핵심입니다. 이것은 상태를 관리하는 그래프의 뼈대입니다.
AgentState는 대화의 현재 상태를 담는 컨테이너로, 메시지 히스토리와 다음 단계 정보를 저장합니다. 다음으로 add_node로 각 작업 단계를 추가합니다.
"analyze" 노드는 사용자 입력을 분석하고, "respond" 노드는 응답을 생성합니다. 마지막으로 add_edge로 노드 간 연결을 정의합니다.
분석이 끝나면 응답으로, 응답이 끝나면 종료로 이어집니다. 실제 현업에서는 어떻게 활용할까요?
예를 들어 온라인 쇼핑몰의 고객센터 챗봇을 개발한다고 가정해봅시다. 사용자가 "환불하고 싶어요"라고 하면 환불 정책 확인 노드로, "배송 조회"라고 하면 주문 조회 노드로 분기합니다.
각 노드는 독립적으로 작동하지만, 전체적으로는 하나의 매끄러운 대화 흐름을 만들어냅니다. 네이버, 카카오 같은 대기업에서도 이런 패턴을 적극적으로 활용하고 있습니다.
하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수 중 하나는 너무 많은 노드를 만드는 것입니다.
모든 작은 단계마다 노드를 만들면 오히려 복잡해집니다. 이렇게 하면 디버깅이 어려워지고 성능도 저하될 수 있습니다.
따라서 의미 있는 단위로 노드를 묶어서 설계해야 합니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 설명을 들은 김개발 씨는 눈이 반짝였습니다. "아, 그래서 상태 기반 그래프라고 하는군요!" LangGraph를 제대로 이해하면 복잡한 AI 애플리케이션도 깔끔하게 구조화할 수 있습니다.
여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.
실전 팁
💡 - 작은 프로젝트부터 시작해서 노드와 엣지의 개념에 익숙해지세요
- 그래프를 그려보면서 설계하면 실수를 줄일 수 있습니다
2. LangChain vs LangGraph 차이점
커피를 마시던 김개발 씨가 문득 궁금해졌습니다. "LangChain도 있는데, 왜 굳이 LangGraph가 필요할까?" 마침 옆자리의 박시니어 씨가 웃으며 답했습니다.
"두 도구는 목적이 다르답니다."
LangChain은 LLM 애플리케이션의 기본 빌딩 블록을 제공하는 라이브러리입니다. 반면 LangGraph는 복잡한 흐름 제어와 상태 관리에 특화된 프레임워크입니다.
체인처럼 순차적으로 실행되는 LangChain과 달리, LangGraph는 조건 분기와 순환이 가능합니다.
다음 코드를 살펴봅시다.
# LangChain 방식: 순차적 체인
from langchain.chains import LLMChain
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.run(input_text)
# 단순하지만 복잡한 분기는 어렵습니다
# LangGraph 방식: 조건부 그래프
from langgraph.graph import StateGraph
def router(state):
# 상태에 따라 다음 노드를 동적으로 결정합니다
if state["user_type"] == "premium":
return "premium_service"
return "basic_service"
graph = StateGraph(State)
graph.add_conditional_edges("start", router)
# 조건에 따라 유연하게 분기합니다
김개발 씨는 지난주에 LangChain으로 간단한 Q&A 봇을 만들었습니다. 문서를 읽고, 질문에 답하는 기본적인 기능은 잘 작동했습니다.
하지만 이번 프로젝트는 다릅니다. 사용자 유형에 따라 다른 서비스를 제공해야 하고, 경우에 따라 추가 질문을 받아야 합니다.
LangChain으로는 이런 복잡한 흐름을 구현하기가 까다로웠습니다. 박시니어 씨가 화이트보드에 그림을 그리며 설명합니다.
"LangChain은 기차 같아요. 정해진 레일 위를 순서대로 달리죠.
LangGraph는 도로 네트워크 같습니다. 신호등과 이정표에 따라 경로가 바뀌죠." 그렇다면 두 도구의 차이점은 정확히 무엇일까요?
쉽게 비유하자면, LangChain은 마치 요리 레시피와 같습니다. 재료를 준비하고(프롬프트), 순서대로 조리하고(체인), 완성된 요리를 내놓습니다(결과).
단계가 명확하고 예측 가능합니다. 반면 LangGraph는 내비게이션 앱과 같습니다.
현재 위치(상태)를 확인하고, 교통 상황(조건)에 따라 경로를 재계산하며, 목적지에 도달할 때까지 계속 안내합니다. LangChain만으로는 어떤 한계가 있을까요?
LangChain의 체인 구조는 본질적으로 선형적입니다. A 다음에 B, B 다음에 C가 실행됩니다.
물론 SequentialChain이나 RouterChain으로 어느 정도 분기를 구현할 수 있지만, 복잡한 조건 분기나 루프를 표현하기는 어렵습니다. 예를 들어 "사용자가 만족할 때까지 답변을 개선하는" 루프를 구현하려면 추가 코드가 많이 필요합니다.
또한 상태 관리가 암묵적입니다. 중간 단계의 상태를 명시적으로 추적하기 어렵습니다.
바로 이런 한계를 극복하기 위해 LangGraph가 탄생했습니다. LangGraph는 명시적인 그래프 구조를 제공합니다.
노드와 엣지로 흐름을 정의하므로 복잡한 분기도 직관적으로 표현할 수 있습니다. 또한 상태가 일급 객체입니다.
StateGraph의 상태는 각 노드를 거치며 변경되고, 언제든지 접근할 수 있습니다. 무엇보다 순환이 자연스럽습니다.
특정 조건이 만족될 때까지 같은 노드를 반복 실행하는 것이 쉽습니다. 위의 코드를 비교해 보겠습니다.
LangChain 예제에서는 LLMChain이 입력을 받아 순차적으로 처리합니다. 간단하고 명확하지만, 입력에 따라 다른 동작을 하려면 체인 밖에서 if문으로 처리해야 합니다.
반면 LangGraph 예제의 router 함수는 상태를 보고 동적으로 다음 노드를 결정합니다. "premium_service"와 "basic_service"라는 서로 다른 경로로 분기할 수 있습니다.
add_conditional_edges는 이런 조건부 분기를 그래프에 직접 표현합니다. 실제 현업에서는 어떤 선택을 해야 할까요?
간단한 Q&A 봇이나 문서 요약처럼 선형적인 작업에는 LangChain이 적합합니다. 설정이 간단하고 빠르게 프로토타입을 만들 수 있습니다.
하지만 고객 상담 시스템, 멀티 에이전트 협업, 게임 AI처럼 복잡한 흐름 제어가 필요하면 LangGraph를 선택해야 합니다. 실제로 많은 기업들이 프로토타입은 LangChain으로, 프로덕션은 LangGraph로 전환하는 패턴을 따릅니다.
하지만 주의할 점도 있습니다. 초보 개발자들은 "LangGraph가 더 강력하니 무조건 써야지"라고 생각하기 쉽습니다.
하지만 단순한 작업에 복잡한 도구를 쓰면 오히려 개발 시간이 늘어나고 유지보수가 어려워집니다. 프로젝트의 복잡도를 먼저 평가하고, 그에 맞는 도구를 선택하는 것이 중요합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.
"아, LangChain과 LangGraph는 경쟁 관계가 아니라 보완 관계군요!" 실제로 두 도구를 함께 사용하는 경우도 많습니다. LangChain의 컴포넌트를 LangGraph의 노드로 활용하면 최고의 효율을 낼 수 있습니다.
실전 팁
💡 - 프로토타입은 LangChain으로 빠르게, 프로덕션은 LangGraph로 견고하게
- 두 도구를 혼용하는 것도 좋은 전략입니다
3. 저수준 오케스트레이션 개념
점심 식사 후 김개발 씨는 LangGraph 문서를 읽다가 "low-level orchestration"이라는 표현을 발견했습니다. "오케스트레이션은 알겠는데, 저수준이란 게 뭐지?" 궁금증을 참지 못하고 박시니어 씨에게 물었습니다.
저수준 오케스트레이션은 개발자가 AI 에이전트의 실행 흐름을 세밀하게 제어할 수 있다는 의미입니다. 고수준 프레임워크가 추상화로 편리함을 제공한다면, 저수준 도구는 투명성과 제어권을 제공합니다.
LangGraph는 내부 동작을 감추지 않고, 개발자가 원하는 대로 커스터마이징할 수 있게 합니다.
다음 코드를 살펴봅시다.
from langgraph.graph import StateGraph
from langgraph.checkpoint import MemorySaver
# 저수준 제어: 체크포인트를 직접 관리합니다
checkpointer = MemorySaver()
def custom_node(state):
# 노드 내부 로직을 완전히 제어합니다
print(f"현재 상태: {state}")
# 복잡한 비즈니스 로직을 자유롭게 구현
if state["retry_count"] > 3:
state["status"] = "failed"
else:
state["status"] = "processing"
return state
graph = StateGraph(State)
graph.add_node("custom", custom_node)
# 실행 과정의 모든 단계를 개발자가 결정합니다
김개발 씨는 이전에 다른 AI 프레임워크를 사용해본 적이 있습니다. 버튼 몇 개만 누르면 챗봇이 뚝딱 만들어지는 노코드 플랫폼도 써봤습니다.
처음에는 신기했지만, 조금 복잡한 기능을 추가하려고 하니 막막했습니다. "이 부분만 바꾸고 싶은데 어떻게 하지?" 블랙박스 안을 들여다볼 수 없어 답답했습니다.
박시니어 씨가 커피를 한 모금 마시고 말합니다. "그게 바로 고수준과 저수준의 차이예요.
고수준 도구는 편하지만 제한적이고, 저수준 도구는 복잡하지만 자유롭죠." 그렇다면 저수준 오케스트레이션이란 정확히 무엇일까요? 쉽게 비유하자면, 고수준 오케스트레이션은 마치 자동차의 자동 변속기와 같습니다.
운전자는 그냥 액셀을 밟기만 하면 차가 알아서 기어를 변속합니다. 편하지만, 언제 어떻게 기어가 바뀌는지 제어할 수 없습니다.
반면 저수준 오케스트레이션은 수동 변속기와 같습니다. 운전자가 직접 클러치를 밟고 기어를 바꿉니다.
번거롭지만, 상황에 맞춰 최적의 제어가 가능합니다. 고수준 프레임워크만 사용하면 어떤 한계가 있을까요?
많은 AI 플랫폼들은 추상화를 통해 복잡성을 숨깁니다. "에이전트 생성" 버튼을 누르면 뒤에서 어떤 일이 일어나는지 알 수 없습니다.
간단한 프로젝트에는 문제없지만, 프로덕션 환경에서는 한계가 드러납니다. 예를 들어 특정 조건에서만 캐싱을 적용하고 싶거나, 에러가 발생했을 때 커스텀 복구 로직을 실행하고 싶을 때 막힙니다.
디버깅도 어렵습니다. 뭔가 잘못되었는데 어디서 문제가 생긴 건지 추적할 방법이 없습니다.
바로 이런 문제를 해결하기 위해 저수준 오케스트레이션이 필요합니다. LangGraph는 내부 동작을 투명하게 공개합니다.
각 노드가 실행될 때 정확히 무슨 일이 일어나는지, 상태가 어떻게 변하는지 모두 볼 수 있습니다. 또한 커스터마이징이 자유롭습니다.
체크포인트 저장 방식, 에러 처리, 재시도 로직 등 모든 것을 개발자가 직접 구현할 수 있습니다. 무엇보다 디버깅이 쉽습니다.
각 단계의 입력과 출력을 확인할 수 있어 문제를 빠르게 찾을 수 있습니다. 위의 코드를 살펴보겠습니다.
MemorySaver는 체크포인트를 메모리에 저장하는 저수준 컴포넌트입니다. 원한다면 Redis나 데이터베이스에 저장하는 커스텀 체크포인터를 만들 수도 있습니다.
custom_node 함수 내부를 보면, 상태를 직접 검사하고 수정합니다. "재시도 횟수가 3회를 넘으면 실패 처리"하는 비즈니스 로직을 자유롭게 구현했습니다.
print 문으로 현재 상태를 출력해 디버깅할 수도 있습니다. 이 모든 것이 프레임워크의 제약 없이 가능합니다.
실제 현업에서는 어떻게 활용할까요? 금융 서비스의 대출 심사 시스템을 예로 들어봅시다.
단순히 "대출 승인"과 "대출 거부"만 있는 게 아니라, 신용도에 따라 여러 단계의 검토가 필요합니다. 특정 조건에서는 추가 서류를 요청하고, 심사 과정을 로그로 남기고, 이상 거래 패턴을 감지해야 합니다.
이런 복잡한 비즈니스 로직을 구현하려면 저수준 제어가 필수입니다. 하지만 주의할 점도 있습니다.
초보 개발자들은 "저수준이 강력하니까 무조건 모든 걸 직접 만들어야지"라고 생각하기 쉽습니다. 하지만 불필요한 저수준 코딩은 복잡성만 증가시킵니다.
이미 프레임워크가 제공하는 기능을 다시 구현할 필요는 없습니다. 따라서 필요한 부분만 커스터마이징하고, 나머지는 기본 구현을 사용하는 것이 현명합니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 이해했다는 표정을 지었습니다.
"아, 저수준이라고 해서 품질이 낮다는 게 아니라, 제어의 수준이 세밀하다는 뜻이군요!" 저수준 오케스트레이션을 제대로 이해하면 프레임워크를 도구로 다루는 것이 아니라 파트너로 활용할 수 있습니다. 여러분도 필요한 부분을 커스터마이징하며 실력을 쌓아보세요.
실전 팁
💡 - 처음에는 기본 구현을 사용하고, 필요할 때만 커스터마이징하세요
- 디버깅을 위해 print나 로깅을 적극 활용하세요
4. pip install langgraph 설치
드디어 LangGraph를 실제로 써볼 시간입니다. 김개발 씨는 터미널을 열고 설치 명령어를 검색했습니다.
"설치는 간단하겠지?" 하지만 의존성 충돌 에러가 떴습니다. 당황한 김개발 씨에게 박시니어 씨가 조언합니다.
"가상환경부터 만들어요."
LangGraph 설치는 pip를 통해 간단히 진행됩니다. 하지만 깔끔한 개발 환경을 위해 가상환경을 먼저 만드는 것이 좋습니다.
Python 3.9 이상이 필요하며, LangChain 패키지도 함께 설치됩니다. 첫 설치라면 의존성 패키지가 많아 시간이 조금 걸릴 수 있습니다.
다음 코드를 살펴봅시다.
# 1단계: 가상환경 생성 (프로젝트별 독립 환경)
python -m venv langgraph_env
# 2단계: 가상환경 활성화
# Windows: langgraph_env\Scripts\activate
# Mac/Linux: source langgraph_env/bin/activate
# 3단계: pip 업그레이드 (최신 버전 사용 권장)
pip install --upgrade pip
# 4단계: LangGraph 설치 (의존성 자동 설치)
pip install langgraph
# 5단계: 설치 확인 (버전 출력)
python -c "import langgraph; print(langgraph.__version__)"
# 추가: LangSmith 연동 시 (선택사항)
pip install langsmith
김개발 씨는 신나는 마음으로 LangGraph를 설치하려고 했습니다. 터미널에 "pip install langgraph"를 입력했는데, 갑자기 빨간 에러 메시지가 주르륵 나타났습니다.
"ERROR: Cannot install langgraph due to conflicting dependencies..." 기존 프로젝트에서 설치한 다른 패키지와 충돌이 난 것입니다. 박시니어 씨가 김개발 씨의 화면을 보더니 말합니다.
"아, 전역 환경에 바로 설치하면 안 돼요. 가상환경을 만들어야죠." 그렇다면 왜 가상환경이 필요할까요?
쉽게 비유하자면, 가상환경은 마치 개인 서재와 같습니다. 집 거실에 모든 책을 쌓아두면 어떤 책이 어느 프로젝트용인지 헷갈립니다.
하지만 프로젝트별로 서재를 따로 만들면 깔끔하게 관리할 수 있습니다. LangGraph 프로젝트는 LangGraph 서재에, Django 프로젝트는 Django 서재에 필요한 패키지만 설치하는 것입니다.
이렇게 하면 의존성 충돌을 피하고, 프로젝트를 다른 환경에 배포할 때도 필요한 패키지 목록을 쉽게 관리할 수 있습니다. 가상환경 없이 설치하면 어떤 문제가 생길까요?
전역 Python 환경에 패키지를 마구 설치하면 버전 충돌이 발생합니다. 프로젝트 A는 LangChain 0.1이 필요한데, 프로젝트 B는 LangChain 0.2가 필요하다면?
하나를 설치하면 다른 프로젝트가 깨집니다. 또한 시스템 전체에 영향을 미칠 수 있습니다.
잘못된 패키지를 설치했다가 다른 프로그램이 동작하지 않는 경우도 있습니다. 더 큰 문제는 재현성입니다.
동료에게 프로젝트를 넘겨줄 때 "제 컴퓨터에서는 됐는데요?"라는 상황이 생깁니다. 바로 이런 문제를 막기 위해 가상환경을 먼저 만들어야 합니다.
python -m venv 명령어는 현재 디렉토리에 독립된 Python 환경을 생성합니다. 이 환경 안에는 Python 인터프리터와 pip가 별도로 들어있습니다.
가상환경을 활성화하면 터미널 프롬프트 앞에 (langgraph_env) 같은 표시가 나타납니다. 이제 여기서 설치하는 모든 패키지는 이 가상환경에만 영향을 줍니다.
작업이 끝나면 deactivate 명령어로 가상환경을 빠져나올 수 있습니다. 위의 코드를 단계별로 살펴보겠습니다.
1단계에서 "langgraph_env"라는 이름의 가상환경을 만듭니다. 이름은 원하는 대로 바꿀 수 있습니다.
2단계는 운영체제마다 명령어가 다릅니다. Windows는 백슬래시를, Mac과 Linux는 source를 사용합니다.
3단계에서 pip를 최신 버전으로 업그레이드하는 것은 선택이지만 권장됩니다. 최신 pip는 의존성 해결이 더 똑똑합니다.
4단계가 핵심입니다. LangGraph를 설치하면 LangChain, Pydantic 등 필요한 패키지가 자동으로 함께 설치됩니다.
5단계로 설치가 제대로 되었는지 확인합니다. 버전이 출력되면 성공입니다.
실제 현업에서는 어떻게 관리할까요? 팀 프로젝트에서는 requirements.txt 파일을 사용합니다.
"pip freeze > requirements.txt"로 현재 설치된 패키지 목록을 저장하고, 다른 팀원은 "pip install -r requirements.txt"로 동일한 환경을 재현합니다. 더 현대적인 방법으로는 Poetry나 Pipenv 같은 도구를 사용하기도 합니다.
대기업에서는 Docker 이미지에 가상환경을 포함시켜 배포하는 것이 일반적입니다. 하지만 주의할 점도 있습니다.
초보 개발자들은 가상환경을 만들어놓고 활성화하는 것을 잊어버립니다. 터미널 프롬프트에 가상환경 이름이 표시되는지 항상 확인해야 합니다.
또한 가상환경 폴더를 Git에 커밋하면 안 됩니다. .gitignore에 "*_env/" 같은 패턴을 추가해 제외해야 합니다.
가상환경은 언제든지 다시 만들 수 있으므로, requirements.txt만 관리하면 됩니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
박시니어 씨의 조언대로 가상환경을 만들고 다시 설치하니 성공했습니다. "터미널에 초록색 SUCCESS가 떴어요!" 이제 LangGraph를 사용할 준비가 끝났습니다.
깔끔한 개발 환경을 유지하면 나중에 고생하지 않습니다.
실전 팁
💡 - 프로젝트마다 별도의 가상환경을 만드는 습관을 들이세요
- requirements.txt를 Git에 커밋해 팀원과 환경을 공유하세요
5. Google Pregel Apache Beam 영감
LangGraph 문서를 읽던 김개발 씨는 흥미로운 문장을 발견했습니다. "Inspired by Google's Pregel and Apache Beam." "Pregel이 뭐지?
그래프 DB인가?" 궁금증이 생긴 김개발 씨는 또다시 박시니어 씨를 찾아갔습니다.
Google Pregel은 대규모 그래프 처리를 위한 분산 컴퓨팅 모델입니다. Apache Beam은 배치와 스트림 데이터 처리를 통합한 프레임워크입니다.
LangGraph는 이 두 시스템의 핵심 아이디어인 "그래프 기반 병렬 처리"와 "상태 전달 메커니즘"을 AI 워크플로우에 적용했습니다.
다음 코드를 살펴봅시다.
# Pregel의 핵심 개념: Vertex-centric 프로그래밍
from langgraph.graph import StateGraph
def process_node(state):
# 각 노드는 독립적으로 실행됩니다 (Pregel의 superstep)
state["processed"] = True
# 다음 노드로 상태를 전달합니다 (message passing)
return state
# Apache Beam의 영감: 파이프라인 구조
graph = StateGraph(State)
graph.add_node("step1", process_node)
graph.add_node("step2", process_node)
graph.add_node("step3", process_node)
# 데이터가 노드를 거치며 변환됩니다 (Beam의 PTransform)
graph.add_edge("step1", "step2")
graph.add_edge("step2", "step3")
# 병렬 실행 가능 (분산 처리의 기반)
김개발 씨는 그래프 알고리즘 수업에서 BFS, DFS는 배웠지만, Pregel은 처음 듣는 이름입니다. "그래프 처리인 건 알겠는데, 어떤 문제를 해결하는 거지?" 인터넷을 검색해보니 Google이 웹 페이지 순위를 계산하기 위해 만든 시스템이라고 나옵니다.
"그게 AI 에이전트랑 무슨 상관이지?" 박시니어 씨가 화이트보드를 꺼내 듭니다. "페이지랭크를 생각해봐요.
웹 페이지는 노드, 링크는 엣지죠. 각 페이지의 중요도를 계산하려면 연결된 모든 페이지를 봐야 해요.
이걸 효율적으로 하려면?" 그렇다면 Pregel이란 정확히 무엇일까요? 쉽게 비유하자면, Pregel은 마치 우체국 시스템과 같습니다.
수백만 개의 집(노드)이 있고, 각 집은 이웃에게 편지(메시지)를 보냅니다. 모든 집이 동시에 편지를 받고, 내용을 처리하고, 다시 다음 편지를 보냅니다.
이 과정을 "superstep"이라고 부릅니다. 중요한 점은 각 집이 독립적으로 작동한다는 것입니다.
중앙 관리자가 일일이 지시하지 않아도, 각자 규칙에 따라 움직입니다. 이것이 대규모 병렬 처리의 핵심입니다.
그렇다면 Apache Beam은 무엇일까요? Apache Beam은 컨베이어 벨트 공장에 비유할 수 있습니다.
원재료가 들어오면 여러 단계의 가공을 거쳐 완제품이 나옵니다. 각 단계(PTransform)는 독립적으로 작동하며, 데이터는 파이프라인을 따라 흐릅니다.
중요한 것은 배치 데이터(한 번에 처리)든 스트림 데이터(계속 들어오는)든 동일한 파이프라인으로 처리한다는 점입니다. LangGraph도 이 아이디어를 차용해, 일회성 질문이든 연속적인 대화든 같은 그래프로 처리합니다.
LangGraph는 이 두 시스템에서 무엇을 배웠을까요? 첫째, 노드 중심 사고입니다.
Pregel처럼 각 노드가 독립적인 작업 단위입니다. 노드는 입력 상태를 받아 처리하고, 출력 상태를 내보냅니다.
둘째, 메시지 전달입니다. 노드 간에 상태가 전달되는 방식이 Pregel의 메시지 패싱과 유사합니다.
셋째, 파이프라인 구조입니다. Beam처럼 여러 단계를 연결해 복잡한 처리를 구성합니다.
넷째, 병렬 실행 가능성입니다. 의존성이 없는 노드들은 동시에 실행될 수 있어 성능이 향상됩니다.
위의 코드를 개념적으로 분석해 보겠습니다. process_node 함수는 Pregel의 vertex program에 해당합니다.
각 노드가 자신의 상태만 보고 독립적으로 동작합니다. 다른 노드가 무엇을 하는지 신경 쓰지 않습니다.
add_edge로 연결하는 구조는 Apache Beam의 파이프라인과 유사합니다. 데이터가 step1 → step2 → step3 순서로 흐르며 변환됩니다.
만약 step1과 step3가 독립적이라면 동시에 실행될 수 있습니다. 이것이 암묵적 병렬성입니다.
실제 현업에서 이런 설계가 왜 중요할까요? 대규모 AI 시스템을 생각해봅시다.
수천 명의 사용자가 동시에 챗봇을 사용하고 있습니다. 각 대화는 독립적인 그래프 인스턴스로 처리됩니다.
Pregel처럼 각 인스턴스는 병렬로 실행되며, 서로 간섭하지 않습니다. 또한 특정 노드에서 외부 API를 호출하는 동안 다른 노드가 계속 작동할 수 있습니다.
이런 비동기 병렬 처리가 가능한 것은 Pregel과 Beam의 설계 철학을 계승했기 때문입니다. 하지만 주의할 점도 있습니다.
초보 개발자들은 "병렬 처리가 가능하다니 무조건 빠르겠네"라고 생각하기 쉽습니다. 하지만 직렬 의존성이 많으면 병렬화 효과가 줄어듭니다.
A가 끝나야 B가 시작되고, B가 끝나야 C가 시작되는 구조라면 병렬 처리가 불가능합니다. 따라서 그래프를 설계할 때 독립적인 노드를 최대한 만들어야 성능 이점을 얻을 수 있습니다.
다시 김개발 씨의 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 감탄했습니다.
"와, LangGraph가 그냥 만들어진 게 아니구나. 구글과 아파치의 검증된 아이디어를 가져온 거네요!" 거인의 어깨에 올라서면 더 멀리 볼 수 있습니다.
LangGraph도 이미 검증된 분산 시스템의 지혜를 AI 개발에 적용한 것입니다.
실전 팁
💡 - 노드를 설계할 때 독립성을 최대한 유지하면 병렬 처리 이점을 누릴 수 있습니다
- Pregel과 Beam의 원리를 이해하면 LangGraph의 설계 철학이 더 명확해집니다
6. LangSmith 통합 디버깅
김개발 씨가 드디어 첫 LangGraph 애플리케이션을 만들었습니다. 하지만 예상과 다른 결과가 나왔습니다.
"어디서 잘못된 거지?" 로그를 뒤지던 김개발 씨에게 박시니어 씨가 말합니다. "LangSmith를 써보셨어요?"
LangSmith는 LangChain과 LangGraph 애플리케이션을 위한 통합 모니터링 및 디버깅 플랫폼입니다. 각 노드의 입력, 출력, 실행 시간을 시각적으로 추적하고, 프롬프트 변경 이력을 관리하며, 프로덕션 환경의 성능을 분석할 수 있습니다.
개발부터 배포까지 전 과정을 지원합니다.
다음 코드를 살펴봅시다.
import os
from langchain_core.tracers.context import tracing_v2_enabled
from langgraph.graph import StateGraph
# LangSmith 설정: API 키로 추적을 활성화합니다
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "your-api-key-here"
os.environ["LANGCHAIN_PROJECT"] = "my-langgraph-project"
def debug_node(state):
# 이 함수의 모든 실행이 LangSmith에 기록됩니다
result = complex_logic(state)
# 입력, 출력, 실행 시간이 자동으로 추적됩니다
return result
graph = StateGraph(State)
graph.add_node("debug", debug_node)
# 실행하면 LangSmith 대시보드에서 실시간 확인 가능
with tracing_v2_enabled(project_name="my-project"):
result = graph.compile().invoke(initial_state)
김개발 씨는 LangGraph로 고객 상담 봇을 만들었습니다. 로컬에서 테스트할 때는 잘 작동했는데, 실제 사용자 데이터로 돌리니 이상한 답변이 나왔습니다.
"분석 노드는 통과했는데 왜 응답이 이상하지?" 터미널 로그를 보지만 어디서 잘못된 건지 파악이 안 됩니다. 각 노드에 print문을 추가하며 디버깅하는데, 이것도 한계가 있습니다.
박시니어 씨가 노트북을 가져와 보여줍니다. "LangSmith를 쓰면 이렇게 한눈에 볼 수 있어요." 화면에는 그래프의 각 노드가 시각적으로 표시되고, 각 단계의 입력과 출력이 깔끔하게 정리되어 있습니다.
그렇다면 LangSmith란 정확히 무엇일까요? 쉽게 비유하자면, LangSmith는 마치 블랙박스와 같습니다.
자동차 사고가 났을 때 블랙박스를 보면 무슨 일이 있었는지 정확히 알 수 있습니다. LangSmith도 AI 애플리케이션의 모든 실행 과정을 기록합니다.
어떤 프롬프트가 입력되었는지, LLM이 무엇을 반환했는지, 각 노드가 얼마나 걸렸는지 모두 추적됩니다. 이것을 observability(관측 가능성)라고 부릅니다.
LangSmith 없이 디버깅하면 어떤 어려움이 있을까요? 복잡한 LangGraph 애플리케이션은 여러 노드를 거치며 상태가 변합니다.
중간에 문제가 생겨도 어느 노드에서 잘못되었는지 파악하기 어렵습니다. print문을 추가해도 로그가 뒤섞여 읽기 힘듭니다.
더 큰 문제는 프로덕션 환경입니다. 실제 사용자의 버그를 재현하려면 같은 입력으로 다시 실행해봐야 하는데, 그 입력 자체를 모르는 경우가 많습니다.
또한 성능 병목을 찾기도 어렵습니다. 전체 응답 시간이 느린데 어느 노드가 원인인지 알 수 없습니다.
바로 이런 문제를 해결하기 위해 LangSmith가 필수입니다. LangSmith는 자동 추적을 제공합니다.
코드를 거의 수정하지 않아도 모든 실행이 기록됩니다. 시각적 대시보드에서 그래프 구조를 보며 각 노드의 상태를 확인할 수 있습니다.
타임라인 뷰로 어느 노드가 시간이 오래 걸리는지 한눈에 파악할 수 있습니다. 또한 프롬프트 버전 관리도 지원합니다.
프롬프트를 수정하며 실험할 때 어떤 버전이 최고 성능을 냈는지 비교할 수 있습니다. 무엇보다 팀 협업에 유용합니다.
동료가 특정 실행 결과를 URL로 공유하면, 같은 화면을 볼 수 있습니다. 위의 코드를 살펴보겠습니다.
환경 변수 설정이 핵심입니다. LANGCHAIN_TRACING_V2를 "true"로 설정하면 추적이 활성화됩니다.
LANGCHAIN_API_KEY는 LangSmith 웹사이트에서 발급받습니다. LANGCHAIN_PROJECT는 프로젝트 이름으로, 여러 실험을 구분하는 데 사용됩니다.
tracing_v2_enabled 컨텍스트 매니저를 사용하면 특정 코드 블록만 추적할 수도 있습니다. 이제 그래프를 실행하면 LangSmith 대시보드에 실시간으로 결과가 표시됩니다.
실제 현업에서는 어떻게 활용할까요? 스타트업 A사는 LangSmith로 사용자 피드백을 추적합니다.
"이 답변이 이상해요"라는 신고가 들어오면, 해당 실행 ID를 찾아 정확히 무슨 일이 있었는지 재현합니다. 대기업 B사는 성능 최적화에 활용합니다.
전체 실행 시간의 80%를 차지하는 병목 노드를 찾아 개선했습니다. 또한 A/B 테스트를 할 때도 유용합니다.
프롬프트 버전 A와 B를 비교해 어느 쪽이 더 나은 결과를 내는지 데이터로 확인합니다. 하지만 주의할 점도 있습니다.
초보 개발자들은 LangSmith에 민감한 데이터를 그대로 보내는 실수를 합니다. 사용자의 개인정보나 비밀번호가 로그에 남으면 보안 문제가 됩니다.
따라서 프로덕션 환경에서는 데이터 마스킹을 설정해야 합니다. 또한 LangSmith는 유료 서비스입니다.
무료 플랜은 추적 횟수에 제한이 있으므로, 개발 단계에서만 활성화하고 프로덕션에서는 필요한 경우만 추적하는 것이 좋습니다. 다시 김개발 씨의 이야기로 돌아가 봅시다.
LangSmith 대시보드를 본 김개발 씨는 감탄했습니다. "와, 정말 한눈에 보이네요!
이 노드에서 잘못된 데이터가 들어갔구나." 단 몇 분 만에 버그를 찾은 김개발 씨는 이제 LangSmith 없이는 개발할 수 없을 것 같습니다. 좋은 도구는 개발자의 시간을 절약해줍니다.
실전 팁
💡 - 개발 초기부터 LangSmith를 설정해두면 나중에 디버깅이 훨씬 쉽습니다
- 프로덕션에서는 민감한 데이터를 마스킹하는 것을 잊지 마세요
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Helm 마이크로서비스 패키징 완벽 가이드
Kubernetes 환경에서 마이크로서비스를 효율적으로 패키징하고 배포하는 Helm의 핵심 기능을 실무 중심으로 학습합니다. Chart 생성부터 릴리스 관리까지 체계적으로 다룹니다.
보안 아키텍처 구성 완벽 가이드
프로젝트의 보안을 처음부터 설계하는 방법을 배웁니다. AWS 환경에서 VPC부터 WAF, 암호화, 접근 제어까지 실무에서 바로 적용할 수 있는 보안 아키텍처를 단계별로 구성해봅니다.
AWS Organizations 완벽 가이드
여러 AWS 계정을 체계적으로 관리하고 통합 결제와 보안 정책을 적용하는 방법을 실무 스토리로 쉽게 배워봅니다. 초보 개발자도 바로 이해할 수 있는 친절한 설명과 실전 예제를 제공합니다.
AWS KMS 암호화 완벽 가이드
AWS KMS(Key Management Service)를 활용한 클라우드 데이터 암호화 방법을 초급 개발자를 위해 쉽게 설명합니다. CMK 생성부터 S3, EBS 암호화, 봉투 암호화까지 실무에 필요한 모든 내용을 담았습니다.
AWS Secrets Manager 완벽 가이드
AWS에서 데이터베이스 비밀번호, API 키 등 민감한 정보를 안전하게 관리하는 Secrets Manager의 핵심 개념과 실무 활용법을 배워봅니다. 초급 개발자도 쉽게 따라할 수 있도록 실전 예제와 함께 설명합니다.