
.codex를 복사하는 것만으로는 부족한 이유: 크로스 플랫폼 Codex 세션 마이그레이터 구축하기
요약
Codex 세션을 다른 기기로 옮길 때 발생하는 경로 및 메타데이터 불일치 문제를 해결하기 위한 'Codex Migrate' 구축 과정을 다룹니다. 단순 파일 복사가 아닌 SQLite 인덱스 복구와 크로스 플랫폼 경로 매핑의 중요성을 설명합니다.
핵심 포인트
- 단순 .codex 디렉토리 복사로는 세션과 인덱스가 완벽히 복구되지 않음
- JSONL 데이터, 메타데이터, SQLite 인덱스, 경로 정보의 유기적 결합 필요
- POSIX, Windows, WSL 등 다양한 경로 체계에 대한 매핑 처리 필수
- Rust 기반의 오픈 소스 도구인 Codex Migrate를 통한 해결책 제시
Codex 작업을 컴퓨터 간에 옮길 때, 저는 마이그레이션 과정이 간단할 것이라고 예상했습니다. 기존의 .codex 디렉토리를 새 기기로 복사하고 Codex를 다시 열기만 하면 될 것이라고 생각했죠.
파일들은 그곳에 있었지만, 결과는 제가 기대했던 것이 아니었습니다. 과거 프로젝트들이 안정적으로 나타나지 않았고, 인터페이스에서 세션이 누락되기도 했으며, macOS에 저장된 경로들은 Windows에서 전혀 의미가 없었습니다.
이러한 문제로 인해 저는 로컬 Codex 세션을 마이그레이션, 복구, 백업 및 내보내기 위한 오픈 소스 Rust 데스크톱 애플리케이션이자 CLI인 Codex Migrate를 구축하게 되었습니다.
이 글은 프로젝트를 발표하는 것에 초점을 맞추기보다는, 그 이면에 있는 엔지니어링 문제들에 대해 다룹니다.
첫 번째 잘못된 가정: 파일이 전체 상태이다
Codex 세션은 주로 롤아웃 (rollout) JSONL 데이터로 표현되지만, 단순히 해당 파일들을 새로운 .codex 디렉토리 아래의 어딘가에 배치한다고 해서 애플리케이션이 이를 올바르게 표시한다는 보장은 없습니다.
관련된 여러 상태 요소들이 있습니다:
- 대화 기록을 포함하는 롤아웃 (rollout) JSONL 파일
- 제목, 타임스탬프, 아카이브 상태, 현재 작업 디렉토리와 같은 스레드 메타데이터 (metadata)
- 로컬 애플리케이션에서 사용하는 SQLite 인덱스 (indexes)
- 소스 컴퓨터에서 유효했던 경로 (paths)
이는 중요한 차이를 만들어냅니다:
대화 데이터를 보존하는 것은 사용 가능한 프로젝트와 세션 인덱스를 복구하는 것과 같지 않습니다.
따라서 마이그레이션 도구를 위해, 저는 롤아웃 (rollout) JSONL을 신뢰할 수 있는 원천 (source of truth)으로 취급하는 반면, SQLite는 대상 환경에 필요한 메타데이터를 복구하는 데 사용되는 어댑터 (adapter)로 취급합니다. 소스 데이터베이스는 통째로 복사되지 않습니다.
경로는 문제의 일부이다
다음 경로에서 생성된 세션을 생각해 보십시오:
/Users/alex/Projects/my-app
Windows로 이동한 후, 해당 프로젝트는 다음과 같을 수 있습니다:
D:\Projects\my-app
이전 경로는 단순히 외관상의 문제만이 아닙니다. 이는 세션이 그룹화되고, 표시되며, 다시 열리는 방식에 영향을 미칠 수 있습니다. 마이그레이션 도구는 다음과 같은 여러 경로 체계 (path families)를 이해해야 합니다:
- POSIX 경로 (POSIX paths)
- Windows 드라이브 문자 경로 (Windows drive-letter paths)
- UNC 경로 (UNC paths)
/mnt/d/Projects와 같은 WSL 경로 (WSL paths)
Codex Migrate는 원래의 작업 디렉토리 (working directory)를 기준으로 세션을 그룹화하며, 사용자가 선택한 각 프로젝트를 대상 컴퓨터의 실제 디렉토리에 바인딩 (bind)하도록 요청합니다. 또한 상위 디렉토리 매핑 (parent-directory mapping)도 지원합니다. 만약 /Users/alex/Projects에서 D:\Projects로 모든 프로젝트가 이동했다면, 하나의 규칙으로 전체 트리 (tree)를 매핑할 수 있습니다.
프로젝트가 더 이상 존재하지 않는 세션을 위해 '기록 전용 모드 (history-only mode)'가 있습니다. 이는 원래의 작업 디렉토리가 여전히 존재하는 것처럼 가장하지 않으면서도 대화에 대한 접근 권한을 보존합니다.
마이그레이션 도구는 역사를 암묵적으로 조작해서는 안 됩니다
다음 문제는 충돌 처리 (conflict handling)였습니다. 스레드 UUID (thread UUID)가 대상 컴퓨터에 이미 존재할 수 있으므로, 파일을 맹목적으로 덮어쓰는 것은 안전하지 않습니다.
병합 플래너 (merge planner)는 보수적인 규칙을 사용합니다:
| 소스 및 대상 상태 | 결과 |
|---|---|
| 로컬에 UUID가 존재하지 않음 | 가져오기 (Import) |
| ... |
마지막 케이스가 중요합니다. 동일한 UUID를 가진 두 개의 JSONL 파일은 동기화 또는 수동 복사 이후에 갈라진 (diverged) 기록을 나타낼 수 있습니다. 이들을 자동으로 연결 (concatenating)하면 실제로 일어나지 않은 대화가 생성될 수 있습니다. 새로운 UUID를 생성하는 것은 식별 충돌 (identity conflict)을 해결하기보다 숨기는 결과를 초래합니다.
따라서 첫 번째 버전은 갈라진 기록들을 이어 붙이는 것을 거부합니다.
로컬 데스크톱 유틸리티에서도 트랜잭션 (Transactions)은 중요합니다
세션 마이그레이션은 사용자 데이터를 수정하므로, 부분적으로 완료된 임포트는 허용될 수 없습니다.
임포트 흐름은 애플리케이션 수준에서 트랜잭션 (transactional) 방식으로 이루어집니다:
- 대상 데이터베이스가 사용 중인지 확인합니다.
- SQLite Online Backup 스냅샷을 생성합니다.
- 선택된 롤아웃 (rollout) 파일들을 검증합니다.
- 스테이징 영역 (staging area)에 파일들을 작성합니다.
- 파일들을 최종 위치로 이동합니다.
- 런타임 (runtime)에 감지된 스키마 (schema) 필드만 업데이트합니다.
- 롤아웃 및 데이터베이스 일관성을 확인합니다.
- 단계가 실패할 경우 스냅샷을 복구하고 새 파일들을 제거합니다.
롤백 (Rollback) 스냅샷은 다음 경로에 저장됩니다:
$CODEX_HOME/migration_transactions/<TRANSACTION_ID>/
GUI를 통해 사용자는 이전 스냅샷을 검사, 복구 및 삭제할 수도 있습니다.
이는 직접적인 파일 복사보다 더 많은 작업이 필요했지만, 마이그레이션 소프트웨어에는 명확한 실패 모델 (failure model)이 필요합니다. "대부분의 파일이 복사되었다"는 것은 유효한 성공 상태가 아닙니다.
기존에 동기화된 .codex 디렉터리 지원하기
모든 사용자가 명시적인 마이그레이션을 수행하는 것은 아닙니다. 어떤 사람들은 새 기기의 .codex 디렉터리를 이전 기기의 것으로 교체합니다. 다른 사람들은 두 컴퓨터 간에 이를 동기화하기도 합니다.
이러한 경우에는 세션을 복사할 필요가 없습니다. 데이터는 이미 존재하기 때문입니다. 문제가 되는 것은 현재의 경로 메타데이터 (path metadata)입니다.
이로 인해 별도의 경로 복구 (path repair) 워크플로우가 만들어졌습니다. 이 워크플로우는 로컬 Codex 환경을 스캔하고, 기본적으로 유효하지 않은 경로를 가진 프로젝트들을 표시하며, 사용자가 다음과 같은 작업을 수행할 수 있게 합니다:
- 프로젝트를 개별적으로 매핑
- 상위 디렉터리 매핑 적용
- 매핑 검토 시 모든 프로젝트 포함
- 로컬 메타데이터를 업데이트하기 전에 변경 사항 미리보기
경로 복구를 임포트 (import)와 분리함으로써 모델을 더 이해하기 쉽게 만들었습니다. 즉, 한 작업은 세션 데이터를 이동시키고, 다른 작업은 이미 존재하는 데이터에 대한 참조를 복구합니다.
대화를 내보내는 것은 JSON을 출력하는 것보다 더 복잡합니다
저는 세션이 Codex 외부에서도 읽을 수 있는 상태로 유지되기를 원했습니다. 첫 번째 HTML 익스포터 (exporter)는 또 다른 종류의 문제들을 드러냈습니다:
- 하나의 롤아웃 (rollout)은 동일한 어시스턴트 응답에 대한 여러 표현을 포함할 수 있습니다.
- 도구 이벤트 (tool events)가 중복된 채팅 메시지로 나타나서는 안 됩니다.
- 사용자 및 어시스턴트 메시지는 서로 다른 레이아웃 (layout)이 필요합니다.
- 이미지와 도구 스크린샷은 로컬 경로 또는 구조화된 페이로드 (payload)를 통해 참조될 수 있습니다.
- 휴대 가능한 익스포트 (export)는 별도의 에셋 (asset) 폴더에 의존해서는 안 됩니다.
이제 익스포터는 선택된 세션당 하나의 독립적인 HTML 파일을 생성합니다. 사용자 메시지는 오른쪽에 표시되는 반면, Codex 응답은 더 넓은 왼쪽 레이아웃을 사용합니다. 이미지와 도구 스크린샷은 데이터 URL (data URL)로 직접 임베딩 (embedding)됩니다.
이로 인해 익스포트 파일의 크기는 커지지만, 휴대성 측면의 트레이드오프 (tradeoff)는 그만한 가치가 있습니다. 즉, 하나의 대화가 하나의 파일이 됩니다.
크로스 플랫폼 GUI 세부 사항도 여전히 중요합니다
핵심 로직은 Rust로 작성되었으며, 네이티브 GUI를 위해 egui/eframe을 사용했습니다. 크로스 플랫폼 (cross-platform) 동작은 파일 시스템 로직에만 국한되지 않았습니다.
저는 다음과 같은 부분에서 플랫폼별 차이점을 경험했습니다:
- 폰트 베이스라인 (font baseline) 정렬
- Windows에서의 애플리케이션 아이콘 임베딩 (embedding)
- macOS 번들 서명 (bundle signing)
- 네이티브 폴더 선택
- 검증 중 경로 구분자 (path separators)
- Windows에서의 GUI 대 콘솔 서브시스템 (subsystem) 동작
한 가지 예로, 초기 macOS 릴리스에는 실행 파일에 링커 (linker)가 생성한 애드혹 (ad-hoc) 서명이 포함되어 있었으나, 리소스가 추가된 후에는 완전한 번들 서명이 포함되지 않았습니다. 이로 인해 Gatekeeper는 다운로드된 애플리케이션이 손상되었다고 보고했습니다. 해결 방법은 완성된 앱 번들을 서명하고, 패키징하기 전에 codesign --verify --deep --strict 명령어로 이를 검증하는 것이었습니다.
이는 컴파일 (compilation)에 성공했다고 해서 배포 가능한 데스크톱 애플리케이션이 올바르게 패키징되었다는 것을 의미하지는 않는다는 유용한 교훈을 줍니다.
의도적인 데이터 경계
이 도구는 의도적으로 세션 관련 데이터만 마이그레이션 (migration)합니다. 다음 항목은 제외됩니다:
- 인증 (Authentication)
- 설정 (Configuration)
- 스킬 (Skills)
- 플러그인 (Plugins)
- 로그 및 캐시 (Logs and caches)
- 장치별 상태 (Device-specific state)
이러한 방식은 작업을 이해하기 쉽게 유지하며 컴퓨터 간에 자격 증명 (credentials)을 이동하는 것을 방지합니다. 또한 백업을 더 쉽게 검사할 수 있게 해줍니다. 최소한의 백업은 독점적인 아카이브 형식 대신 Codex 디렉토리와 동일한 기본 구조를 사용합니다.
현재 상태 및 한계
Codex Migrate는 현재 다음 기능을 제공합니다:
- 영어 및 중국어 지원 네이티브 GUI
- 동일한 마이그레이션 엔진을 사용하는 CLI
- macOS, Windows, Linux 및 WSL 경로 처리
- 활성 및 아카이브된 세션 지원
- 선택적 프로젝트 및 세션 가져오기
- 경로 복구 및 상위 디렉토리 매핑
- 충돌 인지 병합 (Conflict-aware merging)
- 트랜잭션 백업 및 롤백 (Rollback)
- 독립형 HTML 내보내기
주요 한계점은 호환성 위험입니다. 로컬 Codex 저장 구조는 버전 간에 변경될 수 있으므로, 이 프로젝트는 하나의 영구적인 SQLite 레이아웃을 가정하는 대신 런타임 스키마 검사 (runtime schema inspection)를 수행합니다. 그럼에도 불구하고, 실제 사례 보고는 여전히 중요합니다.
소스 코드
이 프로젝트는 GitHub에서 MIT 라이선스 하에 이용 가능합니다:
https://github.com/ChenglongLi777/codex-migrate
macOS 및 Windows용 사전 빌드된 릴리스(releases)를 사용할 수 있습니다. 바이너리는 현재 상용 인증서로 서명되지 않았거나 Apple의 공증(notarized)을 받지 않았으므로, 저장소에는 관련 첫 실행 경고가 문서화되어 있으며 SHA-256 체크섬이 게시되어 있습니다.
Codex Migrate는 독립적인 커뮤니티 프로젝트이며 OpenAI와 제휴하거나 OpenAI의 승인을 받지 않았습니다.
만약 운영 체제 간에 Codex 기록을 이동했거나 .codex 디렉토리를 정기적으로 동기화하고 있다면, 여러분이 겪은 예외적인 사례(edge cases)들에 대해 알려주시면 감사하겠습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기