1 정의
DAG 구축 4 단계 레시피의 후반부 (Buisson, 2021, Ch.4):
3. 반복 (Iterate)
├── 미관측 변수의 Proxy 식별
├── 추가 원인 탐색 (외부 변수)
└── 새 변수 추가 시 위 단계 재실행
4. 단순화 (Simplify)
├── 무관한 chain 축약
├── Slicing/Aggregating 으로 가독성 ↑
└── Cycle breaking
이 단계는 수렴 (convergence) 까지 반복. 새 변수를 추가해도 분석 결과가 거의 변하지 않으면 멈춤.
분석가의 자연스러운 함정:
- 너무 일찍 멈춤: 단계 1~2 후 “충분하다” 판단. 잔여 confounding 큼.
- 너무 늦게 멈춤: 끝없는 반복. 작업 완료가 안 됨.
- 단순화 회피: 정확성 추구로 인해 모든 변수 유지. 보고서가 읽히지 않음.
대신 권장:
- 변수 추가의 수익 체감 (decreasing returns) 을 인식
- 단순화는 분석 마지막에 한 번 통째로
- “충분히 좋은” CD 가 “완벽한” CD 보다 가치
→ Buisson 표현: “perfection is the enemy of done.”
2 단계 3-1: 데이터 검증 도구
2.1 도구 선택 매트릭스
| 변수 1 | 변수 2 | 도구 | 출력 |
|---|---|---|---|
| 수치 | 수치 | Pearson 상관 | \(r \in [-1, 1]\) |
| Binary | Binary | Pearson 또는 Cramer’s V | \(r\) 또는 \(V\) |
| 범주 (3+ 수준) | 범주 (3+ 수준) | Cramer’s V | \(V \in [0, 1]\) |
| 수치 | 범주 | Group Means + ANOVA | F 통계량, \(\eta^2\) |
| 수치 | Binary | Point-biserial 상관 (= Pearson) | \(r\) |
해석 임계값: Treatment ↔︎ Outcome 의 연관 강도와 같은 자릿수 이상 → 회귀 포함 후보.
“Binary 변수를 0/1 로 처리하고 Pearson 상관 계산. 결과가 Cramer’s V 와 매우 가깝다.”
이 trick 이 왜 작동하는가:
- Binary 변수는 분포가 단순 (두 점)
- Pearson 의 가정 (정규성) 위반이지만 이진의 경우 부분적으로 유효
- Cramer’s V 는 binary-binary 일 때 Pearson 의 절댓값과 거의 동일
→ 분석가가 “통계학자에게는 비밀로 하고” Pearson 으로 binary 처리. 코드 단순화.
Production 분석에서는 정확한 도구 (Cramer’s V) 를 사용하지만, 1차 탐색에서는 Pearson 으로 충분.
2.2 호텔 사례의 상관 행렬 분석
| NRD | Cancel | Children | ADR | PrevCancel | RepGst | Year | |
|---|---|---|---|---|---|---|---|
| NRD | 1.00 | 0.16 | -0.03 | -0.04 | 0.15 | -0.02 | -0.02 |
| Cancel | 1.00 | 0.06 | 0.13 | 0.13 | -0.09 | 0.09 | |
| Children | 1.00 | 0.34 | -0.03 | -0.05 | 0.03 | ||
| ADR | 1.00 | -0.07 | -0.16 | 0.19 | |||
| PrevCancel | 1.00 | 0.25 | -0.12 | ||||
| RepGst | 1.00 | 0.02 | |||||
| Year | 1.00 |
Treatment (NRD) ↔︎ Outcome (Cancel): 0.16
임계값 0.1 적용:
- 포함 후보: PrevCancel (0.15, 0.13), ADR (0.13)
- 잠재 후보: Children (0.06), RepGst (-0.09), Year (-0.02, 0.09)
분석가의 시선 흐름:
- Treatment 행과 Outcome 행 먼저 — 1순위 후보 식별
- 변수 간 강한 상관 다음 — 다중공선성·집계 후보 (Children-ADR 의 0.34)
- Treatment·Outcome 외 변수의 변수: 무시 (지금은) — 단계 3 의 “추가 원인” 탐색에서 다룸
이 흐름이 효율적. 모든 셀을 동등하게 보면 시간 낭비.
2.3 범주 변수 — Cramer’s V 행렬
| NRD | Cancel | DistCh | MktSeg | CustTyp | PrevCan | RepGst | Country | Quarter | |
|---|---|---|---|---|---|---|---|---|---|
| NRD | 1.00 | 0.16 | 0.03 | 0.28 | 0.01 | 0.15 | 0.02 | 0.15 | 0.03 |
| Cancel | 1.00 | 0.15 | 0.22 | 0.13 | 0.13 | 0.09 | 0.16 | 0.07 | |
| DistCh | 1.00 | 0.73 | 0.09 | 0.14 | 0.35 | 0.16 | 0.07 | ||
| MktSeg | 1.00 | 0.33 | 0.17 | 0.4 | 0.17 | 0.07 | |||
| CustTyp | 1.00 | 0.07 | 0.11 | 0.11 | 0.06 |
발견:
- DistCh ↔︎ MktSeg = 0.73 — 거의 동일 정보. 하나만 사용
- Quarter 모든 변수와 V < 0.1 — 제거 후보
- CustTyp ↔︎ MarketSeg = 0.33 — 중간 다중공선성
Quarter 가 다른 변수와 V < 0.1 → 분기별 패턴이 약함.
비즈니스 함의:
- 호텔의 NRD·취소 패턴이 계절성을 거의 보이지 않음
- “여름 휴가철 NRD 정책” 같은 계절 의존 가설 기각
- 계절성을 보고 싶으면 더 작은 단위 (월, 주) 로 변수 만들어야
CD 갱신:
Quarter 제거
[Seasonality] (미관측, 추후 연구) ──→ NRD, Cancel
미관측 노드로 남기면 추후 더 정밀한 시간 변수 (월별 dummy) 로 검증 가능.
2.4 수치-범주 — Group Means + Bootstrap CI
평균 차이가 진짜인지, 표본 잡음인지 점검:
import numpy as np
def bootstrap_ci(values, n_iter=1000, alpha=0.05):
means = []
for _ in range(n_iter):
sample = np.random.choice(values, len(values), replace=True)
means.append(np.mean(sample))
lower = np.percentile(means, 100 * alpha / 2)
upper = np.percentile(means, 100 * (1 - alpha / 2))
return lower, upper
# Transient
transient_adr = df[df["CustomerType"] == "Transient"]["ADR"]
lo, hi = bootstrap_ci(transient_adr)
print(f"Transient ADR 95% CI: [{lo:.2f}, {hi:.2f}]")CI 가 다른 그룹의 평균과 겹치지 않으면 → 통계적으로 유의한 차이.
Buisson 의 권장: Bootstrap 은 분석가의 친구. 평균·차이·신뢰구간 모두 한 도구로. (자세한 내용은 Ch.7 — Phase A-BUI7 시리즈에서.)
2.5 검증 후의 CD
Customer Type, Market Segment Year
Distribution Channel ↓ ↓
↓ ↓ ↓
│ │ │
│ PrevCancel ───────────┤
│ IsRepeatedGuest │
│ Children │
│ ADR │
│ Country │
│ ↓ │
└────→ NRDeposit ──→ IsCanceled
↗
(Quarter 제거됨)
검증 단계의 가정 (단순 상관 = 인과) 의 한계: “변수 간 상관 자체가 confounded 일 수도 있음.”
예: PrevCancel 과 Personal Char 의 상관이 IsRepeatedGuest 를 통해 매개될 수도. 그러나 우리 분석 대상 (NRD → Cancel) 에 영향 없으면 무시 가능.
→ “흰 거짓말 (white lies)”: 정확하지 않지만 분석에 충분.
3 단계 3-2: Proxy 식별
3.1 미관측 변수의 Proxy
Proxy 변수: 미관측 변수의 효과를 부분적으로 캡처하는 관측 변수.
호텔 사례:
[Conscientiousness] (미관측)
↓ ↓
[ConfirmEmail] IsCanceled
(관측 — proxy)
↓
IsCanceled
- 성실한 사람이 (1) 확인 이메일 요청 ↑, (2) 취소 안 함
- 확인 이메일은 성실성의 행동적 흔적 (proxy)
- 회귀에 ConfirmEmail 포함 → 성실성 일부 통제
확인 이메일 요청은 여러 원인이 있다:
- 성실성 (의도 진지)
- 디지털 친숙도 (디지털 채널 익숙)
- 일정 정확성 필요 (직무상)
만약 회귀에 ConfirmEmail 만 넣으면, 성실성 외 다른 효과도 잡힘. 부분 통제.
→ Proxy 는 잔여 confounding 감소 도구. 완전 제거 아님.
다중 proxy 사용 가능 — 같은 미관측 변수에 대한 여러 proxy 를 동시에 회귀에 포함하면 더 깨끗한 통제.
3.2 Proxy 식별의 절차
- 미관측 변수 목록 (CD 의 음영 노드)
- 각 미관측에 대해 brainstorm:
- 이 trait/intent 이 강하면 어떤 행동을 더 할까?
- 어떤 데이터가 그 행동의 흔적?
- 데이터 검증: 후보 proxy 와 미관측의 (가설적) 효과 점검
- 회귀 포함: 검증된 proxy 를 회귀에
호텔 사례:
| 미관측 변수 | Proxy 후보 | 근거 |
|---|---|---|
| Conscientiousness | ConfirmEmail, EarlyCheckIn, NoLastMinuteChange | 성실 행동의 흔적 |
| TripReason (휴가) | Children, StayLength, Q3Q4 | 휴가 패턴 |
| TripReason (출장) | CorporateCard, MidWeekCheckIn | 비즈니스 패턴 |
| Risk Tolerance | ADR (가격 수용), CancellationProtect 거부 | 위험 수용 |
3.3 행동 페르소나 (Behavioral Personas)
여러 proxy 를 묶어 의미 있는 페르소나 생성:
Conscientiousness + IsRepeatedGuest + ConfirmEmail
↓
"Recurring Business Traveler" (binary)
↓
NRDeposit, IsCanceled
이 페르소나가 회귀의 단일 변수로. 비즈니스 의사결정에 직접 매핑.
페르소나의 가치:
- 분석가가 도메인 직관을 데이터로 검증
- 마케팅·운영 팀에게 의미 있는 단위
- 정밀한 미관측 trait 보다 페르소나가 더 안정적
→ Buisson 의 권장: “원시 변수 → 페르소나 → 회귀” 의 3 단계.
4 단계 3-3: 추가 원인 탐색
4.1 외부 변수 (Outer Variables)
Outer Variable: CD 에서 부모 (parent) 가 없는 노드. 다른 변수의 영향을 받지 않는 변수.
호텔 사례에서 outer 변수:
- Year (시간 — 부모 없음, 자명)
- 인구통계 (Country, 등) — 더 깊은 trait 가 있을 수 있지만 보통 outer 처리
새 confounder 가능성:
“Outer 변수 A 가 NRD 만 영향, B 가 Cancel 만 영향. A 와 B 의 공통 원인 C 가 있다면, C 가 NRD 와 Cancel 의 confounder.”
Year 의 부모가 없으니 그쪽은 끝. 그러나 다른 outer 변수 (예: Country) 의 부모는 있을 수 있다:
- Country 의 원인: 호텔 마케팅 지역 전략, 항공 노선, 국경 정책
- 이 원인들 중 어떤 것이 NRD 또는 Cancel 의 직접 원인일 수 있음 → 추가 confounder
→ CD 를 “외부로” 확장하면서 새 confounder 발굴.
그러나 수익 체감: 외부로 갈수록 분석 대상과 연관 약해짐. 1~2 단계 외부 정도면 충분.
4.2 반복의 종료 조건
수렴 기준:
- 새 변수 추가 시 NRD 의 추정 OR 변화 < 5%
- 새 후보 변수의 Treatment·Outcome 상관 < 임계값
- 도메인 직관으로 더 떠올릴 변수 없음
이 중 하나가 충족되면 단계 3 종료.
분석가가 “수렴” 으로 착각하지만 실제로는 분석가의 피로 인 경우가 많다.
- 새 변수 brainstorm 에 노력 들기 어려움
- “충분하다” 핑계로 일찍 종료
- 잔여 confounding 큼
해결:
- 분석가가 동료에게 “5분 brainstorm” 요청
- 도메인 전문가 인터뷰 (변수 후보 추가)
- 다른 회사·연구의 사례 참조
→ 외부 시각이 분석가의 사각지대를 보완.
5 단계 4: 단순화
5.1 단순화의 5 가지 도구
5.2 호텔 사례의 단순화
검증 후 CD 에 남은 변수:
{IsRepeatedGuest, Children, Year, Country, ADR,
CustomerType, MarketSegment, DistCh, PrevCancel}
이 중:
- IsRepeatedGuest, Children, Year: NRD 와 Cancel 양쪽으로 가는 화살표가 약함. PrevCancel 과 ADR 이 같은 정보 흡수. 제거 가능.
- Country, ADR: 강한 confounder. 유지.
- CustomerType vs MarketSegment vs DistCh: 강하게 상관. 하나만 (예: MarketSegment) 유지.
- PrevCancel: 핵심 confounder. 유지.
단순화된 CD:
[Personal Characteristics] [Trip Reason] [Cancel Reason]
↓ ↓ ↓
│ │ │
┌──────────────────────────────────────────────────┐
│ Country, ADR, MarketSegment, PrevCancel │
│ ↓ │
│ NRDeposit ──→ IsCanceled │
│ ↗ │
└──────────────────────────────────────────────────┘
이게 최종 분석 CD. 회귀에 5 변수 (Country, ADR, MarketSegment, PrevCancel, NRD) 사용.
5.3 보고용 vs 분석용 CD
분석가가 두 CD 를 유지:
- 분석용 CD (정밀): 모든 변수 포함, 미관측 노드까지. 분석가의 작업 도구.
- 보고용 CD (단순): 핵심 confounder 만. 비즈니스 파트너에게.
분석용 (15 노드, 25 화살표) ← 분석가 사용
↓ 단순화
보고용 (5 노드, 6 화살표) ← 회의·문서
비즈니스 파트너가 “왜 이 변수만 통제했나” 질문 시:
- 보고용 CD 보여주고 핵심 설명
- 분석용 CD 는 부록으로 첨부 (요청 시)
→ CD 의 layered 표현. 청중에 따라 깊이 조절.
6 응용 — 다른 비즈니스 사례의 단계 3·4
6.1 이커머스 — 추천 효과의 단순화
분석 질문: “추천이 구매를 높이는가?”
분석용 CD (단계 1~3 후):
- Past Actions (구매 빈도, 카트 추가)
- Intentions (검색 의도, 가격 비교)
- Cognition (브랜드 인지)
- Personal (인구통계)
- Business (디바이스, 시간대, 페이지)
- Time (계절)
→ 약 15~20 변수.
단순화 (단계 4):
- 디바이스·시간대·페이지 → “Browse Context” 묶음
- 인구통계 → “Demographic Index”
- 검색 의도 (proxy: 검색 키워드 수) → 1 변수
보고용: 5 변수 + Treatment + Outcome.
6.2 SaaS — Onboarding 효과의 단순화
분석 질문: “튜토리얼이 retention 을 높이는가?”
검증 후 핵심 변수:
- Past SaaS Experience (Past Actions)
- Job Role (Personal — proxy for intent)
- Signup Channel (Business)
- Cohort Month (Time)
단순화: 4 변수 + Treatment + Outcome. 모든 confounder 가 동시에 covariate.
→ “튜토리얼 효과 = Job Role·Signup Channel·Cohort 통제 후 retention 차이.”
이 한 문장이 보고용 결론.
7 코드 예시 — Python 으로 단계 3·4 시연
7.1 반복적 변수 추가의 효과 추적
import numpy as np
import pandas as pd
import statsmodels.api as sm
# 가상 호텔 데이터
np.random.seed(42)
n = 5000
# 미관측 trait
trait = np.random.normal(0, 1, n)
# 관측 변수 — trait 과 상관
country = (trait * 0.3 + np.random.normal(0, 1, n)).clip(-3, 3)
adr = trait * 0.5 + np.random.lognormal(4.5, 0.3, n) * 0.2
prev_cancel = (trait * 0.4 + np.random.uniform(-1, 1, n) > 0).astype(int)
market_seg = (trait + np.random.normal(0, 1, n) > 0.5).astype(int)
# Treatment (NRD) — trait 과 prev_cancel 영향
nrd_logit = -3 + 1 * trait + 1.5 * prev_cancel
nrd_prob = 1 / (1 + np.exp(-nrd_logit))
nrd = (np.random.uniform(0, 1, n) < nrd_prob).astype(int)
# Outcome — trait 과 prev_cancel 영향, NRD 진짜 효과는 0
cancel_logit = -1 + 0.8 * trait + 0.5 * prev_cancel
cancel_prob = 1 / (1 + np.exp(-cancel_logit))
cancel = (np.random.uniform(0, 1, n) < cancel_prob).astype(int)
df = pd.DataFrame({
"NRD": nrd, "Cancel": cancel,
"PrevCancel": prev_cancel, "MarketSeg": market_seg,
"Country": country, "ADR": adr,
})
# 변수를 단계적으로 추가하며 NRD OR 추적
print("=== 변수 추가에 따른 NRD OR 변화 ===")
sequence = ["NRD", "+ PrevCancel", "+ MarketSeg", "+ Country", "+ ADR"]
features = []
for step, label in enumerate(sequence):
if step == 0:
features = []
else:
features.append(["PrevCancel", "MarketSeg", "Country", "ADR"][step-1])
cols = ["NRD"] + features
X = sm.add_constant(df[cols])
m = sm.Logit(df["Cancel"], X).fit(disp=False)
or_nrd = np.exp(m.params["NRD"])
print(f" Step {step}: {label:20s} OR(NRD) = {or_nrd:.2f}")예상 결과:
| Step | 모형 | OR(NRD) |
|---|---|---|
| 0 | NRD 만 | ~5 |
| 1 | + PrevCancel | ~3.5 |
| 2 | + MarketSeg | ~2.8 |
| 3 | + Country | ~2.4 |
| 4 | + ADR | ~2.2 |
각 단계마다 OR 이 줄어듦. 수렴 추세가 보이면 멈춤.
남은 OR 2.2 가 진짜 NRD 효과인지, 잔여 confounding 인지 결정 못함. → 미관측 trait 가 통제 안 됨.
해결: RCT 또는 IV.
→ 이 코드가 분석가의 수렴 진단 도구. 변수 추가가 OR 을 거의 바꾸지 않으면 단계 3 종료.
7.2 Proxy 식별 시뮬레이션
# 미관측 trait 의 proxy 후보 식별
np.random.seed(42)
n = 5000
trait = np.random.normal(0, 1, n)
# 여러 후보 변수 (일부는 trait 과 상관)
proxy1 = trait * 0.7 + np.random.normal(0, 0.5, n) # 강한 proxy
proxy2 = trait * 0.3 + np.random.normal(0, 0.7, n) # 약한 proxy
proxy3 = np.random.normal(0, 1, n) # 무관 (trait 과 독립)
outcome = trait * 1.0 + np.random.normal(0, 0.5, n)
df_proxy = pd.DataFrame({
"trait": trait,
"proxy1": proxy1,
"proxy2": proxy2,
"proxy3": proxy3,
"outcome": outcome,
})
# Outcome 과 후보 상관
print("\n=== Proxy 후보 점검 ===")
for col in ["proxy1", "proxy2", "proxy3"]:
r = df_proxy[col].corr(df_proxy["outcome"])
print(f" corr({col}, outcome) = {r:.3f}")예상 결과:
- proxy1 ↔︎ outcome: ~0.7 (강한 proxy)
- proxy2 ↔︎ outcome: ~0.3 (약한 proxy)
- proxy3 ↔︎ outcome: ~0.0 (무관)
proxy1 가 trait 의 효과를 가장 잘 캡처. 회귀에 포함 시 잔여 confounding ↓.
비즈니스 함의: 미관측 변수 (성실성) 의 강한 proxy (확인 이메일 요청) 가 회귀에 포함되면 잔여 편향 크게 감소.
7.3 단순화된 CD 시각화
import networkx as nx
import matplotlib.pyplot as plt
# 분석용 CD (전체)
G_full = nx.DiGraph()
G_full.add_edges_from([
("[Trait]", "PrevCancel"),
("[Trait]", "MarketSeg"),
("[Trait]", "Country"),
("[TripReason]", "NRD"),
("[TripReason]", "Cancel"),
("PrevCancel", "NRD"),
("PrevCancel", "Cancel"),
("MarketSeg", "NRD"),
("MarketSeg", "Cancel"),
("Country", "NRD"),
("Country", "Cancel"),
("ADR", "NRD"),
("ADR", "Cancel"),
("Year", "NRD"),
("Year", "Cancel"),
("NRD", "Cancel"),
])
# 보고용 CD (단순)
G_simple = nx.DiGraph()
G_simple.add_edges_from([
("Personal Char", "NRD"),
("Personal Char", "Cancel"),
("PrevCancel", "NRD"),
("PrevCancel", "Cancel"),
("ADR", "NRD"),
("ADR", "Cancel"),
("NRD", "Cancel"),
])
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# Full
pos_full = nx.spring_layout(G_full, seed=42)
nx.draw(G_full, pos_full, ax=axes[0], with_labels=True,
node_color="lightblue", node_size=2000, font_size=8, arrows=True)
axes[0].set_title(f"분석용 CD ({G_full.number_of_nodes()} 노드)")
# Simple
pos_simple = nx.spring_layout(G_simple, seed=42)
nx.draw(G_simple, pos_simple, ax=axes[1], with_labels=True,
node_color="lightgreen", node_size=2500, font_size=10, arrows=True)
axes[1].set_title(f"보고용 CD ({G_simple.number_of_nodes()} 노드)")
plt.tight_layout()
plt.savefig("cd_full_vs_simple.png", dpi=80)
plt.show()분석용: 11 노드, 16 화살표 — 분석가의 사고 도구 보고용: 5 노드, 7 화살표 — 비즈니스 의사결정 도구
같은 분석이지만 청중에 따라 다른 표현. CD 가 layered 표현이라는 의미.
→ 분석 보고서에는 보고용 CD 가 본문, 분석용 CD 가 부록. 청중이 선택해서 깊이 조절.
8 5 단계 종합 — DAG 구축의 전체 레시피
1. 측정하려는 관계로 시작
Cause of Interest ──→ Effect of Interest
2. 후보 변수 식별
- 행동 과학·비즈니스 지식 활용
- 6 카테고리 brainstorm
- 데이터부터 보지 않음 (WYSIATI 함정 회피)
3. 데이터로 관측 변수 검증
- Pearson 상관 (수치)
- Cramer's V (범주)
- Group means + Bootstrap CI (혼합)
- VIF (다중공선성)
4. 반복적 확장
- Proxy 식별 (미관측의 행동 흔적)
- 추가 원인 탐색 (외부 변수)
- 수렴까지 반복
5. 단순화
- Chain 축약·확장
- Slicing·Aggregating
- Cycle breaking
- 분석용 vs 보고용 CD 분리
목표: 현재 가진 데이터로 가장 좋은 인과 추정. 완벽한 CD 가 아닌, 사용 가능한 CD.
“이 절차는 매우 누적적이고 전이 가능하다. 한 분석에서 한 번 해두면, 그 비즈니스의 인과 관계 지식이 다음 분석에 재사용된다.”
— Buisson
첫 분석: 6주 소요 (모든 변수 brainstorm + 검증 + 단순화) 두 번째 비슷한 분석: 1주 (기존 CD 재활용 + 새 변수 보강) 신규 팀원 온보딩: 2일 (CD 와 변수 목록 받으면 시작 가능)
→ DAG 가 조직의 자산. 한 사람의 분석이 아니라 팀의 인과 추론 framework 가 됨.
9 관련 주제
9.1 Ch.4 의 형제 글 (Ch.4 완결)
- E-BUI4-0 DAG 0부터 짓기 overview — 4 단계 레시피 전체
- E-BUI4-1 비즈니스 문제와 후보 변수 식별 — 단계 1
- E-BUI4-2 변수 6 범주 분류 — 단계 1·2 깊이
9.2 후속 챕터
- E-BUI5-0 Deconfounding overview — Ch.5: DCC 와 Backdoor Criterion 으로 confounder 통제
9.3 이전 챕터
- E-BUI3-0 인과 다이어그램 도입 overview — CD 의 기본 구조
- E-BUI3-2 체인과 포크 구조 — Mediator·Confounder
- E-BUI3-3 충돌 변수와 경로 — Collider·Path
9.4 Hernan 정통 cross-link
- Causal_Inference/07 교란 — Confounding 의 학술적 정의
- Causal_Inference/04 관찰 연구와 식별 조건 — Confounder 통제의 식별성
9.5 카테고리 진입점
- Experimentation 학습 로드맵 — 11 Phase × 7 교재 매핑