재현 가능한 데이터 사이언스 워크플로우 — JupyterLab과 Git으로 실험을 관리하는 법
노트북 기반 실험을 재현 가능하게 만드는 프로젝트 구조, 환경 고정, Git 버전 관리, CI 자동화를 실무 관점에서 정리한다.
2026-08-12 · 최초 발행 2025-10-14
몇 달 전에 결과를 낸 노트북을 다시 실행했는데 숫자가 달라지는 경험은 데이터 사이언스 팀이라면 한 번쯤 겪는다. 원인은 대체로 셋 중 하나다 — 라이브러리 버전이 바뀌었거나, 원본 데이터가 조용히 갱신됐거나, 랜덤 시드가 고정되지 않았거나. 재현 가능한 연구(Reproducible Research)란 이 세 가지, 즉 입력(데이터·파라미터)과 처리(코드·라이브러리), 출력(모델·리포트) 전 과정을 고정하고 추적해서 같은 코드·데이터·환경이면 항상 같은 결과가 나오는 상태를 말한다. JupyterLab은 이 실험을 담는 인터랙티브 개발 환경이고, Git은 그 변경 이력과 릴리스를 관리하는 백본이다.
프로젝트 구조가 첫 방어선이다
data/raw → interim → processed처럼 계층형 디렉터리로 단계를 분리하고, 원천 데이터는 읽기 전용으로 취급하며, 파생 산출물은 명시적으로 버전을 매기는 것이 기본 규약이다. 파일명·노트북 파라미터·시드 설정에 일관된 규칙을 정해두지 않으면, 나중에 어떤 노트북이 어떤 데이터로 어떤 결과를 냈는지 추적하기가 급격히 어려워진다.
.
├─ data/
│ ├─ raw/ # 원천 데이터(읽기 전용)
│ ├─ interim/ # 중간 산출물
│ └─ processed/ # 학습/평가용
├─ notebooks/
│ ├─ 01_eda.ipynb
│ └─ 02_train.ipynb # 파라미터화
├─ src/
│ ├─ data.py
│ ├─ features.py
│ └─ train.py
├─ reports/
│ └─ figures/
├─ environment.yml
├─ Makefile
└─ .gitignore
환경은 고정해야 재현성이 된다
conda/mamba나 Poetry로 Python 버전과 패키지를 고정하고 lock 파일을 운영하는 것이 출발점이다. 여기서 한 단계 더 나아가 Docker 컨테이너로 OS·CUDA까지 고정하면 재현성이 훨씬 강해진다. 난수 시드와 결정적 연산 설정, 하드웨어 의존성까지 문서화해두지 않으면 "내 노트북에서는 됐다"는 흔한 함정에 빠진다.
# environment.yml
name: ds-workflow
channels: [conda-forge]
dependencies:
- python=3.11
- jupyterlab=4.2
- pip
- pip:
- papermill==2.5.0
- nbstripout==0.6.1
- nbdime==3.2.1
- black==24.8.0
- isort==5.13.2
- pytest==8.2.0
노트북은 실험 산출물이지 로직 저장소가 아니다
노트북은 EDA·프로토타이핑·리포팅에는 최적화되어 있지만, 핵심 로직을 노트북 안에만 두면 재사용도 테스트도 어려워진다. 노트북은 실험·리포트 산출물로 취급하고, 핵심 로직은 src 모듈로 빼는 것이 이 워크플로우의 핵심 원칙이다. Papermill로 노트북을 파라미터화해서 일괄 실행하면 시드나 하이퍼파라미터를 바꿔가며 같은 노트북을 재현 가능하게 여러 번 돌릴 수 있고, nbstripout·nbdime으로 노트북의 출력 셀을 정리하면 Git diff가 코드 변경만 깔끔하게 보여준다. 데이터 검증(스키마·통계) 단계를 파이프라인 앞쪽에 넣어, 실패하면 바로 중단하고 원인을 리포팅하게 만드는 것도 중요하다.
# Makefile
ENV=ds-workflow
run-lab:
conda run -n $(ENV) jupyter lab
execute:
conda run -n $(ENV) papermill notebooks/02_train.ipynb notebooks/02_train.out.ipynb -p seed 42 -p n_estimators 200
test:
conda run -n $(ENV) pytest -q
lock:
conda env export --name $(ENV) --from-history > environment.lock.yml
Git은 코드만이 아니라 실험 스냅샷을 관리한다
main/trunk 기반 브랜칭, 작은 단위 커밋, PR 리뷰는 여느 소프트웨어 프로젝트와 다르지 않다. 데이터 사이언스 워크플로우에서 추가되는 건 두 가지다. 하나는 태그로 실험·모델 릴리스를 스냅샷화하는 것, 다른 하나는 대용량 데이터를 Git LFS나 DVC로 별도 관리하는 것이다. pre-commit 훅으로 포맷팅과 노트북 정리를 자동화해두면 리뷰어가 코드 로직에만 집중할 수 있다.
git init
git lfs install
nbstripout --install
nbdime config-git --enable # 최신 정보 확인 필요
# .pre-commit-config.yaml
repos:
- repo: https://github.com/kynan/nbstripout
rev: 0.6.1
hooks: [{ id: nbstripout }]
- repo: https://github.com/psf/black
rev: 24.8.0
hooks: [{ id: black }]
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks: [{ id: isort }]
CI가 재현성을 실제로 검증한다
환경을 고정하고 노트북을 정리하는 것만으로는 재현성이 "보장"되지 않는다 — 실제로 매번 실행해서 확인해야 한다. GitHub Actions 같은 CI에서 테스트를 돌리고 Papermill로 노트북을 실제 실행한 뒤 결과를 아티팩트로 업로드하면, PR마다 "이 변경이 실제로 재현 가능한 결과를 내는가"를 자동으로 검증할 수 있다.
# .github/workflows/ci.yml
name: ci
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mamba-org/provision-with-micromamba@v15
with:
environment-file: environment.yml
cache-downloads: true
cache-environment: true
- name: Run tests
run: pytest -q
- name: Execute notebook
run: papermill notebooks/02_train.ipynb notebooks/02_train.out.ipynb -p seed 42
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: reports
path: |
notebooks/02_train.out.ipynb
reports/**
전체 흐름을 그려보면 입력에서 검증, 전처리, 학습, 리포트, 버전 관리, 배포까지 한 방향으로 흐르되 검증 단계에서 실패하면 즉시 멈추고 알림을 보내는 구조가 된다.
실무에서는 이렇게 쓰인다
예측 모델 개발 파이프라인이라면 데이터 적재→검증→전처리→학습→평가→리포트를 자동화하고 PR마다 CI로 재현 시험을 돌리며, 모델 릴리스는 Git 태그로 스냅샷하고 모델·메트릭·노트북 결과는 아티팩트로 보관한다. 월별로 갱신되는 공개 데이터를 다루는 주기 리포트/대시보드라면 Papermill로 노트북을 일괄 실행하고, 실패 시 알림·이슈를 생성하며 원천 데이터의 스키마 변경을 추적해야 한다. 논문이나 블로그 결과를 재현해야 하는 상황에서는 환경 락 파일과 컨테이너 이미지를 함께 공유하고, 데이터 접근 권한·버전 정책을 포함한 재현 체크리스트를 두면 검증 시간을 줄일 수 있다.
어디서 JupyterLab을 돌릴 것인가
로컬 JupyterLab은 진입장벽이 낮지만 개인 머신 환경에 의존해 환경 편차가 크고 재현성이 취약하다. 리모트(공유 서버) JupyterLab은 GPU 같은 고성능 자원을 쓸 수 있고 공용 환경을 표준화할 수 있지만, 계정·자원 예약과 관리 정책이 필요하고 동시 사용자 제약이 생긴다. 컨테이너화(Docker/Podman) JupyterLab은 이미지로 환경을 고정해 재현성·이식성이 가장 높고 오토스케일링도 가능하지만, 이미지 관리 자체가 새로운 운영 부담이 된다. 셋 중 무엇을 택하든 "얼마나 빨리 시작할 수 있는가"와 "얼마나 확실하게 재현되는가"는 반대 방향으로 움직인다는 점을 감안해야 한다.
보안과 거버넌스도 워크플로우의 일부다
원천 데이터는 읽기 전용으로 두고 식별자는 마스킹하며, 접근 토큰은 .env나 Vault 같은 별도 저장소에 분리해야 한다 — 보안을 강화할수록 운영 복잡도는 늘어나는 트레이드오프가 따른다. 노트북 안에 비밀 값을 하드코딩하지 않고 런타임에 주입하는 것도 같은 맥락이다. 커밋 서명, 릴리스 노트, 실험 메타데이터(metrics.json이나 MLflow 등)를 저장해두면 감사·추적이 쉬워지지만 그만큼 저장 비용도 늘어난다.
이런 체계를 갖추면 신규 인력의 온보딩 시간이 3050% 단축되고, 재현 실패율이 60% 이상 줄며, 자동 포맷팅과 노트북 노이즈 제거 덕분에 PR당 리뷰 소요 시간도 2030% 줄어든다.
결국 재현 가능한 연구는 데이터 사이언스 품질 보증의 기반이자 협업 생산성의 기반이다. JupyterLab을 실험·리포팅 허브로, Git을 변경 이력·릴리스 백본으로 결합하고 여기에 환경 고정·데이터 검증·CI 자동화를 통합하는 것이 핵심이며, 표준 구조와 프로세스를 먼저 세운 뒤 컨테이너화나 실험 추적 도구를 점진적으로 확장하는 순서가 안전하다.