cd ~/gh-pm-lab source pm-lab/env.sh
1이벤트 로그와 세 가지 분석
프로세스 마이닝의 입력은 사건 하나가 한 줄인 이벤트 로그다. 줄마다 적어도 세 값이 있어야 한다. 어느 일에 일어났는가(사례, case), 무엇이 일어났는가(활동, activity), 언제 일어났는가(시각, timestamp)다. 누가 했는가(수행자, resource)는 있으면 쓴다. 실습 20의 pm-lab/event-log.csv는 사례가 업무 키, 활동이 GitHub 기록에서 뽑은 12종의 이름이다.
2011년 IEEE 프로세스 마이닝 태스크포스(IEEE Task Force on Process Mining, 의장 빌 판 데르 알스트 Wil van der Aalst)가 낸 Process Mining Manifesto는 분석을 세 가지로 나눈다.
| 유형 | 하는 일 | 이 실습에서 |
|---|---|---|
| 발견 (discovery) | 사전 모델 없이 로그만으로 프로세스 모델을 만든다 | 직접 이어짐 관계, 변형 (2·3단계) |
| 적합성 검사 (conformance checking) | 정해 둔 모델과 로그를 비교해 어긋난 곳을 찾는다 | process-rules.tsv (4단계) |
| 개선 (enhancement) | 모델에 시간, 빈도 같은 성과 정보를 더해 병목을 찾는다 | 대기 시간 중앙값 (3단계) |
| 방법 | 발표 | 특징 |
|---|---|---|
| 직접 이어짐 그래프 (Directly-Follows Graph, DFG) | - | 한 사례 안에서 활동 A 바로 다음에 B가 나온 횟수와 시간을 화살표로 그린다. 대부분의 도구가 첫 화면으로 보여 준다 |
| 알파 알고리즘 (Alpha algorithm) | 2004, van der Aalst · Weijters · Măruşter | 직접 이어짐 관계에서 순서, 병행, 선택을 구분해 페트리넷(Petri net)을 만든다. 짧은 반복과 드문 사건에 약하다 |
| 휴리스틱 마이너 (Heuristics Miner) | 2006, Weijters · van der Aalst · Alves de Medeiros | 연결이 나온 빈도를 따져 드문 연결을 걸러 내고 주된 흐름을 남긴다 |
| 귀납적 마이너 (Inductive Miner) | 2013, Leemans · Fahland · van der Aalst | 로그를 나눠 가며 블록 구조 모델을 만든다. 막힘 없이 끝까지 실행되는(sound) 모델을 보장한다 |
이벤트 로그를 도구 사이에 주고받는 표준 형식은 XES(IEEE 1849-2016, 2023년 개정)다. CSV도 대부분의 도구가 읽는다. event-log.csv의 열 이름(case_id, activity, timestamp)을 도구의 가져오기 화면에서 지정하면 된다.
2프로세스 발견
bash github-pm-starter-kit/process-map.sh pm-lab/event-log.csv > pm-lab/process-map-report.md
보고서의 직접 이어짐 관계 표를 그림으로 바꾼 것이 아래 프로세스 지도다. 재생하면 업무 하나가 점 하나로 나타나 기록 시각을 따라 화살표 위를 움직인다. 화살표 숫자를 “대기 시간 중앙값”으로 바꾸면 2일 이상 기다린 연결이 주황색으로 표시된다.
변형(variant)은 같은 활동 순서로 흐른 사례의 묶음이다. 6건이 4개 변형으로 나뉘었다.
| 사례 수 | 활동 순서 | 사례 | | - | - | - | | 3 | 업무 생성 → 작업 시작 → 첫 커밋 → PR 생성 → 리뷰 승인 → PR 병합 → 업무 완료 | LP-01, LP-04, LP-06 | | 1 | 업무 생성 → 막힘 → 막힘 해소 → 작업 시작 → 첫 커밋 → 결정 기록 → PR 생성 → 변경 요청 → 리뷰 승인 → PR 병합 → 업무 완료 → 다시 열림 → 첫 커밋 → PR 생성 → 리뷰 승인 → PR 병합 → 업무 완료 | LP-03 | | 1 | 업무 생성 → 작업 시작 → 막힘 → 막힘 해소 → 업무 완료 | LP-05 | | 1 | 업무 생성 → 작업 시작 → 첫 커밋 → PR 생성 → 변경 요청 → 리뷰 승인 → PR 병합 → 업무 완료 | LP-02 |
- LP-01, LP-04, LP-06은 같은 7단계를 거쳤다. 팀이 기대한 기본 경로다.
- LP-02는 PR 리뷰에서 변경 요청이 한 번 있었다. 원인은 10월 26일의 정책 변경이다.
- LP-03은 사건 17건으로, 막힘, 결정 기록, 변경 요청, 다시 열림이 모두 한 업무에 있다.
- LP-05는 PR이 없다. QA 업무는 결과를 이슈의 테스트 항목표로 남기기 때문이다.
변형 수가 사례 수에 가까우면 업무마다 거친 순서가 제각각이다. 사례가 많아질수록 “사례 수가 가장 많은 변형이 전체의 몇 %인가”가 절차가 정착했는지 보는 숫자가 된다.
3기다린 구간 찾기
직접 이어짐 관계 표의 대기 시간은 앞 활동 기록부터 다음 활동 기록까지의 달력 시간이다. Launchpad에서 2번 이상 나온 연결만 보면 다음과 같다.
| 연결 | 횟수 | 중앙값 | 최대 | 읽는 법 |
|---|---|---|---|---|
| 업무 생성 → 작업 시작 | 5 | 20.3일 | 32.3일 | 계획상 기다린 백로그 기간이다. 병목이 아니다 |
| PR 생성 → 변경 요청 | 2 | 2.2일 | 3.8일 | 첫 리뷰까지의 대기 |
| 변경 요청 → 리뷰 승인 | 2 | 2.2일 | 3.1일 | 고치고 다시 리뷰받기까지 |
| 막힘 → 막힘 해소 | 2 | 1.4일 | 1.9일 | 선행 업무를 기다린 시간 |
| PR 생성 → 리뷰 승인 | 4 | 1.3일 | 3.7일 | 첫 리뷰까지의 대기 |
| 첫 커밋 → PR 생성 | 5 | 21.1시간 | 2.9일 | 개발 기간의 일부 |
| 작업 시작 → 첫 커밋 | 5 | 20.1시간 | 3.9일 | 개발 기간의 일부 |
| 리뷰 승인 → PR 병합 | 6 | 1.3시간 | 17시간 | 승인 뒤에는 거의 바로 병합한다 |
| PR 병합 → 업무 완료 | 6 | 1분 미만 | 1분 미만 | 닫는 키워드로 병합과 함께 닫힌다 |
PR을 만든 뒤 첫 리뷰까지의 시간이 가장 긴 두 건은 LP-02(PR #9, 목요일 15시 생성, 월요일 첫 리뷰)와 LP-04(PR #21, 금요일 17시 생성, 화요일 첫 리뷰)다. 둘 다 주말이 끼어 있다. 달력 시간으로 재면 주말이 대기로 잡히므로, 규칙은 다음 단계처럼 작업일로 정한다.
4적합성 검사
팀이 합의한 절차를 규칙 파일로 적는다. 키트의 process-rules.tsv는 선언적 규칙(declarative constraint) 네 종류를 쓴다. 절차 전체를 순서도로 그리는 대신 “반드시 지킬 것”만 적는 방식이다.
# type A B limit description precedence 리뷰 승인 PR 병합 - 병합 전에 승인 리뷰가 있다 precedence PR 병합 업무 완료 - 업무를 닫기 전에 병합된 PR이 있다 response 막힘 막힘 해소 - 막힘은 해소로 이어진다 absence 다시 열림 - - 완료한 업무를 다시 열지 않는다 within PR 생성 리뷰 승인|변경 요청 1 PR을 만든 다음 작업일까지 첫 리뷰가 있다
| 유형 | 뜻 |
|---|---|
absence | A가 한 번도 없어야 한다 |
response | A가 나오면 그 뒤 어딘가에 B가 나와야 한다 |
precedence | B가 나올 때마다 그 앞(직전 B 이후)에 A가 있어야 한다 |
within | A 다음의 첫 B가, A 다음 날부터 센 작업일 수 안에 나와야 한다 |
보고서의 적합성 검사 부분이다.
| 규칙 | 내용 | 위반 사례 | | - | - | - | | precedence 리뷰 승인 / PR 병합 | 병합 전에 승인 리뷰가 있다 | 없음 | | precedence PR 병합 / 업무 완료 | 업무를 닫기 전에 병합된 PR이 있다 | 1건: LP-05 | | response 막힘 / 막힘 해소 | 막힘은 해소로 이어진다 | 없음 | | absence 다시 열림 | 완료한 업무를 다시 열지 않는다 | 1건: LP-03 | | within PR 생성 / 리뷰 승인 또는 변경 요청 / 1작업일 | PR을 만든 다음 작업일까지 첫 리뷰가 있다 | 3건: LP-01, LP-02, LP-04 | 규칙을 모두 지킨 사례: 1/6건
위반은 곧 잘못이 아니다. 위반마다 규칙을 고칠지, 일하는 방식을 고칠지 정한다.
| 위반 | 기록이 말하는 것 | 정할 것 |
|---|---|---|
| LP-05: 병합된 PR 없이 닫힘 | QA 업무는 PR을 만들지 않는다 | 규칙을 고친다. QA 업무는 “테스트 항목표 링크가 있는 댓글”을 완료 근거로 삼는다 |
| LP-03: 다시 열림 | 10-26 결정 기록(정책 변경) 뒤 PR #20의 리뷰에서 재신청 테스트가 요청되었지만, 재신청 때 동의 시각이 갱신되지 않는 결함은 QA(11-05)에서 처음 발견되었다 | 일하는 방식을 고친다. 정책이 바뀐 업무는 완료 조건에 바뀐 항목의 테스트를 넣는다 |
| LP-01, LP-02, LP-04: 다음 작업일까지 첫 리뷰 없음 | 세 건 모두 첫 리뷰까지 2작업일이 걸렸다 | 팀이 정한다. 리뷰 당번을 두거나, 규칙을 2작업일로 바꾼다 |
규칙을 모두 지킨 사례 비율(1/6)은 프로세스 마이닝의 적합도(fitness)에 해당하는 값이다. 학술 도구는 페트리넷 모델에 로그를 토큰으로 흘려 보는 토큰 재생(token replay)이나, 모델과 로그를 한 걸음씩 맞춰 보는 정렬(alignment)로 적합도를 계산한다. 이 키트는 규칙 단위로 세므로 어느 규칙이 몇 건을 어겼는지 바로 읽을 수 있다.
5사례가 많을 때
사례 6건으로는 변형도 병목도 우연에 가깝다. 키트에는 사례가 많은 연습용 로그로 가상 팀 Orbit의 6개월 기록이 들어 있다. 실제 팀의 기록이 아니라 정해진 규칙으로 만든 합성 데이터이며, samples/orbit-event-log-clean.csv는 품질 문제를 정리한 판이다(업무 257건). 같은 분석을 하면 다음과 같다. 위 프로세스 지도의 “데이터”를 “Orbit 업무 257건”으로 바꾸면 같은 데이터를 재생할 수 있다.
프로세스 지도에서 “최소 횟수”를 올리면 드문 연결이 사라지고 주된 흐름만 남는다. 휴리스틱 마이너가 빈도로 연결을 거르는 방식을 손으로 해 보는 것이다. 257건 중 125건(49%)이 기본 경로 하나로 흘렀다. 적합성 검사에서 “병합된 PR” 규칙을 어긴 23건은 모두 PR을 만들지 않는 QA 업무다. Launchpad의 LP-05와 같은 경우가 사례가 많아지면 규칙 위반의 큰 덩어리가 된다.
6위키에 프로세스 지도 올리기
--mermaid는 보고서 대신 Mermaid 흐름도를 출력한다. GitHub는 Mermaid 코드 블록을 위키, 이슈, PR, Markdown 파일에서 그림으로 그린다.
bash github-pm-starter-kit/process-map.sh pm-lab/event-log.csv --mermaid count > pm-lab/process-map.md bash github-pm-starter-kit/wiki-sync.sh "$PM_REPO" --process pm-lab/process-map.md --push
```mermaid flowchart LR S((시작)) E((끝)) a0["업무 생성 · 6"] a1["작업 시작 · 6"] a2["첫 커밋 · 6"] a3["PR 생성 · 6"] a4["막힘 · 2"] a5["리뷰 승인 · 6"] a6["PR 병합 · 6"] a7["업무 완료 · 7"] (중간 생략) a2 -->|1| a10 a7 -->|6| E ```
--mermaid time은 화살표에 대기 시간 중앙값을 적는다. --min-count 2를 붙이면 한 번만 나온 연결을 뺀다.
7Python 도구로 넘기기 (선택)
더 많은 알고리즘이 필요하면 같은 CSV를 프로세스 마이닝 라이브러리 PM4Py(Fraunhofer FIT에서 개발을 시작한 오픈소스, AGPL-3.0 라이선스)로 읽는다. 아래는 PM4Py 2.7.23으로 예시 로그를 읽어 직접 이어짐 관계, 귀납적 마이너 모델, 토큰 재생 적합도와 정밀도를 계산한 것이다.
python3 -m pip install pm4py
import pandas as pd
import pm4py
df = pd.read_csv("pm-lab/event-log.csv", keep_default_na=False)
df["timestamp"] = pd.to_datetime(df["timestamp"])
log = pm4py.format_dataframe(df, case_id="case_id", activity_key="activity", timestamp_key="timestamp")
dfg, start, end = pm4py.discover_dfg(log)
for (a, b), n in sorted(dfg.items(), key=lambda x: -x[1])[:5]:
print(n, a, "->", b)
net, im, fm = pm4py.discover_petri_net_inductive(log)
fit = pm4py.fitness_token_based_replay(log, net, im, fm)
prec = pm4py.precision_token_based_replay(log, net, im, fm)
print("fitness", round(fit["log_fitness"], 2), "precision", round(prec, 2))
print("variants", len(pm4py.get_variants(log)))
6 PR 병합 -> 업무 완료 6 리뷰 승인 -> PR 병합 5 업무 생성 -> 작업 시작 5 작업 시작 -> 첫 커밋 5 첫 커밋 -> PR 생성 fitness 1.0 precision 0.59 variants 4
PM4Py의 직접 이어짐 관계 횟수는 process-map.sh의 표와 같다. 귀납적 마이너 모델은 기록 6건을 모두 재현하지만(적합도 1.0), 기록에 없는 순서도 허용하므로 정밀도(precision)가 0.59다. 적합도와 정밀도는 함께 본다. 모든 순서를 허용하는 모델은 적합도가 1.0이어도 절차로 쓸 수 없다.
8한계
- 기록되지 않은 활동은 지도에 없다. 회의, 메신저, 구두로 정한 일은 결정 기록 댓글로 남겨야 로그에 들어온다.
- 작업 시작은 하루 한 번 찍은 스냅숏에서 온 근사값이다. 시간 단위의 정밀도가 필요하면 스냅숏을 더 자주 찍는다.
- 업무 하나에 PR이 둘 이상이거나 PR 하나가 업무 여러 개를 닫으면, 업무를 사례로 삼는 로그는 그 관계를 한 줄로 펼쳐 버린다. 업무, PR, 리뷰를 각각의 객체로 두는 객체 중심 이벤트 로그(object-centric event log, OCEL)가 이 문제를 다룬다.
이벤트 로그의 resource 열에는 사람의 아이디가 있다. 사람별 리뷰 시간이나 위반 건수를 비교하기 시작하면 기록하는 방식 자체가 바뀌어 로그가 쓸모를 잃는다. 분석 단위는 절차와 연결이다. 로그를 팀 밖에 줄 때는 cut -d, -f1-3,5 pm-lab/event-log.csv로 resource 열을 빼고 준다.
- Launchpad 로그의 변형 4개를 각각 설명할 수 있다.
- 대기 시간이 긴 연결 중 병목이 아닌 것(계획상 대기)을 구분했다.
- 적합성 검사 위반마다 규칙을 고칠지 일하는 방식을 고칠지 정했다.
- 프로세스 지도를 위키에 올렸다.
마치며...
4회차에서는 이슈, PR, 토론, 브랜치, 위키, 스냅숏을 이벤트 로그 하나로 엮어 업무가 실제로 흐른 길을 그렸다. 발견은 “어떻게 흘렀는가”를, 적합성 검사는 “합의한 대로 흘렀는가”를, 대기 시간은 “어디서 멈췄는가”를 답한다. 이 분석의 재료는 1회차부터 지켜 온 기록 규칙(업무 키, 완료 조건, 닫는 키워드, 결정 기록 양식)이고, 규칙이 지켜진 만큼만 로그가 정확해진다. 저장소와 팀이 늘어날 때의 운영 방법은 심화 페이지 “확장성”에 있다. 다음 회차부터 시작하는 2단계에서는 업무 수백 건의 이벤트 로그로 개발 프로세스의 병목을 찾고 개선안을 낸다.