콘텐츠로 이동

ARK-1139: 데이터 플랫폼 리뉴얼

Jira: ARK-1139 | 상태: In Progress | 담당: 백재현 | 설계 보고서: data-platform-design.vercel.app

최종 업데이트: 2026-03-27


문제의식 — 왜 지금 바꿔야 하는가

현재 Data Platform의 설계는 "AI가 먼저 제안하고 사람이 고른다" 는 가정 위에 세워졌다. 하지만 실제 사용 패턴을 보면 이 가정이 틀렸음을 알 수 있다.

1. 자동 제안(Proposal)의 함정

데이터 소스를 연결하면 Agent가 자동으로 proposal을 생성한다. 얼핏 편리해 보이지만 사용자의 의도가 시스템 안으로 들어가는 입구 자체가 없다.

흐름 비교

사용자가 원하는 것: "이 DB의 거래 테이블로 월별 매출 추이 CM을 만들고 싶다"

현재 시스템: Agent가 자동으로 여러 proposal 생성 → 사용자가 그 중에서 선택 → 원하는 게 없으면 다시 요청

Agent가 먼저 추측하고, 사용자는 그 추측에 반응하는 수동적인 위치에 놓인다. 협업이 아니라 correction의 반복이 된다.

2. "Source가 주인공"인 구조의 경직성

현재 구조 Source → Proposals (1:N) 은 Source를 중심에 두지만, 실제 작업 단위와 맞지 않는다.

현재 구조는 "설계서(proposal)"를 "실행 단위(pipeline)"로 오용하고 있다.

  • 같은 Source에서 변환 로직이 다른 CM 2개를 만들고 싶다면? → 독립 Pipeline 2개가 있어야 한다
  • Pipeline을 일시정지하거나 재실행하고 싶다면? → Proposal에는 상태 개념이 없다
  • Pipeline 실행 이력을 추적하고 싶다면? → Proposal은 설계서지 실행 단위가 아니다

3. Schedule이 잘못된 곳에 묶여 있다

Schedule의 본질적인 의미는 "이 데이터 소스를 언제 갱신할 것인가" 다. Proposal을 선택했냐 안 했냐는 Schedule과 무관해야 한다.

올바른 모델: Dataset 단위로 schedule 설정 → 갱신 시 연결된 Pipeline 자동 트리거

4. 파일 업로드 부재 — 사용 케이스의 절반을 놓치고 있다

DB 커넥션만 지원하면 실제 사용자가 활용하는 데이터의 상당 부분을 커버할 수 없다.

  • 리서치 하우스의 리포트 데이터 (CSV)
  • 특수 데이터 벤더의 커스텀 포맷
  • 팀 내부에서 직접 가공한 데이터셋

파일만 있으면 CM을 만들 수 있어야 한다. 이 능력이 없으면 플랫폼의 접근성 자체가 제한된다.

5. 팀 사일로 — 좋은 데이터가 팀 안에 갇힌다

각 팀이 만든 Content Model이 팀 경계 안에 갇혀 있다. A팀이 공들여 만든 고품질 CM이 있어도 B팀은 그 존재조차 모른다.

데이터도 코드처럼 재사용될 수 있어야 한다. 검증된 CM은 팀 간에 공유·구독되어 중복 작업을 없애야 한다.


AS-IS vs TO-BE

관점 AS-IS TO-BE
워크플로우 Scan → Propose → Select → Extract User intent → Agent 협업 → Preview → Production
구조 1 Source → N Proposals 1 Pipeline = 1 Dataset (1:1)
실행 단위 Proposal (설계서) Pipeline (실행 단위)
Schedule 귀속 Proposal에 1:1 Dataset 단위 → Pipeline 자동 트리거
파일 지원 DB 커넥션만 DB + 파일 업로드 (CSV/Parquet/JSON/Excel)
Agent 역할 자동 제안 생성 사용자 피드백 기반 CM 초안 반복 생성
팀 간 공유 불가 Catalog Marketplace (구독 모델)

구현 진행 현황

브랜치: wogus/ARK-1139 | 마지막 확인: 2026-03-27

진행 상태

레포 커밋 PR 변경 내용 상태
arkraft-api 107606a #441 vendors 테이블, vendor→connector, ConnectorType enum, test 엔드포인트, artifacts, AI descriptions, spec docs, OAuth 전파 ✅ PR 생성
arkraft-web 27e22149 #437 Source/Dataset 서브탭 분리, Proposals UI 제거, AI descriptions 표시, DOCS 탭, artifacts 탭, TEST 버튼 ✅ PR 생성
arkraft-agent-data 6d64468 #106 PostgreSQL schema 버그 수정, AI descriptions (Claude SDK), spec docs S3 로드, artifacts 디렉토리, 이벤트 로깅 ✅ PR 생성

[arkraft-api] POST /data/sources/{id}/test — 연결 테스트 엔드포인트

PR: #441

데이터 소스 저장 전/후 연결 가능 여부를 확인하는 엔드포인트 추가.

POST /data/sources/{id}/test
→ { "success": true/false, "message": "..." }

UI에서는 SourceDetail 헤더에 TEST 버튼이 위치하며, 결과 뱃지가 버튼 좌측에 표시된다.


[arkraft-api] source_docs_prefix + artifacts 엔드포인트

무엇을 바꿨나:

  • DataS3Paths.source_docs_prefix 헬퍼: spec 문서(xlsx/xls 등)가 저장되는 S3 경로 생성
  • GET /data/sources/{id}/artifacts: 워크스페이스 전체 파일 조회 (scan_manifest.json 항목만이 아닌 전체)
# source_docs_prefix 패턴
s3://arkraft-{env}/data-sources/{source_id}/docs/

scan_manifest.json 지원: scan 실행 후 생성되는 scan_manifest.json을 통해 artifacts 목록 파싱 가능. 단, artifacts 엔드포인트는 manifest 항목에 한정하지 않고 워크스페이스 전체를 반환한다.


[arkraft-api] AI descriptions 스키마 응답 포함

무엇을 바꿨나:

  • 스키마 API 응답(GET /data/sources/{id}/schema)에 AI 생성 descriptions 포함
  • DB/테이블/컬럼 레벨의 descriptions가 agent scan 후 S3에 저장되어 있으면 함께 반환
{
  "tables": [{
    "name": "transactions",
    "description": "일별 주식 거래 내역 테이블 (AI 생성)",
    "columns": [{
      "name": "amount",
      "description": "거래 금액 (KRW 단위, AI 생성)"
    }]
  }]
}

[arkraft-api] USE_OAUTH / OAuth 토큰 data agent 전파

data agent 컨테이너 실행 시 환경변수로 USE_OAUTH 플래그와 OAuth 액세스 토큰을 주입. agent 내부에서 Claude API 호출 시 Bedrock 직접 호출 대신 OAuth 경유 방식으로 전환 가능.


[arkraft-api] spec 문서 허용 확장자 확장

이전 이후 추가
.pdf, .txt, .md .xlsx, .xls, .xlsm, .xlsb

spec 문서란 데이터 소스의 컬럼 정의서, 데이터 명세서 등 agent가 description 생성 시 참고하는 문서다.


[arkraft-api] vendors 테이블 신규, connector 리네임

PR: #441

무엇을 바꿨나:

  • data_sources.vendor (VARCHAR)data_sources.connector (ConnectorType enum, NOT NULL)
  • data_sources.source_type 컬럼 완전 제거
  • vendors 테이블 신규 생성 (team-scoped, team_id IS NULL = 전팀 공용)
  • data_sources.vendor_id, data_sets.vendor_id FK 추가
  • ConnectorType StrEnum: postgresql/mysql/mariadb/s3/glue_catalog/rest_api/ftp/sftp
  • CONNECTOR_CATEGORY 맵 추가, DataSourceResponse.category 필드 노출
  • Migration: e5b0031e15ee_add_vendors_connector_enum.py (autogenerated)

왜 바꿨나 (ADR-5, ADR-6): 아래 ADR 섹션 참조


[arkraft-web] Sources → Source/Dataset 서브탭 분리

PR: #437

무엇을 바꿨나:

변경 전:
  /data/sources → DataSourcesClient (Source 목록 + ProposalSection)
  SubNav에 Data 섹션 드롭다운

변경 후:
  /data/sources → redirect → /data/sources/source
  /data/sources/source → Source 목록 (TertiaryNav: Source | Dataset)
  /data/sources/dataset → Dataset 목록 (신규 라우트)
  SubNav: Data 섹션에서 서브메뉴 숨김 (TertiaryNav로 이관)
  DataSourcesClient: ProposalSection 제거, container resize 동적 계산
  SourceDetail: Schema 탭 flex layout 수정, 헤더 단일 행 컴팩트 레이아웃

드래그 resize 성능 최적화 (rAF 쓰로틀)

드래그 중 setState 호출을 제거하고, requestAnimationFrame으로 쓰로틀된 DOM 직접 조작으로 전환했다.

문제: mousemove 이벤트는 초당 수백 번 발생한다. 매번 setPanelHeight 호출 → React re-render → 버벅임.

1차 개선 (이전): DOM 직접 조작 (panelRef.current.style.height)으로 re-render 제거.

2차 개선 (이번): requestAnimationFrame으로 프레임당 1회로 추가 쓰로틀.

const rafRef = useRef<number | null>(null);

function onMove(ev: MouseEvent) {
  if (!dragRef.current) return;
  const clientY = ev.clientY;
  if (rafRef.current !== null) return; // 이미 rAF 예약됨 → 스킵
  rafRef.current = requestAnimationFrame(() => {
    rafRef.current = null;
    if (!dragRef.current || !panelRef.current) return;
    const delta = dragRef.current.startY - clientY;
    // ... 높이 계산
    panelRef.current.style.height = `${next}px`;
  });
}

function onUp() {
  if (rafRef.current !== null) {
    cancelAnimationFrame(rafRef.current); // 미처리 rAF 취소
    rafRef.current = null;
  }
  const finalH = panelRef.current.clientHeight;
  setPanelHeight(finalH); // mouseup 시에만 state 동기화
}
단계 방식 효과
AS-IS mousemovesetState re-render 연속 발생, 버벅임
1차 개선 DOM 직접 조작 re-render 제거
2차 개선 (현재) rAF 쓰로틀 + DOM 조작 프레임당 1회 제한, 60fps 보장

resize handle에 select-none 추가

드래그 중 텍스트 선택이 발생하는 문제도 함께 수정. resize handle div에 select-none 클래스 추가.

SourceDetail 헤더 레이아웃 컴팩트화

변경 전: 2행 레이아웃 (소스명+상태 / 메타정보 / 버튼)으로 헤더 높이가 컸다.

변경 후: 단일 flex items-center 행으로 통합 — 소스명, ScanChip, 메타정보(truncate), 버튼 순서로 좌→우 배치.

// 변경 전: py-4, 2행 구조
<div className="shrink-0 border-b border-neutral-800 px-5 py-4">
  <div className="flex items-start justify-between mb-3">
    <div>
      <h2 className="text-[15px] ...">...</h2>
      <p className="text-[12px] ...">...</p>
    </div>
    <div className="flex items-center gap-2 shrink-0">버튼들</div>
  </div>
</div>

// 변경 후: py-2, 단일 행
<div className="shrink-0 border-b border-neutral-800 px-5 py-2">
  <div className="flex items-center gap-2 mb-2">
    <h2 className="text-[13px] ... shrink-0">소스명</h2>
    <ScanChip />
    <p className="text-[11px] ... min-w-0 truncate">메타정보</p>
    <div className="flex items-center gap-2 shrink-0 ml-auto">버튼들</div>
  </div>
</div>
flowchart TD
    direction TB
    A[/data/sources] -->|redirect| B[/data/sources/source]
    subgraph TertiaryNav["TertiaryNav (Sources 진입 시)"]
        direction TB
        B[Source 탭]
        C[Dataset 탭]
    end
    B --> D[Source 목록 UI]
    C --> E[Dataset 목록 UI]

미결 질문 — Web

  1. /data/sources/dataset 페이지는 현재 어떤 상태인가요? 빈 placeholder인가요, 아니면 기존 Dataset 목록 UI가 이미 있나요?
  2. ProposalSection을 UI에서 완전히 제거한 건가요, 아니면 점진적 제거의 첫 단계인가요? 기존 selected 상태의 proposal 데이터는 어떻게 접근하나요?

[arkraft-web] AI descriptions 표시 + DOCS 탭 + artifacts 탭

PR: #437

AI descriptions 표시

스키마 뷰(SchemaTreeView)에 AI 생성 descriptions 렌더링 추가. initialTable prop으로 초기 선택 테이블 지정 가능 (URL 파라미터 연동 등에 활용).

<SchemaTreeView
  sourceId={sourceId}
  initialTable="transactions"  // AI description 포함 응답으로부터 하이라이트
/>

DOCS 탭 — spec 문서 관리 UI

spec 문서를 업로드/조회하는 탭 신규 추가.

기능 구현
문서 목록 조회 업로드 존 위에 기존 문서 목록 표시
파일 업로드 클릭 + drag & drop 모두 지원
허용 확장자 .xlsx, .xls, .xlsm, .xlsb, .pdf, .txt, .md

drag & drop 구현 포인트: - onDragOvere.preventDefault() + setDragOver(true) - onDrope.dataTransfer.files 처리 - onDragLeavesetDragOver(false) - 업로드 직후 presigned URL 요청 → S3 직접 PUT (double JSON.stringify 버그 수정 포함)

artifacts 탭 — scan 산출물 파일 브라우저

scan 완료 후 생성되는 워크스페이스 파일들을 탐색하는 탭. GET /data/sources/{id}/artifacts API를 사용하며, manifest 항목에 한정하지 않고 전체 파일 목록을 표시.

TEST 버튼 레이아웃 이동

이전 이후
하단 버튼 영역 SourceDetail 헤더 우측

연결 테스트 결과 뱃지(✅ 연결됨 / ❌ 실패)가 TEST 버튼 좌측에 인라인 표시.


[arkraft-agent-data] AI descriptions + S3 artifacts 구조 개선

PR: #106

AI descriptions — DB/테이블/컬럼 설명 자동 생성

scan 완료 후 Claude Agent SDK를 이용해 DB 전체 구조에 대한 자연어 설명을 자동 생성한다.

인증 분기:

if USE_OAUTH:
    # Claude Agent SDK + OAuth 토큰 (arkraft-api가 주입)
    client = AnthropicBedrock(access_token=os.environ["OAUTH_TOKEN"])
else:
    # Claude Agent SDK + Bedrock IAM (기존 방식)
    client = AnthropicBedrock()

Bedrock 직접 호출(boto3.client("bedrock-runtime")) → Claude Agent SDK로 전환. 구조화된 출력(tool_use 블록)으로 DB/테이블/컬럼 descriptions를 한 번에 생성.

spec docs 활용:

source_docs_prefix 하위 S3 파일들을 로드하여 description 생성 프롬프트에 주입. 예: transactions_spec.xlsx가 있으면 컬럼 정의서를 참고해 더 정확한 description 생성.

spec_docs = load_spec_docs_from_s3(source_docs_prefix)
prompt = f"""
DB Schema:
{schema_json}

Reference Documents:
{spec_docs_text}

Generate descriptions for each table and column.
"""

S3 artifacts 디렉토리 구조

scan 실행 결과물을 기능별 prefix로 분리 저장:

s3://arkraft-{env}/data-sources/{source_id}/workspace/
  ├── schema.json          # 전체 스키마 (테이블/컬럼 목록)
  ├── descriptions.json    # AI 생성 descriptions
  ├── scan_manifest.json   # 산출물 목록 (파일명, 크기, 생성시각)
  └── artifacts/
      ├── sample_data/     # 각 테이블 샘플 데이터
      └── stats/           # 컬럼별 통계

이벤트 로깅

agent 실행 중 LLM 이벤트를 구조화된 로그로 기록:

이벤트 타입 로그 내용
thinking extended thinking 블록 텍스트
text 일반 텍스트 응답
tool_use 도구 호출명 + 입력 파라미터

PostgreSQL schema 범위 버그 수정

# 변경 전: public schema 고정
schema_filter = "schema_name = 'public'"

# 변경 후: 시스템 schema 제외 전체 탐색 (MySQL/MariaDB와 동일 방식)
excluded = ['information_schema', 'pg_catalog', 'pg_toast']
# + pg_temp_*, pg_toast_* 패턴 제외

핵심 도메인 구조

flowchart TD
    direction TB
    subgraph Source["Source Layer"]
        direction TB
        S1[RDS Connection]
        S2[Cloud Warehouse]
        S3[File Upload]
    end
    subgraph Dataset["Dataset"]
        direction TB
        D1[spec 정의]
        D2[schedule/cron]
    end
    subgraph Pipeline["Pipeline Layer"]
        direction TB
        P1[1 Pipeline = 1 Dataset]
        P2[Agent 피드백 루프]
        P3[CM 생성]
    end
    subgraph Catalog["Catalog Layer"]
        direction TB
        C1[CatalogEntry]
        C2[Marketplace 구독]
    end
    Source --> Dataset --> Pipeline --> Catalog

도메인별 설계

Source

  • RDS Connection / Cloud Warehouse / File Upload 통합
  • vendor는 Source에 귀속 (RDS/Warehouse만 해당)
  • File Upload: S3 presigned URL, CSV/Parquet/JSON/Excel, 크기 제한 없음

Dataset

  • Source 하위 소속
  • vendor는 Source로부터 자동 상속
  • spec 메타정보 보유 (하단 ARK-1149 참조)
  • Dataset 단위로 schedule(cron) 설정
  • Dataset 갱신 시 연결된 Pipeline 자동 트리거

Pipeline

  • 1 Pipeline = 1 Dataset (엄격한 1:1)
  • vendor는 Dataset.vendor 자동 참조 (직접 설정 불필요)
  • 인터랙션: 채팅 아님 — Agent가 CM 초안 생성 → 사용자 피드백 → 반복
  • 1 Pipeline → N Content Model(CM) 생성 가능

Content Model (CM)

  • Pipeline에서 생산되는 개별 데이터 산출물
  • 상태: buildingpreviewscheduledrunningpaused
  • 결과는 CatalogEntry로 연결

Catalog

  • CM 생산 결과물 집합소
  • is_public 플래그로 Marketplace 공개 여부 결정
  • CatalogSubscription으로 팀 간 구독

핵심 설계 결정 (ADR)

ADR-1: Pipeline과 Dataset을 1:1로 제한

결정: 1 Pipeline = 1 Dataset (N:1 금지)

이유:

  • Pipeline은 Dataset의 schema와 긴밀하게 결합된다 (transform definition이 schema 기반)
  • Dataset schema가 바뀌면 Pipeline의 transform도 재검토가 필요
  • 1:1 제약이 있으면 Dataset 변경의 영향 범위가 명확해진다
  • 같은 Dataset으로 다른 변환이 필요하면 → Pipeline을 새로 만들면 된다 (명시적)

ADR-2: Schedule을 Dataset에 귀속

결정: Schedule은 Dataset 단위로 설정, Pipeline을 자동 트리거

이유:

  • Schedule의 의미론적 주체는 "데이터 소스의 갱신 주기"이지 "어떤 변환을 선택했나"가 아님
  • Dataset이 갱신되면 연결된 모든 Pipeline이 트리거되어야 함 (데이터 일관성)
  • Pipeline 레벨에서 수동 트리거도 가능 (유연성 확보)

ADR-3: Agent 인터랙션을 구조화된 피드백 루프로

결정: 채팅 UI 대신 "CM 초안 → 피드백 입력 → 재생성" 루프

이유:

  • 채팅은 이력 관리가 어렵고 "어느 버전의 CM인가"를 추적하기 어렵다
  • 구조화된 피드백은 각 수정 사항이 PipelineFeedback 레코드로 저장됨
  • 특정 시점으로 롤백하거나 다른 피드백으로 재시도 가능
  • Agent 입장에서도 명확한 diff 기반 수정이 더 정확하다

ADR-4: 파일 업로드는 S3 presigned URL

결정: 파일 업로드는 S3 presigned URL 방식, 크기 제한 없음

이유:

  • API 서버를 통한 파일 프록시는 대용량 파일에서 병목이 됨
  • Presigned URL은 클라이언트 → S3 직접 업로드로 서버 부하 없음
  • 금융 데이터의 특성상 대용량 파일이 많아 크기 제한을 두지 않음

ADR-5: Source 연결 방식 필드명을 connector로 통일

결정: engine / vendor (문자열) 대신 connector 단일 필드 사용

배경:

초기 설계에서는 engine (postgresql, mysql 등 DB 엔진명)과 vendor (데이터 제공사) 두 개념이 혼재했다. DB가 아닌 소스(S3, REST API, FTP/SFTP)가 추가되면서 engine이라는 이름 자체가 어색해졌다.

후보 문제점
engine S3, REST API에 "엔진"은 의미 없음
vendor 별도 VendorModel 엔티티로 분리됨 (아래 ADR-6) — 중복/혼란
connector 모든 소스 유형에 자연스럽게 적용 가능

결정:

  • data_sources.vendordata_sources.connector (컬럼명 변경)
  • source_type 컬럼 제거 — connector 값에서 카테고리 파생 가능
  • connector 타입: ConnectorType StrEnum (DB 레벨 non-native enum)

connector → 카테고리 매핑:

CONNECTOR_CATEGORY = {
    "postgresql": "database",
    "mysql": "database",
    "mariadb": "database",
    "s3": "object_storage",
    "glue_catalog": "object_storage",
    "rest_api": "api",
    "ftp": "ftp",
    "sftp": "ftp",
}

영향 범위: data_sources 테이블 + 이를 참조하는 모든 서비스, 스키마, 테스트

ADR-6: Vendor를 별도 엔티티(vendors 테이블)로 분리

결정: vendor(데이터 제공사)를 문자열이 아닌 독립 엔티티로 관리

배경:

  • 데이터 소스(Source)는 연결 방법(connector)을 가지지만, 그 데이터의 출처(FnGuide, Bloomberg, 내부 생성)는 다른 개념
  • Dataset은 Source 없이 직접 생성될 수도 있어 vendor 정보를 Dataset에도 독립적으로 지정해야 할 필요가 생김
  • Source의 vendor를 Dataset이 단순 상속하는 방식만으로는 "출처 불명" Dataset을 표현할 수 없음

설계:

CREATE TABLE vendors (
    id UUID PRIMARY KEY,
    name VARCHAR(255) NOT NULL,        -- "FnGuide", "Bloomberg", "Internal"
    code VARCHAR(100) UNIQUE NOT NULL, -- "fnguide", "bloomberg", "internal"
    description TEXT,
    team_id UUID,                      -- NULL이면 글로벌, 값이 있으면 팀 전용
    created_at TIMESTAMPTZ,
    updated_at TIMESTAMPTZ
);
ALTER TABLE data_sources ADD COLUMN vendor_id UUID REFERENCES vendors(id);
ALTER TABLE data_sets ADD COLUMN vendor_id UUID REFERENCES vendors(id);

규칙:

  • team_id IS NULL: 전팀 공용 vendor (e.g., FnGuide, Bloomberg)
  • team_id IS NOT NULL: 특정 팀 전용 vendor
  • vendors는 CRUD API로 팀이 직접 생성/관리 (POST /data/vendors)

결과: 파일 Dataset의 vendor 지정 방식 미결 사항 해소 (→ data_sets.vendor_id 직접 지정)


DB 스키마 구현 현황

Migration: e5b0031e15ee_add_vendors_connector_enum.py (autogenerated, branch: wogus/ARK-1139)

vendors 테이블 (신규)

CREATE TABLE vendors (
    id         UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name       VARCHAR(255) NOT NULL,
    code       VARCHAR(100) NOT NULL UNIQUE,
    description TEXT,
    team_id    UUID,
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX ix_vendors_team_id ON vendors(team_id);

data_sources 변경사항

AS-IS TO-BE 변경 유형
vendor VARCHAR(255) connector ConnectorType NOT NULL 리네임 + Enum 타입
source_type VARCHAR(50) (제거) 컬럼 삭제
(없음) vendor_id UUID REFERENCES vendors(id) FK 추가

data_sets 변경사항

AS-IS TO-BE 변경 유형
(없음) vendor_id UUID REFERENCES vendors(id) FK 추가

Alembic 마이그레이션 체인

d5b06e57abb1_add_file_id_to_distilling_sessions
    └── e5b0031e15ee_add_vendors_connector_enum ← HEAD

source_type 파생

source_type 컬럼은 제거됐으나, 카테고리가 필요한 경우 CONNECTOR_CATEGORY[source.connector]로 파생한다. (DataSourceResponse.category로 노출)


Sub-tasks

이슈 제목 상태
ARK-1149 Dataset spec 정의 및 파일 업로드 검증 파이프라인 도입 Backlog

ARK-1149: Dataset spec 정의 및 검증 파이프라인

문제

현재 파일 업로드 시마다 LLM Full Pipeline이 실행된다:

업로드 → detect_normalize → cm_check → spec → transform → register

데이터 형상을 매번 추론해야 하므로 비효율적이고, 기존 Dataset과의 형상 일치 여부 검증이 없다.

설계 방향

Dataset = 데이터 파일들 + 그 파일들이 따라야 할 spec

Dataset spec 구조

{
  "columns": [{"name": "date", "type": "date"}, ...],
  "time_axis": {"column": "date", "format": "%Y-%m-%d", "frequency": "daily"},
  "entity_axis": {"column": "ticker"},
  "value_columns": ["close", "volume"],
  "cm_name": "kr_stock_price_daily",
  "delivery_lag": 1
}

spec 정의 시점

Dataset 생성 경로 spec 정의 시점 출처
Source(RDB) → Dataset Dataset 생성 시 trial 결과 spec.json 승계
첫 파일 업로드 첫 업로드 Full Pipeline 완료 후 생성된 spec.json 저장

업로드 파이프라인 분기 변경

flowchart TD
    direction TB
    A[파일 업로드 confirm]
    A --> B{Dataset에 spec 있음?}
    B -->|없음 - 첫 업로드| C[Full Pipeline<br>detect_normalize → spec 생성 → 저장]
    B -->|있음| D[검증 모드<br>spec 형상 검증]
    D -->|통과| E[transform + register]
    D -->|실패| F[에러 리포트 반환]

검증 스텝 (신규)

  • 컬럼명/타입 일치 여부
  • time_axis, entity_axis 존재 여부
  • 타입 호환성 (e.g., date 컬럼이 실제로 파싱 가능한지)

영향 범위

레포 변경 내용
arkraft-api Dataset 모델 spec 컬럼 추가, migration, upload 서비스 분기 변경
arkraft-agent-data 검증 스텝 구현
arkraft-web Dataset 상세 UI에 spec 메타 표시

마이그레이션 고려사항

AS-IS TO-BE 비고
data_sources.vendor data_sources.connector ConnectorType enum으로 전환 완료
data_sources.source_type (제거) category 필드로 API 응답에서 파생
dataset_proposals datasets + pipelines 1 proposal → 1 dataset + 1 pipeline (미구현)
proposal.schedule dataset.cron_schedule schedule 귀속 이동 (미구현)
vendors 신규 완료 (ADR-6)
content_models 미구현
catalog_entries 미구현

마이그레이션 원칙

  • 기존 selected 상태의 proposal만 pipeline으로 이관 (unselected는 아카이브)
  • 이관 전 충분한 검증 기간 필요 (기존 API 병행 운영)

미결 사항

설계 미결

  • ~~파일 Dataset의 vendor 지정 방식~~ → data_sets.vendor_id FK로 직접 지정 (ADR-6)
  • ~~spec 문서 S3 업로드 및 agent 연동~~ → source_docs_prefix + DOCS 탭 + agent S3 로드로 완성
  • ~~AI descriptions 스키마 API 노출~~ → GET /data/sources/{id}/schema 응답에 포함
  • Agent tool use 접근 범위 (읽기 전용 vs 쓰기)
  • Catalog Marketplace 접근 제어 (추후)
  • 기존 데이터 마이그레이션 전략
  • Vendor CRUD API (POST /data/vendors, GET /data/vendors) 구현
  • Dataset 상세 UI에 spec 메타 표시 (ARK-1149)
  • Pipeline 모델 구현 및 Proposal 이관