# 03. 데이터 파이프라인 ## 목표 PDF 업로드부터 문제 저장까지의 흐름을 자동화하는 것이 이 파이프라인의 목표입니다. 사용자는 PDF만 업로드하지만, 내부에서는 여러 단계를 거쳐 문제 데이터가 만들어집니다. ## 전체 흐름 1. 사용자가 Streamlit에서 PDF를 업로드한다. 2. 업로드 정보로 `IngestionJob`을 만든다. 3. Airflow DAG가 긴 PDF 처리 작업을 백그라운드에서 실행한다. 4. Airflow 작업 안에서 PDF Ingestion Graph가 실행된다. 5. PDF를 텍스트와 문제 단위로 파싱한다. 6. 파싱 결과의 품질을 검사한다. 7. 품질 게이트를 통과하면 문제를 PostgreSQL에 저장한다. 8. 필요하면 문제를 Chroma에 색인한다. 9. 사용자는 웹에서 저장된 문제를 풀 수 있다. ![데이터 파이프라인 흐름](assets/data-pipeline-flow.png) ## 단계별 설명 ### 1. PDF 업로드 사용자는 Streamlit의 PDF 업로드 화면에서 시험명과 PDF 파일을 입력합니다. 이때 PDF 자체는 런타임 데이터 영역에 저장되고, 데이터베이스에는 처리 작업 정보가 저장됩니다. ### 2. IngestionJob 생성 `ingestion_jobs` 테이블에는 PDF 처리 작업의 상태가 저장됩니다. 예를 들어 아래 같은 정보를 가집니다. - 시험명 - PDF 경로 - 현재 상태 - 현재 단계 - 진행률 - 저장된 문제 수 - 품질 점수 - 오류 메시지 이 테이블이 있기 때문에 사용자는 PDF 처리 중 현재 어디까지 진행됐는지 확인할 수 있습니다. ### 3. Airflow DAG 실행 PDF 처리는 시간이 오래 걸릴 수 있습니다. 그래서 웹 화면 안에서 모든 처리를 끝내기보다 Airflow DAG로 넘기는 구조를 사용합니다. 현재 PDF 처리 DAG는 `dags/cert_study_pdf_ingestion_dag.py`에 있습니다. Airflow는 `job_id`를 받아 해당 작업을 실행합니다. Airflow의 핵심 역할은 직접 문제를 저장하는 것이 아니라, 긴 처리 작업을 실행하고 실패 지점을 추적할 수 있게 만드는 것입니다. 실제 파싱, 품질검증, 저장 흐름은 앱 코드의 PDF Ingestion Graph에서 단계별로 실행됩니다. ### 4. PDF Ingestion Graph 실행 실제 PDF 처리 흐름은 `cert_study_app/graphs/pdf_ingestion_graph.py`에 정의되어 있습니다. 현재 단계는 다음과 같습니다. 1. `coordinator_start` 2. `parse_pdf` 3. `parse_quality` 4. `quality_gate` 5. `ingest_questions` 6. `classify_questions` 7. `visual_questions` 8. `validate_questions` 9. `coordinator_finish` 이렇게 단계를 나누면 실패 지점을 추적하기 쉽고, 나중에 특정 단계만 개선하기도 쉽습니다. ### 5. PDF 파싱 PDF에서 텍스트를 추출하고 문제 단위로 나눕니다. 파싱 결과에는 보통 아래 정보가 포함됩니다. - 문제 본문 - 보기 - 정답 - 해설 - 문제 번호 - 페이지 - 원본 텍스트 - 이미지 경로 LLM은 모든 단계에서 무조건 쓰는 것이 아니라, 구조화가 어렵거나 보정이 필요한 단계에서 선택적으로 사용하는 방향입니다. ### 6. 파싱 품질 검증 파싱된 문제가 바로 저장되면 잘못 나뉜 문제도 데이터베이스에 들어갈 수 있습니다. 이를 줄이기 위해 저장 전에 품질 리포트를 만듭니다. 검사 예시: - 문제 본문이 비어 있는가 - 보기 개수가 너무 적은가 - 정답이 비어 있는가 - 문제 번호가 이상한가 - 같은 문제가 중복으로 들어왔는가 - 청킹 결과가 너무 짧거나 긴가 ### 7. Quality Gate 품질 게이트는 “이 결과를 바로 저장해도 되는가”를 판단하는 단계입니다. 품질이 너무 낮으면 저장을 보류하고, 사람이 확인하거나 파서를 개선할 수 있도록 합니다. 이 프로젝트에서는 사람이 모든 문제를 직접 검수하는 방식보다, 규칙 기반 검증과 제한적인 LLM 활용으로 자동 검수 비율을 높이는 방향을 목표로 합니다. 품질 게이트를 통과하지 못한 결과는 바로 PostgreSQL에 넣기보다 검수 필요 상태로 남기는 것이 좋습니다. 이렇게 해야 잘못 청킹된 문제가 실제 문제풀이 화면에 섞이는 것을 줄일 수 있습니다. ### 8. PostgreSQL 저장 품질 게이트를 통과한 문제는 `questions` 테이블에 저장됩니다. 사용자가 문제를 풀면 풀이 기록은 `attempts` 테이블에 저장됩니다. 개념 정리 데이터는 `concept_notes` 테이블에 저장됩니다. ### 9. Chroma 색인 유사 문제 검색을 위해 문제 내용을 임베딩으로 변환해 Chroma에 저장합니다. 이때 PostgreSQL의 문제 ID와 Chroma의 벡터 데이터가 연결되어 있어야 합니다. 그래야 유사 검색 결과를 다시 실제 문제 화면으로 가져올 수 있습니다. ## 배포용 Seed 생성 Hugging Face Space에서는 로컬 PostgreSQL DB, Chroma 인덱스, PDF 원본을 직접 올리지 않습니다. 대신 배포 시 바로 문제풀이를 확인할 수 있도록 seed 데이터를 생성합니다. 현재 배포용 seed 생성 명령은 다음과 같습니다. ```bash python scripts/export_hf_seed.py ``` 이 스크립트는 기본적으로 아래 두 데이터를 합쳐 사용합니다. - `data/Json/questions.json`: 문제, 보기, 정답, 해설이 정리된 기준 데이터 - `data/parsed_json/reparsed_multipage_images_20260601_205501.json`: 공통 지문과 공통 지문 원문 이미지 정보 생성 결과: - `cert_study_app/demo_data/questions_seed.json` - `static/question_assets/` 현재 seed에는 321문항이 들어 있으며, 공통 지문이 있는 문항은 긴 지문과 원문 이미지 경로가 함께 저장됩니다. Yes/No 진술형 문제는 화면에서 진술별 `예 / 아니오`로 풀 수 있도록 정답이 `Y,N,Y` 같은 형태로 정규화됩니다. ## 파이프라인에서 중요한 점 ### 저장 전에 품질을 확인한다 파싱 결과가 잘못된 상태로 저장되면 나중에 수정하기 어렵습니다. 그래서 저장 전 품질 검증이 중요합니다. ### 긴 작업은 Airflow가 맡는다 PDF 처리, 이미지 분석, 색인은 시간이 오래 걸릴 수 있습니다. 이런 작업은 웹 요청 안에서 모두 처리하기보다 Airflow로 분리하는 것이 안정적입니다. ### LLM 사용은 최소화한다 LLM은 느리고 비용이 들 수 있습니다. 따라서 기본적인 검증은 규칙과 구조화된 데이터 검사로 처리하고, 꼭 필요한 부분에서만 LLM을 사용하는 방향이 좋습니다.