본문 바로가기
카테고리 없음

LangGraph.js 1.5.0으로 올릴 때 PostgresSaver setup을 한 번만 실행해야 하는 이유

by Juelria 2026. 10. 27.
728x90
반응형
SMALL

LangGraph.js의 @langchain/langgraph가 2026년 10월 12일에 1.5.0으로 올라왔어요. 병렬 노드가 같은 상태 필드에 쓴 값을 어떤 순서로 적용하고 다시 재생할지가 바뀌었고, 그 때문에 체크포인트 저장소의 테이블 구조가 달라졌어요. Postgres에 체크포인트를 저장하고 있다면 배포 순서를 잘못 잡았을 때 읽기와 쓰기가 모두 실패해요. 릴리스 노트에서 확인한 변경점과 올리기 전에 할 일을 순서대로 적어 볼게요.

1.5.0에서 실제로 바뀐 것

릴리스 노트에 따르면 한 단계 안에서 둘 이상의 작업이 같은 DeltaChannel에 쓰면, 예전에는 작업 ID 순서로 적용했어요. 작업 ID는 체크포인트 ID가 섞인 해시라서 실행할 때마다 병렬 작업의 순서가 뒤바뀔 수 있었다고 해요. 1.5.0부터는 작업 경로, 작업 ID, 인덱스 순서로 적용하고 다시 재생해요. 파이썬 LangGraph가 쓰던 순서와 맞춘 거예요.

이 순서를 저장소가 기억해야 해서 저장소 계약도 달라졌어요. BaseCheckpointSaver.putWrites는 선택 인자 taskPath를 받고, getTuple과 list는 pendingWrites를 새 정렬 키 순서로 돌려줘야 해요. 메모리, SQLite, Postgres, Redis, MongoDB 저장소는 모두 이 값을 저장하도록 바뀌었어요. 같은 날 @langchain/langgraph-checkpoint도 1.2.0으로 올라왔고, 새 저장소 패키지들은 이 버전 이상을 요구해요.

Postgres는 setup 순서가 전부예요

task_path 열은 PostgresSaver가 세상에 나온 뒤 처음 생긴 마이그레이션이에요. 릴리스 노트에는 새 버전이 트래픽을 받기 전에 setup()을 한 프로세스에서 한 번 실행하라고 적혀 있어요. 실행하기 전에는 column ""task_path"" does not exist 오류로 읽기와 쓰기가 실패해요. 마이그레이션이 필요한 데이터베이스에서 여러 프로세스가 동시에 setup()을 호출하면 checkpoint_migrations에서 중복 키 오류가 날 수 있다는 경고도 있어요.

파드 여러 개가 뜰 때마다 setup()을 부르는 구성이 가장 위험해요. 시작 코드에서 setup()을 빼서 배포 전에 한 번 도는 작업으로 옮기면 돼요.

import { PostgresSaver } from ""@langchain/langgraph-checkpoint-postgres"";

const checkpointer = PostgresSaver.fromConnString(process.env.DATABASE_URL!);
await checkpointer.setup(); // 새 버전 배포 전에 마이그레이션 작업에서 한 번만
await checkpointer.end();

앱 서버는 fromConnString으로 연결한 저장소를 compile({ checkpointer })에 넘기기만 해요. setup()은 마이그레이션 작업에만 둬요. 마지막 줄의 end()는 작업이 끝난 뒤 연결을 닫아 프로세스가 멈춰 있지 않게 하려고 넣었어요.

진행 중인 스레드를 먼저 정리하세요

릴리스 노트에서 가장 신경 쓸 문장은 이거예요. 저장된 델타 쓰기가 있는데 끝나지 않은 단계, 그러니까 인터럽트와 작업 오류, 중단, 드레인으로 멈춘 단계가 있으면 업그레이드의 영향을 받아요. 그런 스레드는 쓰는 쪽을 올리기 전에 예전 런타임으로 끝내라고 해요. 끝난 형제 노드 없이 인터럽트된 노드 하나만 남은 경우는 그대로 이어 실행해도 안전하다고 해요.

사람 승인을 기다리며 몇 시간씩 멈춰 있는 스레드가 많은 서비스라면 이 부분이 일정을 좌우해요. 병렬 노드가 둘 이상인 그래프에서 승인 대기에 걸린 스레드 수부터 세어 보세요.

경로 모양도 바뀌었어요. Send가 만드는 작업 경로와 노드 오류 핸들러의 작업 경로가 파이썬과 같은 모양이 되면서 langgraph_path 메타데이터 값이 달라져요. Send나 오류 핸들러 아래에서 task()를 호출하는 함수형 코드는 작업 ID도 함께 바뀌어요. 이 경로를 로그나 대시보드의 필터로 쓰고 있다면 배포 전에 조건을 확인하세요.

1.5.1과 1.5.2까지 같이 보기

1.5.0이 나오고 4일 뒤인 10월 16일에 1.5.1이 나왔어요. 병렬 인터럽트를 하나씩 재개할 때 이미 끝난 작업의 출력이 두 번 적용되던 문제와, 일부 답만 응답 스키마 검증에 실패하면 나머지 작업이 중간에 취소되던 문제가 고쳐졌어요. 오류는 그대로 ZodError로 나가고, 새로 생긴 isInvalidResume(error)로 다른 오류와 구분할 수 있어요. 스트리밍으로 돌린 그래프가 체크포인트에 남기는 AIMessage도 invoke 때와 같아졌어요.

10월 21일의 1.5.2는 @langchain/protocol 의존성을 맞추는 패치예요. 같은 날 나온 @langchain/langgraph-checkpoint-postgres 1.1.2는 약 61MB보다 큰 값이 든 체크포인트를 읽을 때 프로세스가 invalid array length로 죽던 문제를 고쳤어요. 대용량 도구 출력을 상태에 통째로 담아 두는 앱이라면 이 패치까지 올려야 안심할 수 있어요.

올리는 순서

  1. 대기 중인 인터럽트 스레드와 병렬 노드가 있는 그래프를 확인해요.
  2. 스테이징 데이터베이스에서 마이그레이션 작업으로 setup()을 한 번 실행해요.
  3. 스테이징에서 인터럽트 후 재개와 병렬 노드 실행을 한 번씩 돌려요.
  4. @langchain/langgraph는 1.5.2, Postgres 저장소는 1.1.2 이상으로 맞추고 운영 데이터베이스에 같은 작업을 한 번 돌려요.
  5. 운영 앱을 배포해요.

서드파티 체크포인트 저장소를 쓰고 있다면 넘겨받은 taskPath를 저장하고 새 정렬 키 순서로 돌려주는지부터 확인하세요. 무시하는 저장소는 작업 ID 순서로 재생해서 실행 때와 다른 채널 값을 만들 수 있다고 릴리스 노트가 경고해요.

참고한 자료

728x90
반응형
LIST