변환 후 깨지는 패턴과 수정
marimo convert가 변환을 마쳐도 에디터에서 바로 실행되지 않는 경우가 많습니다. Jupyter에서 자연스럽게 쓰던 패턴들이 marimo의 규칙과 충돌하기 때문입니다. 이 챕터는 가장 자주 마주치는 다섯 가지 패턴과 각각의 수정 방법을 정리합니다.
PART 02 Ch 05에서 MB002와 MB003의 원리를 자세히 설명했습니다. 여기서는 변환 직후 상황에 집중해, 원래 노트북에서 어떤 코드가 이 에러를 유발하는지에 초점을 맞춥니다.
패턴 1: 같은 변수를 여러 셀에서 재정의 (MB002)
Jupyter에서는 실험 중에 같은 변수에 다른 값을 할당해가며 비교하는 일이 흔합니다.
# Jupyter 셀 1df = pd.read_csv("data_v1.csv")df.head()# Jupyter 셀 2df = pd.read_csv("data_v2.csv") # 데이터를 교체해서 비교df.head()
marimo convert 후 이 두 셀은 각각 별도의 @app.cell 함수가 됩니다. 두 셀이 모두 df를 정의하므로 MB002 에러가 발생합니다.
marimo check notebook.py
critical[multiple-definitions]: Variable 'df' is defined in multiple cells --> notebook.py:12:1 12 | def _(): 13 | df = pd.read_csv("data_v1.csv") | ^ ... 18 | def _(): 19 | df = pd.read_csv("data_v2.csv") | ^Found 2 issues.
수정: 변수 이름을 분리합니다.
# Cell 1import pandas as pddf_v1 = pd.read_csv("data_v1.csv")df_v1.head()# Cell 2df_v2 = pd.read_csv("data_v2.csv")df_v2.head()
두 데이터프레임이 독립적으로 존재하므로, 이후 셀에서 두 가지를 동시에 비교할 수 있습니다.
수정: 조건 분기로 하나의 셀에서 처리합니다.
# Cell 1 (단일 셀로 병합)import pandas as pduse_v2 = Truedf = pd.read_csv("data_v2.csv") if use_v2 else pd.read_csv("data_v1.csv")df.head()
use_v2 값만 바꾸면 어떤 데이터를 쓸지 제어할 수 있습니다.
패턴 2: 셀 실행 순서에 의존하는 코드
Jupyter에서는 셀을 원하는 순서로 실행할 수 있습니다. 의도적으로 특정 셀을 건너뛰거나 역순으로 실행하는 경우가 있습니다.
# Jupyter 셀 1results = []# Jupyter 셀 3 (실제로는 셀 2보다 나중에 실행됨)results.append(model_b_score)# Jupyter 셀 2results.append(model_a_score)
marimo에서는 실행 순서가 DAG 의존 관계로 결정됩니다. 코드가 눈에 보이는 셀 순서와 다르게 실행될 것을 가정했다면, 변환 후 의존 관계가 뒤섞입니다.
수정: 의존 방향을 명시적으로 정리합니다.
# Cell 1 — model_a 평가model_a_score = evaluate(model_a)# Cell 2 — model_b 평가 (model_a와 독립)model_b_score = evaluate(model_b)# Cell 3 — 두 결과를 모아서 비교results = [model_a_score, model_b_score]results
각 셀이 명확히 무엇을 정의하고 무엇에 의존하는지를 드러내면, marimo가 올바른 실행 순서를 자동으로 결정합니다.
패턴 3: in-place 변수 변경
Jupyter에서는 데이터프레임을 만든 셀과 다른 셀에서 그 데이터프레임을 수정하는 패턴이 자주 쓰입니다.
# Jupyter 셀 1df = pd.read_csv("data.csv")# Jupyter 셀 2df["log_value"] = np.log(df["value"]) # df를 직접 수정
marimo에서 이 코드는 문법적으로 에러가 아닙니다. 셀 2는 df를 새로 정의하는 것이 아니라 참조해서 수정하기 때문에 MB002가 발생하지 않습니다. 그러나 이 패턴은 의도치 않은 부작용을 만들기 쉽습니다.
셀 2가 실행되면 df 객체 자체가 바뀝니다. 셀 2에 의존하는 셀들은 재실행되지만, df를 직접 참조하는 다른 셀들은 재실행되지 않습니다. df.describe()를 출력하는 셀이 있다면, 셀 2 실행 전 df를 기준으로 결과를 보여줄 수 있습니다.
수정: 수정된 결과를 새 변수에 할당합니다.
# Cell 1import pandas as pdimport numpy as npdf = pd.read_csv("data.csv")df# Cell 2 — df를 수정하는 대신 새 변수로 받기df_enriched = df.assign(log_value=np.log(df["value"]))df_enriched
df_enriched가 새로 정의된 변수이므로, 이 셀에 의존하는 모든 하위 셀이 정상적으로 재실행됩니다. 원본 df도 그대로 남습니다.
패턴 4: 전역 상태 변경
matplotlib의 rcParams나 pandas의 pd.set_option 같이 전역 설정을 바꾸는 코드가 Jupyter에서는 셀 하나에서 처리되고 이후 셀에 자동으로 영향을 미쳤습니다.
# Jupyter 셀 1import matplotlib.pyplot as pltplt.rcParams["figure.figsize"] = (12, 6)plt.rcParams["font.size"] = 14# Jupyter 셀 2 — 위 설정이 적용된 상태로 실행됨plt.plot([1, 2, 3], [4, 5, 6])plt.show()
marimo에서도 이 코드는 에러 없이 동작합니다. 단, 설정 셀이 시각화 셀보다 먼저 실행되지 않을 수 있는 순서 문제가 생길 수 있습니다.
수정: 임포트와 설정을 하나의 셀에 모읍니다.
# Cell 1 — 임포트와 전역 설정 통합import matplotlib.pyplot as pltplt.rcParams["figure.figsize"] = (12, 6)plt.rcParams["font.size"] = 14# Cell 2 — 시각화 (Cell 1에서 설정한 rcParams가 적용됨)fig, ax = plt.subplots()ax.plot([1, 2, 3], [4, 5, 6])fig
패턴 5: print로만 출력하는 코드
Jupyter에서는 print(result) 방식이 익숙합니다. marimo에서도 print가 동작하지만, 셀의 마지막 표현식을 셀 출력으로 렌더링하는 방식이 더 자연스럽습니다.
# Jupyter 방식result = compute()print(result)# marimo 방식result = compute()result # 마지막 표현식이 셀 출력으로 표시됨
기능 차이는 없습니다만, 데이터프레임 같은 객체는 print보다 셀 출력으로 렌더링할 때 훨씬 보기 좋습니다. 변환 후 코드를 정리할 때 print 호출을 마지막 표현식으로 바꾸는 것을 권장합니다.
변환 후 체크리스트
변환이 끝나면 이 순서로 확인합니다.
marimo check notebook.py— MB002, MB003 등 구조적 에러 확인marimo edit notebook.py— 에디터에서 에러 셀 시각 확인- 에러 셀부터 수정 — 한 번에 전체를 고치려 하지 말고, 에러가 없는 셀부터 위에서 아래로 검토
- 실행 순서 의존 코드 점검 — 변수가 정의되기 전에 참조하는 셀이 있는지 확인
- in-place 수정 패턴 점검 — 같은 변수를 여러 셀에서
.append,[]=방식으로 수정하는 부분 확인
정리
- MB002(multiple-definitions): 같은 변수를 여러 셀에서 정의. 변수 이름 분리 또는 셀 병합으로 해결.
- 셀 순서 의존: 의존 방향을 명시적으로 정리해 DAG가 자동 결정하도록.
- in-place 수정: 새 변수에 결과를 할당해 의존 관계를 명확히.
- 전역 설정: 임포트와 설정을 하나의 셀에 모아 순서 문제 방지.
print출력: 마지막 표현식 방식으로 전환하면 데이터프레임 등의 렌더링이 개선됨.