rest_source 노드 QA 가이드 — 외부 REST/RSS 셀프서비스 수집
데이터 수집, 어떤 기능으로? 결정 가이드를 먼저 보세요.
이 문서는 Pipeline Studio의 rest_source 노드(Intel 셀프서비스 수집 Slice B, #2288)를 QA가 화면에서 단계별로 검증할 수 있도록 실제 prod(gend.genon.ai)의 finance-invest 워크스페이스 화면과 함께 정리한 것입니다.
개요
rest_source는 사용자가 코드 없이 외부 REST/RSS API를 페이지네이션·증분(incremental)까지 포함해 records[]로 수집하는 소스 노드입니다. 모든 외부 호출은 Slice A의 egress-proxy(/fetch)를 단일 통로로 경유하므로:
- SSRF 차단(사설·메타데이터·CGNAT IP, DNS rebind, https-only) + secret ws-fence(API 키는 호출 워크스페이스의 외부 egress 호스트 allowlist에 등록된 호스트로만)
- secret은
{{secret.x}}토큰을 미치환 상태로 proxy에 전달 → proxy가 Vault에서 해석·주입. 노드/Dagster는 평문을 절대 수신하지 않습니다(불변식 I1).
현재 상태: 작성·실행 모두 prod 라이브입니다. egress-proxy 는 2026-06-25 전체 활성화됐고 (Epic #2286), rest_source → intel_bronze_sink 풀체인 E2E 가 prod 에서 검증됐습니다 — 본 가이드의 절차만으로 실제 수집까지 동작합니다.
시작 전 — 계정 전제조건 (2026-07-21 E2E 확인)
이 절차는 워크스페이스에 소속된 계정을 전제로 합니다. 확인/준비 사항:
-
워크스페이스 소속 필수. Keycloak 그룹
/tenants/<slug>에 속하지 않으면 상단바에 "워크스페이스 없음" 이 뜨고, egress 호스트 등록 같은 ws 스코프 작업이 에러 없이 조용히 무시됩니다(등록 버튼을 눌러도 목록이 비어 있음). 상단바에 워크스페이스 이름이 보이는지 먼저 확인하세요. -
역할 변경 후에는 재로그인. 그룹/역할은 JWT 클레임이라 기존 세션에는 반영되지 않습니다.
-
배포 권한은 그래프 내용으로 결정됩니다 (#2462). 코드 노드가 없는 수집 그래프(rest_source → intel_bronze_sink)는 ws 멤버(analyst)가 직접 배포·실행할 수 있습니다. 다만 인라인 Shell/Python 코드 노드가 있으면 관리자 권한이 필요하며, 그 코드를 승인된 라이브러리 스텝으로 교체하면 다시 직접 배포할 수 있습니다.
글로벌 admin 실행 제약은 해소됐습니다 (2026-07-21)근거: #2470
과거에는 어떤 워크스페이스에도 속하지 않은 글로벌 admin 이 인텔 파이프라인을 실행하면 422 가 났지만, 이제 실행 워크스페이스가 파이프라인 정의에서 파생되므로 admin·크론 스케줄 모두 정상 실행됩니다. 파이프라인 자체가 워크스페이스 없이 만들어진 경우(admin 이 ws 컨텍스트 없이 생성)만 여전히 422 입니다.
1. 팔레트에서 rest_source 선택
Pipeline Studio에서 새 파이프라인을 만들면 빌더가 열립니다. 좌측 노드 팔레트에 소스 (REST/RSS API) 항목이 있습니다.

2. 캔버스에 드래그&드롭
팔레트의 **소스 (REST/RSS API)**를 캔버스로 끌어다 놓으면 rest_source 노드가 생성됩니다.

3. config 폼
노드를 클릭하면 우측 설정 패널이 열립니다. 기본 필드(URL·method·응답 포맷·records_path) 가 먼저 보이고, 나머지는 "고급 설정" 접이식(#2330)에 묶입니다. JSON 필드는 ⤢ 버튼으로 큰 모달 편집(#2329), 페이지네이션은 드롭다운, secret 참조는 key/value 행 편집기(#2331)입니다. 설정 패널은 드래그로 폭 조절(#2328).

| 필드 | 설명 |
|---|---|
| URL | https://… — {{param.x}}/{{checkpoint.x}} 치환. secret은 URL 금지(헤더에만) |
| method / 응답 포맷 | GET·POST / JSON·XML(RSS) |
| records_path | 응답에서 레코드 배열을 뽑을 경로 ($.list) |
| 헤더 (JSON) | {{secret.x}} 허용 — egress-proxy가 Vault 해석(평문 미수신). 예: {"Authorization":"Bearer {{secret.dart}}"} |
| query (JSON) | 쿼리 파라미터 |
| 페이지네이션 | 전략 드롭다운(none/page/offset/cursor/link_header) — 고르면 그 전략에 필요한 칸(page_param·size 등)만 표시(#2331) |
| 커서 추출 path / 커서 키 / 커서 비교 | 증분 워터마크용 — 응답에서 다음 커서를 뽑을 path, 저장/비교 키, 비교 방식(자동/숫자/날짜/문자) |
| secret 참조 | key/value 행 편집기(#2331) — name → Vault 경로. {{secret.name}} 으로 헤더에서 참조. secret 사용 시 대상 호스트를 egress 호스트 탭에 먼저 등록해야 통과 |
| max_records / max_pages | 안전 상한(서버 글로벌 cap으로 클램프) |
4. config 입력 예시
url_template과 records_path를 채우고, 페이지네이션/커서를 설정합니다. (예: OpenDART 목록 API → $.list)

확인 포인트(QA)
- URL/query/body에
{{secret.x}}를 넣으면 배포·실행이 차단됩니다(I1 — secret은 헤더에만).- 증분(
cursor_extract설정) 시 워터마크는 단조 전진만 합니다(후퇴/중복 전진 거부). 비교 방식 기본값 자동은 숫자 커서(9→100)를 수치로 비교합니다.- 업스트림이 429/5xx를 반환하면 노드가 실패하고 워터마크는 전진하지 않습니다(데이터 갭 방지).
- 상세 fan-out(고급 설정
detail, #2527): 목록 레코드별 상세 URL({{record.<필드>}}참조, 값 자동 percent-encode)을 호출해 payload 에 병합. 비증분 = 레코드 단위 실패 시__detail_error__마커 후 계속(전체 재조회가 자연 재시도), 증분(cursor_extract) 병용 = 부분 실패/스킵도 실행 실패(커서 전진 시 영구 결손 방지). 전면 실패 = 항상 실행 실패.max_calls기본 50(상한 200),delay_ms기본 200(하한 50), fan-out 전체 10분 예산.merge_key가 원본 레코드에 이미 있으면 덮어쓰지 않고 실패 처리. 드라이런은 첫 레코드 1건만 상세 미리보기(detail_previewed/detail_error).
실동작 예시 — 법제처 국가법령정보 API (2026-07-21 prod E2E)
키 없이 공개 파라미터만으로 되는 전체 예시입니다. API 를 직접 붙일 때 이 값들을 그대로 대입해 보세요.
법제처 OPEN API 는 OC 파라미터만 쓰고 secret({{secret.x}})을 사용하지 않으므로 allowlist 등록 없이 SSRF 필터만 통과하면 호출됩니다(위 보안 동작 요약 참조). 헤더에 {{secret.x}} 를 쓰는 API 로 바꿀 때 비로소 egress 호스트 등록이 필요합니다.
| 필드 | 값 |
|---|---|
| URL | https://www.law.go.kr/DRF/lawSearch.do?OC=test&target=law&type=JSON&query=%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4&display=20 |
| method / 응답 포맷 | GET / JSON |
| records_path | $.LawSearch.law |
URL 쿼리의 한글은 percent-encoding 으로 넣습니다 — 위
%EA%B0%9C%EC%9D%B8%EC%A0%95%EB%B3%B4는개인정보입니다. 비-ASCII 를 그대로 넣으면 클라이언트/프록시 구간에 따라 인코딩이 어긋날 수 있습니다.
싱크(intel_bronze_sink) 설정:
| 필드 | 값 |
|---|---|
| source / domain | law_go_kr / law → 적재 테이블 iceberg.bronze.intel_law_raw |
| external_id path | $.법령일련번호 (한글 키도 그대로 동작) |
| MCP 도구 | query_law + 설명 |
결과: 드라이런 13건(892ms) → 실행 SUCCESS → Bronze 13행 → MCP tools/list 에 query_law 자동 노출 → tools/call 로 법령 조회 성공.
적재 후 MCP 로 재현하는 요청은 이렇습니다(JSON-RPC 2.0 / Streamable HTTP):
TOKEN=... # 워크스페이스 멤버의 JWT 또는 M2M 토큰
curl -sS -X POST https://gend.genon.ai/mcp \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
"params":{"name":"query_law","arguments":{"limit":3}}}'
응답은 external_id | payload | fetched_at 마크다운 표이며, payload 에 법령명한글·시행일자 등 원본 필드가 그대로 들어 있습니다. 도구가 목록에 있는지만 볼 때는 "method":"tools/list","params":{} 로 호출합니다. arguments 로 받는 필드는 도구마다 다르므로 tools/list 응답의 inputSchema 를 확인하세요.
$.list 는 OpenDART 기준이고 API 마다 다릅니다. 위처럼 응답 루트가 {"LawSearch": {"law": [...]}} 면 $.LawSearch.law 입니다. 확신이 없으면 드라이런으로 건수가 잡히는지 먼저 확인하는 게 가장 빠릅니다 — 경로가 틀리면 0건으로 나옵니다.
설정 UX — 단계별 (#2328~2336)
사용자 점검 후 설정 입력 경험을 개선했습니다(실 prod 브라우저 캡처):
① 넓은 리사이즈 패널 + 기본/고급 분리 — 기본 필드만 먼저 보이고 "고급 설정"은 접이식. 패널 폭은 드래그로 조절.

② "고급 설정" 펼침 — pagination·cursor·secret_refs·headers·max 등 고급 필드.

③ 페이지네이션 — 전략 드롭다운 + 조건부 필드 — page 선택 시 page_param/size 등 필요한 칸만 표시(raw JSON 입력 불필요).

④ secret 참조 — key/value 행 편집기 — name → Vault 경로를 칸으로 입력(raw JSON 아님).

⑤ JSON 필드 ⤢ 큰 모달 편집 — 좁은 칸 대신 큰 편집창 + 예시 placeholder.

⑥ JSON 인라인 검증 — 잘못된 JSON 은 빨간 에러로 표시(이전엔 조용히 무시).

E2E:
ui/tests/ps-config-ux-e2e.spec.ts(패널 폭 #2336 회귀가드 + 접이식·모달·드롭다운·kv·검증). CI 단위 가드는PipelineStudioBuilderPage.test.tsx(패널 사이즈 % 문자열 단언).
보안·무결성 요약 (참고)
| 상황 | 동작 |
|---|---|
| secret 미사용(공개 RSS 등) | allowlist 불필요 — SSRF 필터만 통과 |
| secret 사용 + 호스트 allowlist 등록됨 | 통과(서버사이드 Vault 해석·헤더 주입, 노드는 평문 미수신) |
| secret 사용 + 호스트 미등록 | 차단 |
| 페이지 일부만 수집(절단/오류) | 가져온 high-water mark까지만 전진, 비-2xx는 실패 처리 |
자동 검증
이 가이드의 단계는 ui/tests/rest-source-e2e.spec.ts(Playwright)로 자동 검증되며, QA_CAPTURE=1로 실행하면 위 스크린샷이 재생성됩니다.
E2E_BASE_URL=https://gend.genon.ai E2E_USERNAME=... E2E_PASSWORD=... \
QA_CAPTURE=1 pnpm exec playwright test rest-source
백필 — 이전 데이터 전부 적재 (#2619)
증분 수집과 별개로, 실행 다이얼로그의 "백필 — 이전 데이터 전부 적재" 체크박스로 과거 데이터를 소급 적재할 수 있습니다.
1단계 — 실행 다이얼로그에서 백필 + 자동 반복 체크 (배포된 rest_source 파이프라인의 "실행" 버튼):

- 백필 run 은 목록을 전 페이지 수집한 뒤(설정
max_records/max_pages대신 서버 상한 사용), 200건/실행 씩 상세(detail)를 채워 적재합니다. 진행 지점은 파이프라인별 백필 커서(__backfill__파티션)에 저장되어 다음 백필 run 이 이어받습니다. 커서는 적재(sink) 성공 후에만 전진하므로, 실패한 백필 run 은 "재실행" 버튼으로 같은 배치를 다시 시도하면 됩니다(배치 유실 없음). - "완료까지 자동 반복" 을 함께 체크하면 실행이 성공할 때마다 다음 배치를 자동으로 트리거합니다(최대 30회, 실패·페이지 이탈 시 중단).
2단계 — 실행 탭에서 진행률 확인 (run 행을 펼치면 적재 결과에 표시):

- 적재 결과에
본문 백필 N/전체진행률이 표시되고, 완주하면 "백필 완료" 뱃지가 붙습니다. 위 캡처는 법제처 국가법령정보 5,597건 백필 완주 시점의 실측 화면입니다. - 백필은 증분 워터마크를 읽지도 쓰지도 않습니다 — 기존 증분(cron) 수집과 안전하게 병행됩니다.
- 상세 호출이 실패한 레코드는 적재에서 제외됩니다(오류 마커 행을 남기지 않음 — 조회 도구에 "본문 없는 행"이 노출되는 것을 방지). 실패 건수는 진행률 옆에 "N건 실패 — 재백필 필요" 경고(amber)로 표기되며, 백필을 다시 완주하면 수렴합니다.
- 동시 백필은 지원하지 않습니다 — 실행 중인 run 이 있으면 백필 체크박스가 비활성화됩니다.
위 두 캡처는 커밋된 E2E 스펙(
ui/tests/ps-backfill-e2e.spec.ts)이QA_CAPTURE=1로 생성한 실측 화면입니다 — UI 가 바뀌면 스펙 재실행으로 갱신합니다.런북 — 백필 커서 리셋: 처음부터 재백필하려면 운영자가 워터마크 행을 삭제합니다:
DELETE FROM ps_pipeline_watermark WHERE pipeline_id='<id>' AND partition_key='__backfill__';