idea_world_labDEV JOURNAL
2026년 6월 22일 월요일

2026 년 6 월 22 일 회고

오늘은 Godot 공식문서 Markdown을 바로 RAG에 넣기보다, 먼저 구조화된 JSONL로 변환해서 확인하는 흐름을 만들었다. 처음에는 공식문서 Markdown을 어떻게 쪼개야 할지 감이 잘 안 왔는데, 변환 UI를 만들어서 파일별 결과를 눈으로 보니 훨씬 수월했다.

Markdown -> JSONL 변환기

Godot 공식문서 Markdown 파일을 업로드하면 Qwen API를 통해 대상 테이블을 분류하고, 결과를 docs_chunks, api_mapping, label_prototypes용 JSONL로 나누는 방식으로 정리했다.

Markdown 업로드와 자동 처리

처음에는 단순히 Markdown을 읽어서 바로 DB에 넣는 식으로 생각했지만, 그렇게 하면 어떤 문서가 어떤 테이블로 가는지 확인하기 어려웠다. 그래서 중간 산출물로 JSONL을 먼저 만들고, 화면에서 변환 결과와 원문 차이를 확인하는 방식으로 바꿨다.

이 방식이 좋은 이유는 다음과 같다.

  • 원본 Markdown과 변환된 JSONL을 비교하면서 볼 수 있다.
  • 한 문서가 docs_chunks, api_mapping, label_prototypes 중 어디로 들어가는지 확인할 수 있다.
  • 잘못 분류된 문서나 비어 있는 결과를 DB에 넣기 전에 걸러낼 수 있다.
  • 나중에 변환 규칙을 바꿔도 JSONL을 다시 생성해서 비교할 수 있다.

변환 결과 확인

변환기는 저장된 JSONL 파일 수와 오류 수를 화면에 보여준다. 오늘 확인한 화면에서는 docs_chunks 쪽 결과가 먼저 쌓였고, api_mapping, label_prototypes, 오류 파일은 아직 비어 있는 상태였다.

저장된 JSONL 결과

이 상태는 이상하다기보다, 초반 문서들이 대부분 설명 문서라서 docs_chunks로 들어가는 것이 자연스러워 보였다. 중요한 것은 결과가 바로 DB로 들어가는 것이 아니라, 먼저 JSONL로 저장되고 사람이 확인할 수 있다는 점이다.

JSONL 미리보기와 테이블 형태 확인

JSONL 결과를 화면에서 JSON 형태와 테이블 형태로 확인할 수 있게 했다. 예를 들어 docs_chunks 레코드는 chunk_id, doc_version, source_url, source_file, source_sha256, doc_type, section_path, heading, content, code_blocks, api_symbols, token_count, metadata 같은 필드를 가진다.

JSONL 미리보기와 테이블

이렇게 보니 그냥 Markdown 파일을 들고 있는 것보다 훨씬 낫다. 특히 chunk_idsource_sha256이 같이 보이니까, 나중에 어떤 청크가 어느 원본에서 나왔는지 추적하기 쉬워졌다. RAG에서 제일 중요한 건 근거가 어디서 왔는지 잃지 않는 것인데, JSONL 중간 산출물이 그 역할을 해줄 수 있을 것 같다.

변환 로그

파일별 변환 로그도 확인했다. 어떤 파일이 시작됐고, Qwen이 어떤 테이블로 분류했으며, 몇 개의 valid record가 나왔는지 볼 수 있었다.

변환 차이와 로그

오늘 확인한 예시에서는 about__complying_with_licenses 문서가 docs_chunks로 분류되고 여러 개의 청크로 변환됐다. 반대로 404 같은 문서는 대상 테이블이 없어서 스킵되는 흐름도 보였다. 이런 로그가 있어야 나중에 1,570개 문서를 모두 처리할 때 어디서 문제가 생겼는지 추적할 수 있다.

로컬 PostgreSQL 세팅

JSONL을 만든 다음에는 로컬 PostgreSQL에 넣을 수 있도록 DB도 정리했다. pgvector/pgvector:pg16 기반으로 컨테이너를 띄우고, docs_chunks, api_mapping, label_prototypes, ingest_reports 테이블을 만들었다.

오늘 특히 중요하게 본 것은 DB 스키마가 JSONL 필드명과 어긋나지 않아야 한다는 점이다. DB가 임의로 필드를 늘리면 JSONL 계약이 흐려지고, 나중에 변환기와 주입기가 서로 다른 기준으로 움직일 수 있다. 그래서 DB payload 컬럼은 JSONL 스키마와 맞추고, DB 운영용 컬럼은 id, embedding, search_tsv, created_at 정도로만 최소화했다.

로컬 DB를 실제로 구동했고, 샘플 JSONL 형태의 insert와 검색도 rollback 테스트로 확인했다. 아직 전체 문서를 DB에 밀어넣은 것은 아니지만, JSONL -> PostgreSQL로 이어지는 길은 훨씬 선명해졌다.

오늘의 판단

오늘 만든 흐름은 최종 RAG 판별기 자체는 아니다. 하지만 지금 단계에서는 꽤 중요한 진전이다.

이전에는 공식문서 Markdown을 어떻게 다뤄야 할지 애매했고, 바로 청킹이나 DB 주입으로 넘어가면 또 구조가 흐려질 위험이 있었다. 오늘은 그 사이에 JSONL 변환/검증 단계를 두면서, 사람이 확인 가능한 중간 산출물을 만들었다.

결론적으로 앞으로의 흐름은 이렇게 잡는 게 맞아 보인다.

Godot 공식문서 Markdown
-> JSONL 변환
-> JSONL 미리보기/검증
-> PostgreSQL 주입
-> Retriever 검색 검증
-> Validator/Qwen 응답 정리

내일 이후에는 전체 1,570개 문서를 끝까지 변환했을 때 docs_chunks, api_mapping, label_prototypes가 어떤 비율로 나오는지 확인해야 한다. 특히 api_mappinglabel_prototypes는 Qwen이 마음대로 만들면 안 되므로, 자동 생성 결과를 그대로 믿지 않고 승인/검증 단계를 따로 둬야 한다.

처리 속도 측정

추가로 변환 속도를 계산해보니 전체 1,570개 Markdown을 모두 JSONL로 변환하는 데 약 42시간 정도 걸릴 것으로 보인다.

현재까지의 실제 처리 시간은 약 1시간 9분 정도였다. 이 시간 동안 done 39개와 deferred 4개를 합쳐 총 43개 파일이 처리됐다. 평균 속도는 파일 1개당 약 1.6분 정도다.

실제 처리 시간: 약 1시간 9분
현재 처리된 파일: done 39개 + deferred 4개 = 43개
평균 속도: 파일 1개당 약 1.6분
전체 1,570개 변환 예상 시간: 약 42시간

처음에는 체감상 1분에 1개 정도라고 생각했지만, 실제 로그 기준으로 계산하면 그보다 조금 더 오래 걸린다. 오늘 안에 1,500개 전체 변환을 끝내기는 어려울 수 있으므로, 처리 속도와 실패/보류 파일 수를 계속 기록하면서 진행해야 한다.

다음 검증 계획

내일과 모레는 시간이 안 될 가능성이 높아서 바로 이어서 작업하지 못할 것 같다. 그래도 나중에 다시 이어서 할 작업은 어느 정도 정리됐다.

먼저 수집과 변환 도중 이미 생성된 JSONL을 로컬 PostgreSQL에 밀어넣어야 한다. 지금까지 만든 변환기는 Markdown을 docs_chunks, api_mapping, label_prototypes용 JSONL로 나누는 데 초점이 있었고, 다음 단계는 이 JSONL을 실제 DB에 넣은 뒤 검색 가능한 상태인지 확인하는 것이다.

그 다음에는 docs/roadmaps/2026-06-21-initial-rag-classifier-architecture.md에 작성했던 workflow 흐름을 Python 스크립트로 작게 검증해볼 예정이다.

source code
-> AST Parser
-> Retriever
-> evidence JSONL / evidence bundle
-> Qwen 3.6 API 호출
-> 응답 확인

즉, 임의의 Godot 소스코드를 Python 스크립트에 넣고, AST Parser가 심볼과 버전 신호를 뽑는지 확인한다. 그 결과를 Retriever에 넣어서 PostgreSQL에서 관련 공식문서 청크나 API 매핑을 가져오게 하고, 반환받은 JSONL/evidence bundle을 Qwen 3.6 API에 전달했을 때 어떤 응답이 나오는지 중간중간 확인할 계획이다.

이 과정은 최종 자동화가 아니라, 각 단계가 실제로 이어지는지 확인하는 작은 end-to-end 검증에 가깝다. 특히 Qwen이 기억으로 답하는지, 아니면 Retriever가 넘긴 근거를 바탕으로 답하는지 확인해야 한다.

공개 레포와 비공개 전환에 대한 생각

최근에 이 레포지토리를 한 번 공개했다가 다시 비공개로 전환했다. 이유는 단순했던 것 같다. 내 실력을 보여주고 싶지 않았다.

지금의 내 실력은 아직 형편없다고 느껴진다. 그런데 동시에 Godot 전용 모델을 만들고, Hugging Face에 업로드하고, 그것을 시작점으로 강의 영상, 대학, 포트폴리오, 명예 같은 여러 영역으로 확장하고 싶다는 생각도 했다. 어떻게 보면 너무 꿈같은 망상처럼 보인다. 그래도 내가 남긴 회고와 작업 기록을 누군가가 발판 삼아 성장할 수 있다면, 나도 그 과정에서 더 빠르게 성장할 수 있지 않을까 하는 생각이 들었다.

사실 아직도 공개하기 싫은 마음이 있다. 내가 만든 것들이 깊이가 없어 보일까 봐 두렵기도 하고, 반대로 여러 지식을 얕게나마 엮어서 쉬워 보이게 만들어낸 부분이 누군가에게 그대로 털릴까 봐 경계했던 것 같기도 하다. 결과물이 엄청난 기술이라서라기보다, 내가 지금까지 고민하고 연결한 흔적이 통째로 드러나는 것이 부담스러웠던 것 같다.

PR이나 CI/CD 파이프라인도 세팅했지만, 원래 생각했던 흐름은 GitHub workflow에서 PR이 올라오면 Oracle Cloud에 띄워둔 로컬 LLM endpoint가 자동으로 PR 리뷰를 해주는 구조였다. Oracle Cloud 쪽은 24GB VRAM 환경이라 웬만한 로컬 모델은 돌릴 수 있을 것 같아서, 호스팅된 LLM을 코드 리뷰 자동화에 붙이는 쪽을 생각했다. 다만 Oracle Cloud 계정을 분실해서 이 흐름은 당분간 다시 쓰기 어렵다. RunPod도 PR 검증용으로 쓰기에는 작업할 때마다 세팅을 다시 해야 하는 번거로움이 있다. 그래서 당장은 수동 작업과 문서화 중심으로 진행하고, LLM 기반 PR 리뷰 자동화는 나중에 다시 잡는 편이 현실적일 것 같다.

결론은 이거인 것 같다. 사실 아무도 안 볼 가능성이 큰데도, 나는 털리는 것에 대한 경계가 매우 심했다. 하지만 설령 누군가가 내 작업을 참고하거나 가져간다 해도, 그걸 발판 삼아 나도 더 뛰어나져야 한다. 공개 여부는 여전히 조심스럽게 결정해야 하지만, 두려움 때문에 기록 자체를 멈추지는 말아야겠다.