Pipeline Studio 코드 노드 QA 가이드 — Python · Shell
이 문서는 Pipeline Studio 코드 노드(Python/Shell)를 QA가 화면에서 직접 따라 검증할 수 있도록 단계별 스크린샷과 함께 정리한 것입니다. 모든 화면은 실제 GenD 환경의 데모 워크스페이스에서 캡처했습니다.
개요
코드 노드는 워크스페이스에서 작성한 사용자 코드(Python transform 함수 또는 Shell 스크립트)를 적재 파이프라인의 한 단계로 실행합니다. 코드는 격리 러너(gend-codexec) 안에서만 돌고, 그 러너는 네트워크 차단 · 시크릿 없음 · 자원 상한(시간/메모리) 으로 봉인돼 있습니다.
Phase 1 범위(이 가이드 대상) — "순수 변환": 상위 노드가 준 데이터(text/chunks/metadata 등)를 받아 변환해 돌려줍니다.
- ✅ Python 표준 라이브러리(json/re/math/datetime…) 사용
- ✅ 배포 전 드라이런으로 샘플 입력에 대해 즉시 테스트
- ❌ 네트워크 호출 · 외부 시크릿/DB/S3 접근 ·
pip install— (후속 Phase. 지금은 차단됨)
권한 (업데이트됨, #2106 P1): 인라인 코드 노드의 직접 배포는 여전히 관리자(admin) 만 가능합니다(일반 멤버는 배포 시 403). 단, 워크스페이스 멤버도 이제 코드 드라이런과 재사용 스텝 제출(승인 대기)이 가능하고, 워크스페이스 admin(
/tenants/<slug>/admins)이 승인한 library_ref 스텝만으로 구성한 파이프라인은 멤버도 배포할 수 있습니다. 페르소나별 전체 흐름은 비-admin 코드 작성 QA 가이드 참조.
1. Python 코드 노드 — 작성 & 드라이런
1-1. 코드 노드 추가
파이프라인 스튜디오 빌더 좌측 노드 팔레트에 Python 코드 / Shell 코드 가 있습니다. 캔버스로 끌어다 놓습니다.

1-2. 코드 작성
노드를 클릭하면 우측 설정 패널에 코드 에디터가 열립니다. Python은 transform(input: dict) -> dict 함수를 정의합니다. 타임아웃(초) · 메모리(MB) · 샘플 입력(드라이런용 JSON) 도 함께 설정합니다.

1-3. ✅ 드라이런(배포 전 테스트)
▶ 드라이런 버튼을 누르면 샘플 입력으로 코드를 격리 러너에서 즉시 실행하고 결과를 보여줍니다. status: ok · output: {...} · metrics(소요시간/메모리) 가 표시됩니다.

확인 포인트(QA): 입력(
{})에transform이 더한 키(marker)가output에 보이면 정상입니다. 배포 없이 여기서 코드를 검증할 수 있습니다.
2. ✅ 엣지: 보안 가드 — 위험 코드 거부
Python 코드는 배포·드라이런 시 정적 보안 검사(L1) 를 통과해야 합니다. import socket/subprocess/os/eval 등 네트워크·시스템 접근은 거부됩니다.
import socket 을 넣고 드라이런하면 python 코드 정책 위반 [import] line N: socket 토스트가 뜨고 러너에 도달하지 않습니다(작성 단계에서 차단).

확인 포인트(QA): 금지 모듈/호출이 줄 번호와 함께 거부되는지, 그리고 거부 시 실행이 일어나지 않는지 확인하세요.
Python 코드는 배포·드라이런 전에 정적 보안 검사(L1 AST) 를 통과해야 합니다(socket/subprocess/os/eval/open 등 금지). Shell 코드는 이에 상응하는 정적 게이트가 없습니다 — sh 스크립트를 정적으로 안전 판정할 등가물이 없기 때문입니다. 따라서 Shell 의 안전성은 전적으로 런타임 샌드박스 경계(egress 차단 NetworkPolicy · 시크릿 env 0 · 전용 노드풀 podMaxPids PID 캡 · CPU/메모리/시간 자원 상한)에 의존합니다. 둘 다 격리 러너 안에서만 실행되어 호스트/클러스터에 닿지 못하지만, "작성 단계 거부"는 Python 에만 적용된다는 점을 운영 시 인지하세요.
3. Shell 코드 노드
Shell 노드는 stdin 으로 입력 JSON 을 받아 stdout 으로 출력 JSON 을 내보내는 스크립트입니다(jq/coreutils 사용 가능). 예: cat(그대로 통과), jq '.chunks |= map(ascii_upcase)'.

확인 포인트(QA): Shell 은 정적 보안 검사 등가물이 없어 샌드박스 경계(네트워크 0·시크릿 0·자원 상한) 에만 의존합니다(설계상 의도). 출력은 반드시 JSON 한 줄이어야 합니다.
4. ✅ 엣지: 타임아웃
무한 루프·장시간·과도한 자원 사용 코드는 자원 상한(CPU·시간·메모리) 에서 강제 종료되어 status: error 로 끝납니다. 예: timeout=2 에 while True: pass → CPU 상한 초과로 execution killed (signal …; CPU/메모리/시간 자원 상한 초과). (블로킹 대기는 wall-clock 초과 시 execution timeout.) 참고로 import time 같은 비허용 모듈은 실행 이전에 L1 보안 검사에서 먼저 거부됩니다.

확인 포인트(QA): 무한 루프/장시간 코드가 파이프라인을 멈추지 않고 타임아웃으로 끊기는지 확인하세요. (메모리 초과·과도한 출력도 같은 방식으로
status: error.)
5. 풀 파이프라인에 코드 노드 넣고 실행
코드 노드는 그래프 어디든 끼울 수 있습니다(통과형). 예: 소스 → 파싱 → Python 코드 → 청킹 → 임베딩 → 벡터 적재. 코드 노드는 상위 데이터를 받아 변환한 뒤 그대로 하위로 흘려보냅니다.

저장 → 배포 → 실행 하면 코드 노드가 실제 적재 실행 안에서 격리 러너로 돌고, 모든 노드가 성공(초록)으로 끝납니다.

확인 포인트(QA): 코드 노드(
Python 코드)를 포함해 6개 노드가 모두 초록(성공)이면, 사용자 코드가 실제 파이프라인 런에서 격리 실행된 것입니다.
6. 출력 유형 선언 (output_type) — 변환 체인에 끼워넣기
코드 노드는 기본적으로 pass-through(입력 유형을 그대로 전달)라, rest_source(records) 뒤에
붙여도 다음 노드에는 여전히 records 로 보입니다 — 그래서 청킹(text 요구) 같은 노드에
연결할 수 없었습니다. 고급 설정 → "출력 유형" 을 선언하면 이 제약이 풀립니다.
| 선언 | 출력 JSON 계약 | 이어지는 노드 예 |
|---|---|---|
records | {"records": [ {...}, ... ]} | 싱크 (Intel Bronze + MCP) |
text | {"text": "..."} | 청킹 |
chunks | {"chunks": ["...", ...]} | 임베딩 / 벡터 적재 |
예 — 법제처 본문을 커스텀 노드로 시맨틱 인덱싱 체인에 넣기:
rest_source(상세 fan-out) → Python(output_type=text) → 청킹 → 임베딩 → 벡터 적재
def transform(input):
# 목록 레코드들의 본문(detail)을 하나의 텍스트로 합쳐 청킹에 넘긴다
parts = []
for r in input.get("records", []):
d = r.get("detail") or {}
parts.append(str(d.get("조문", "")))
return {"text": "\n\n".join(parts)}
2026-07-23 prod 실측 — 아래 화면은 실제 prod 에서 이 체인을 구성·실행한 것입니다 (법제처 목록+본문 fan-out → Python 조문 추출 → 청킹 → 임베딩 → Weaviate 7개 벡터 적재 확인).
고급 설정의 "출력 유형" 드롭다운:

완성된 체인 — rest_source → Python(출력 유형=text) → 청킹 → 임베딩 → 벡터 적재.
선언 전에는 Python→청킹 연결이 거부되지만, 선언 후에는 드래그로 연결됩니다:

실행 성공 (모든 노드 ok):

동작 규칙:
- 선언하면 검증기가 그 유형으로 다음 노드 연결을 허용합니다. 상류 토큰(records 등)은
실행기의 병합 전달(
{...upstream, ...출력}) 그대로 계속 흐릅니다. - 실행 시 실제 출력이 선언과 다르면 노드가 즉시 실패합니다(예:
output_type=text인데 출력에text문자열 키가 없음). 선언 노드가 선언하지 않은 상류 키를 덮어쓰는 것도 실패 처리됩니다 — 빈 입력/오염으로 다운스트림이 조용히 잘못 성공하는 것을 막는 가드입니다. - 커스텀 전처리(내 서비스 호출) 노드도 동일하게 지원 —
응답 매핑으로 선언 키를 만들고 출력 유형을 선언하면 됩니다.
- 배포 권한: 인라인 코드 노드가 있는 그래프는 기존 정책(#2462)대로 관리자 배포가 필요합니다(버튼 툴팁으로 안내됨). 승인된 라이브러리 스텝으로 바꾸면 analyst 도 직접 배포할 수 있습니다.
- 대용량 텍스트 주의: 본문 전체를 그대로 text 로 내보내면 청크가 수백 개로 불어나 임베딩 호출이 타임아웃될 수 있습니다(실측: 법령 3건 조문 전체 → embed 타임아웃). 추출 단계에서 필요한 범위로 잘라 내보내거나 임베딩 노드의 타임아웃을 조정하세요.
QA 체크리스트 요약
| # | 시나리오 | 기대 결과 |
|---|---|---|
| 1 | Python transform 드라이런 | status: ok + output 에 변환 결과 |
| 2 | import socket 드라이런 | 정책 위반 토스트(줄번호), 실행 안 됨 |
| 3 | Shell cat 드라이런 | status: ok |
| 4 | time.sleep > 타임아웃 | status: error / execution timeout |
| 5 | 코드 노드 포함 파이프라인 실행 | 전 노드 성공(초록) |
| 6 | 일반 멤버(비admin)가 코드 노드 배포 | 403 차단(관리자 전용) |
#6(비admin 차단)은 관리자 계정이 아닌 별도 멤버 계정으로 배포를 시도해 확인합니다(API/단위 테스트로도 검증됨).
알아두기 (Phase 1 제약)
- 네트워크 0 · 시크릿 0: 코드에서 외부 호출·DB·S3·환경변수 시크릿에 접근할 수 없습니다(러너가 봉인). 외부 연동이 필요하면
커스텀 전처리 — 내 서비스 호출(http_api) 노드를 쓰세요. - Python = 표준 라이브러리만(Phase 1). 임의
pip패키지는 후속 Phase. - Shell 정적검사 없음: 위 격리 경계에만 의존(설계 명시).
- 자원 상한: 노드별 타임아웃/메모리 설정값이 강제됩니다(플랫폼 상한 내).