Migrating data from Postgres to Convex
Quick Summary
Postgres 데이터를 Convex로 이전하는 방법은 소규모 데이터의 JSONL 덤프·가져오기에서 시작해, 스키마 정의와 관계 필드의 Convex ID 전환, 필요 시 Airbyte 기반 스트리밍 가져오기까지 이어진다.
🖼️ 인포그래픽
🖼️ 4컷 인포그래픽
💡 한 줄 요약
Postgres 데이터를 Convex로 이전하는 방법은 소규모 데이터의 JSONL 덤프·가져오기에서 시작해, 스키마 정의와 관계 필드의 Convex ID 전환, 필요 시 Airbyte 기반 스트리밍 가져오기까지 이어진다.
📌 핵심 요약
- 이 글은 Supabase, Neon, RDS 등 어떤 제공자의 Postgres에서도 Convex로 데이터를 옮기는 절차를 설명한다.
- 데이터가 몇 기가바이트 미만으로 비교적 작다면 psql에서 테이블을 JSONL 파일로 덤프한 뒤 Convex 프로젝트에서 import 명령으로 가져올 수 있다.
- 가져온 데이터는 Convex 대시보드에서 확인할 수 있으며, 이후 defineSchema와 defineTable, v 검증자를 사용해 스키마를 작성한다.
- 기존 Postgres의 외래키나 관계 필드는 Convex의 v.id("table") 방식으로 전환하는 것이 권장되며, 전환 중에는 기존 문자열 ID와 Convex ID를 함께 허용하는 단계적 접근을 사용한다.
- 모든 관계 필드가 유효한 Convex ID로 바뀐 뒤에는 스키마를 v.id 검증자로 좁히고, 대용량 또는 지속적 동기화가 필요하면 Airbyte 통합을 대안으로 검토한다.
🧩 주요 포인트
- 이 글은 Supabase, Neon, RDS 등 어떤 제공자의 Postgres에서도 Convex로 데이터를 옮기는 절차를 설명한다.
- 데이터가 몇 기가바이트 미만으로 비교적 작다면 psql에서 테이블을 JSONL 파일로 덤프한 뒤 Convex 프로젝트에서 import 명령으로 가져올 수 있다.
- 가져온 데이터는 Convex 대시보드에서 확인할 수 있으며, 이후 defineSchema와 defineTable, v 검증자를 사용해 스키마를 작성한다.
- 기존 Postgres의 외래키나 관계 필드는 Convex의 v.id("table") 방식으로 전환하는 것이 권장되며, 전환 중에는 기존 문자열 ID와 Convex ID를 함께 허용하는 단계적 접근을 사용한다.
- 모든 관계 필드가 유효한 Convex ID로 바뀐 뒤에는 스키마를 v.id 검증자로 좁히고, 대용량 또는 지속적 동기화가 필요하면 Airbyte 통합을 대안으로 검토한다.
🧠 상세 정리
1. 마이그레이션 가이드의 범위
글은 Postgres에서 Convex로 데이터를 옮기려는 사용자를 대상으로 하며, 특정 Postgres 제공자에 한정하지 않는다. Supabase, Neon, RDS 등 어떤 환경의 Postgres 인스턴스라도 출발점이 될 수 있다고 설명한다. 전체 흐름은 먼저 데이터를 내보내고, Convex 프로젝트에 가져온 뒤, 스키마와 관계 필드를 Convex 방식에 맞게 정리하는 순서로 구성된다. 단순한 파일 기반 이전과 더 큰 규모의 스트리밍 이전을 구분해, 상황에 따라 다른 경로를 선택할 수 있게 안내한다.
2. psql을 이용한 Postgres 데이터 내보내기
비교적 작은 데이터, 즉 몇 기가바이트 미만의 데이터라면 psql을 통해 파일로 덤프하는 방식이 가능하다고 설명한다. 예시는 people 테이블을 대상으로 하며, psql에서 Postgres 인스턴스에 연결한 뒤 row_to_json을 사용해 각 행을 JSON 객체로 변환한다. 그 결과를 \copy 명령으로 지정한 경로의 people.jsonl 파일에 저장하며, 형식은 JSONL이다. 이 단계는 Convex로 가져오기 전에 테이블 단위 데이터를 이동 가능한 파일 형태로 만드는 준비 과정이다.
3. Convex 프로젝트 생성과 데이터 가져오기
이미 Convex 프로젝트가 있다면 해당 프로젝트 디렉터리로 이동하면 되고, 없다면 npm create convex@latest 명령으로 새 프로젝트를 만들 수 있다. 프로젝트 디렉터리 안에서는 npx convex import 명령을 사용해 JSONL 파일을 Convex 배포 환경으로 가져온다. 예시 명령은 --format jsonLines, --replace, --table people 옵션을 지정해 people.jsonl을 people 테이블에 반영한다. 가져오기가 끝난 뒤에는 npx convex dashboard 명령으로 대시보드를 열어 새 데이터가 들어왔는지 확인할 수 있다.
4. Convex 스키마 정의와 생성 보조
데이터를 가져온 뒤에는 Convex 프로젝트에 스키마를 추가할 수 있으며, 글은 Convex 문서의 스키마 작성 가이드를 참조하라고 안내한다. 예시 스키마는 defineSchema와 defineTable을 사용하고, people 테이블의 id, name, pseudonym 필드를 모두 v.string()으로 정의한다. 대시보드에는 데이터 기반으로 스키마를 자동 생성하는 버튼이 있다는 팁도 제공된다. 또한 psql에서 \d people 명령으로 Postgres 스키마를 덤프한 뒤, 이를 LLM에 제공해 Convex 스키마 작성을 보조받을 수 있다고 설명한다.
5. 테이블 관계를 Convex ID로 다시 연결하기
다른 테이블의 문서를 참조하는 필드가 있다면 Convex에서는 v.id("people") 같은 형태로 표현하는 것이 Convex다운 방식이라고 설명한다. 이 필드는 임의의 문자열이 될 수 없고, 실제 대상 문서의 person._id 값이어야 한다. 기존 ID를 계속 사용하고 해당 필드에 인덱스를 둘 수도 있지만, v.id를 사용하면 런타임 검증자가 ID가 올바른 테이블을 가리키는지 확인하는 이점을 얻을 수 있다. 글은 기존 기본키를 먼저 가져오고, 그 필드에 인덱스를 만든 뒤, 관계 필드를 문자열과 Convex ID를 모두 허용하는 union 형태로 바꾸는 단계적 절차를 제안한다.
6. 관계 필드 마이그레이션과 최종 검증
관계 전환 과정에서는 기존 문자열 ID와 Convex ID를 모두 처리할 수 있는 조회 함수를 작성한다. 예시 함수는 ctx.db.normalizeId로 입력값이 people 테이블의 Convex ID인지 확인하고, 맞으면 db.get으로 가져오며, 아니면 id 인덱스를 통해 기존 id 필드로 문서를 찾는다. 이후 migration을 작성해 blogPosts 테이블을 순회하며 authorId가 가리키는 사람 문서를 찾고, 필요하면 author._id로 관계 필드를 갱신한다. 마지막에는 blogPosts의 authorId 스키마를 v.id("people")로 좁히며, 스키마 검증 덕분에 모든 blog post의 authorId가 유효한 Convex ID일 때만 배포가 성공한다고 설명한다.
7. 대용량 또는 지속적 이전을 위한 Airbyte 대안
글은 파일 덤프 방식이 모든 상황의 유일한 방법이라고 설명하지 않고, 더 큰 데이터나 지속적인 스트리밍 이전이 필요한 경우를 별도로 다룬다. 이런 경우에는 Airbyte 통합을 사용해 볼 수 있으며, 자세한 내용은 Convex의 Airbyte 관련 페이지를 확인하라고 안내한다. 즉, 소규모 이전은 psql 덤프와 convex import로 단순하게 처리하고, 대규모 또는 연속 동기화 성격의 이전은 별도 통합을 검토하는 구조다. 마지막에는 Convex가 클라우드 함수, 데이터베이스, 파일 저장소, 스케줄링, 워크플로, 벡터 검색, 실시간 업데이트를 함께 제공하는 백엔드 플랫폼이라고 소개한다.
🧾 핵심 주장 / 시사점
- 이전 작업의 핵심 난점은 단순 데이터 복사보다 관계 필드를 Convex의 _id 체계에 맞게 안전하게 전환하는 데 있다.
- 기존 문자열 ID와 Convex ID를 union으로 함께 허용하는 중간 스키마는 데이터가 완전히 전환되기 전에도 애플리케이션과 마이그레이션을 단계적으로 운영할 수 있게 한다.
- Convex의 스키마 검증은 최종 스키마와 저장 데이터의 불일치를 배포 단계에서 막아 주는 안전장치로 활용된다.
✅ 액션 아이템
- JSONL 덤프 후 Convex import로 소규모 Postgres 데이터를 이관하고, Supabase·Neon·RDS 구분 없이 대시보드에서 적재 결과를 확인한다.
- defineSchema·defineTable·v 검증자를 우선 적용해 기본 스키마를 정리하고, 관계 필드는 Convex v.id("table") 방식으로 단계적으로 전환한다.
- 문자열 ID와 Convex ID를 병행 허용하는 방식으로 관계 데이터를 점진적으로 이동한 뒤, 전환 완료 시 v.id 검증자로 스키마를 좁혀 일관성을 고정한다.
❓ 열린 질문
- 데이터가 몇 기가바이트 미만인지의 경계값은 어떤 기준으로 정해 수동 JSONL import 경로를 택해야 할 것인가?
- 관계 전환 시 문자열 ID와 Convex ID 병행 허용의 유효 기간은 어떻게 설계해야 참조 무결성 리스크를 최소화할 수 있는가?
- 대용량 혹은 지속 동기화가 필요한 경우 Airbyte 도입을 언제 결정하며, 동기화 지연은 어떤 수준에서 허용 가능한 것으로 평가할 것인가?