대규모 언어모델(LLM) 기반 애플리케이션 개발에서 복잡한 상태 관리와 흐름 제어를 위해 LangGraph와 같은 프레임워크 도입을 고려하는 경우가 많습니다. 하지만 '상태 관리가 깔끔해질 것'이라는 기대와 달리, 오히려 코드가 더 복잡해지는 예상치 못한 결과를 초래할 수 있다는 경험담이 공유되어 주목받고 있습니다. 이는 프레임워크의 도입 자체가 만능이 아니며, 그 활용 방식에 따라 오히려 개발 효율을 저해할 수 있음을 시사합니다.
이번 사례의 핵심 문제는 LangGraph의 핵심 기능인 조건부 엣지(conditional edges), 내장 체크포인터(built-in checkpointer), 휴먼-인-더-루프(Human-in-the-Loop) 등을 제대로 활용하지 않고 껍데기만 도입했다는 점입니다. 구체적으로는 그래프에 노드가 하나뿐인 'current_step → END' 구조로 조건부 분기가 없었고, LangGraph의 `invoke()`가 사실상 함수를 직접 호출하는 것과 다름없었습니다. 또한, LangGraph의 인메모리 `MemorySaver`와 자체 PostgreSQL `Checkpointer`가 동일 세션을 이중으로 저장·복원하며 데이터 불일치를 야기했고, 8,700여 줄에 달하는 3개 LangGraph 실행 코드(Executor)에서 상태 관리, 조건 분기, 엣지 케이스 처리 로직이 중복되는 문제도 발생했습니다. 이는 대부분 '바이브 코딩(즉흥적 코딩)' 방식으로 개발이 진행되면서, 프레임워크의 설계 원칙이나 내부 코드를 깊이 들여다보지 않고 요구사항에 맞춰 부분 최적화만 반복한 결과로 분석됩니다.
이러한 실패 경험을 통해 얻은 교훈은 프레임워크가 무엇을 책임지도록 설계되었는지에 대한 정확한 이해와 활용 구조가 필수적이라는 것입니다. 문제 해결을 위해 독립 토폴로지(independent topology)와 `thread_id` 격리를 통한 세션 분리, 상태(State)에는 메타데이터만 두고 본문은 별도 스토어(Store)로 이동, 정규식 파싱(regex parsing) 대신 네이티브 `tool_use` 사용, 그리고 테스트 가능한 노드 분리 등의 개선이 이루어졌습니다. 이 사례는 단순히 '어떻게 쓰는가'를 넘어 '어떻게 잘못 썼는가'를 통해 프레임워크 도입 시 개발자들이 흔히 겪을 수 있는 시행착오와 그 해결 과정을 명확히 보여주며, LLM 애플리케이션 개발의 복잡성 속에서 올바른 설계와 프레임워크 활용의 중요성을 다시 한번 일깨워줍니다.