iBetter Books
수정

팀 협업 _ git diff와 코드 리뷰 워크플로우

marimo의 .py 파일 형식이 팀 협업에서 어떤 차이를 만드는지, 이 챕터에서 직접 확인합니다. PART 01 Ch 04에서 간단히 언급했던 git diff 이야기를 실제 시나리오와 함께 풀어봅니다.

.ipynb의 git diff 문제

Jupyter .ipynb는 JSON 파일입니다. 셀 코드뿐 아니라 실행 결과, 메타데이터, 실행 횟수까지 파일에 저장됩니다. 코드 한 줄을 바꿔서 저장하면 diff에는 코드 변경뿐만 아니라 출력 JSON도 함께 나타납니다.

아래는 슬라이더 기본값을 5에서 7로 바꾼 경우 .ipynb git diff의 일부입니다.

-   "source": [-    "slider = widgets.IntSlider(value=5, min=0, max=10)\n",+   "source": [+    "slider = widgets.IntSlider(value=7, min=0, max=10)\n",     "display(slider)"    ],    "outputs": [     {      "data": {-      "application/vnd.jupyter.widget-view+json": {-       "model_id": "a3f2b1c9...",+      "application/vnd.jupyter.widget-view+json": {+       "model_id": "d8e4a2c7...",       }      }     }    ],-   "execution_count": 3+   "execution_count": 4

실제 코드 변경은 value=5에서 value=7로 바뀐 한 줄입니다. 그런데 model_id, execution_count, 출력 JSON까지 바뀌어서 diff가 복잡해집니다. PR 리뷰에서 무엇이 실제 변경인지 파악하기 어렵습니다.

.py의 git diff

같은 변경을 marimo .py 파일에서 하면 diff가 이렇습니다.

-slider = mo.ui.slider(0, 10, value=5, label="임계값")+slider = mo.ui.slider(0, 10, value=7, label="임계값")

코드만 바뀝니다. 출력은 파일에 저장되지 않으므로 diff에 출력이 포함되지 않습니다.

팀원이 PR을 열었을 때 리뷰어는 실제 코드 변경만 봅니다. 변경 의도를 파악하고 피드백을 남기는 데 집중할 수 있습니다.

실제 marimo 파일 diff 예시

기능을 추가하는 PR을 예로 듭니다. 데이터 필터링 조건에 연도 선택 위젯을 추가했습니다.

@@ -18,8 +18,14 @@ app = marimo.App() @app.cell def _(df):+    year_slider = mo.ui.slider(2018, 2024, value=2020, label="기준 연도")+    year_slider+    return (year_slider,)++[email protected]+def _(df, year_slider):-    filtered = df[df["year"] >= 2020]+    filtered = df[df["year"] >= year_slider.value]     return (filtered,)

리뷰어는 이 diff만 보고도 무엇이 바뀌었는지 알 수 있습니다. 연도 필터가 하드코딩에서 위젯으로 바뀌었고, 의존 관계도 명확합니다.

marimo check를 CI에 통합하기

코드 변경이 구조적 에러(MB002, MB003)를 만들지 않는지 CI에서 자동으로 검사할 수 있습니다.

marimo check analysis.py

문제가 없으면 출력 없이 종료됩니다. 문제가 있으면 에러 내용을 출력합니다. CI에서 이 동작을 이용해 구조 검사를 자동화할 수 있습니다.

GitHub Actions 예시입니다.

name: marimo checkon: [push, pull_request]jobs:  check:    runs-on: ubuntu-latest    steps:      - uses: actions/checkout@v4      - name: Python 설정        uses: actions/setup-python@v5        with:          python-version: "3.11"      - name: marimo 설치        run: pip install "marimo==0.23.*"      - name: marimo check 실행        run: |          for f in notebooks/*.py; do            marimo check "$f"          done

PR이 올라올 때마다 notebooks/ 폴더의 모든 marimo 파일을 점검합니다. MB002, MB003 같은 에러가 있는 코드는 이 단계에서 확인할 수 있습니다.

머지 충돌 감소

Jupyter 노트북을 팀에서 함께 편집할 때 머지 충돌이 잦습니다. JSON 구조 특성상 두 사람이 다른 셀을 수정해도 JSON 배열 인덱스가 충돌하거나 출력 데이터 때문에 충돌이 발생합니다.

marimo .py 파일은 일반 Python 파일입니다. 서로 다른 @app.cell 함수를 수정하면 각 함수가 독립적인 코드 블록이므로 충돌 가능성이 낮습니다. 충돌이 발생하더라도 Python 코드 충돌은 JSON 충돌보다 이해하고 해결하기 쉽습니다.

노트북을 스크립트처럼 실행하기

marimo 파일은 표준 Python 파일이므로 python 명령으로 직접 실행할 수 있습니다.

python analysis.py

if __name__ == "__main__": app.run() 부분이 있으면 marimo 앱 모드로 실행됩니다. 이 줄이 없어도 파일은 임포트 가능합니다.

CI에서 단순한 임포트 오류나 SyntaxError를 잡으려면 python analysis.py를 실행하거나, marimo 파일을 모듈로 임포트하는 방식으로 검증할 수 있습니다.

팀 도입 시 고려사항

팀 전체가 동시에 marimo로 전환하기 어렵다면 다음 방향을 고려합니다.

저장소 규칙 정의. 새로 만드는 노트북은 marimo .py로, 기존 노트북은 .ipynb를 그대로 유지하는 기간을 둡니다. 두 형식이 섞여 있어도 됩니다.

marimo check를 CI에 넣기. .py 파일에만 적용합니다. .ipynb 파일은 기존 CI 규칙대로 처리합니다.

PR 리뷰 경험 공유. 처음 PR을 올린 사람이 .py diff가 얼마나 읽기 좋은지 팀에 보여줍니다. 직접 경험한 사람이 설득력이 높습니다.

파일 열기를 간단하게. .py 확장자를 클릭했을 때 marimo 에디터가 열리도록 IDE 설정을 공유하거나, Makefilemake edit f=notebooks/analysis.py 같은 단축 명령을 만들어둡니다.

edit:
	marimo edit $(f)

check-all:
	@for f in notebooks/*.py; do \
	    echo "Checking $$f..."; \
	    marimo check "$$f" || exit 1; \
	done

정리

  • marimo .py 파일은 코드만 저장하므로 git diff가 코드 변경만 보여줍니다. .ipynb의 출력 JSON 노이즈가 없습니다.
  • PR 리뷰가 명확해집니다. 리뷰어가 실제 코드 변경에 집중할 수 있습니다.
  • marimo check analysis.py를 CI에 추가하면 MB002, MB003 에러를 PR 단계에서 막을 수 있습니다.
  • 파일이 표준 Python이라서 머지 충돌이 줄고, 충돌이 생겨도 해결하기 쉽습니다.
  • 팀 전환은 점진적으로 진행합니다. 새 파일부터 marimo를 적용하고, CI에 marimo check를 추가하는 것이 시작점으로 좋습니다.