규모별 운영
| 규모 | 업무와 Project | 기록 수집 | 분석 |
|---|---|---|---|
| 팀 1개, 저장소 1개 (이 과정) | 저장소 이슈, 사용자 소유 Project | PM이 스크립트를 실행한다. 스냅숏만 Actions가 찍는다 | 키트 스크립트 |
| 팀 여러 개, 저장소 여러 개 | 저장소마다 이슈, 조직 소유 Project 하나에 여러 저장소의 항목 | 저장소별로 event-log.sh를 돌려 합친다. 업무 키 접두어를 팀마다 다르게 둔다 | 키트 스크립트, PM4Py |
| 조직 전체 | 팀별 Project, 조직 공통 필드와 이슈 유형 | GitHub App이나 웹훅으로 사건을 받아 저장한다 | 프로세스 마이닝 도구, 데이터 저장소 |
규모가 바뀌어도 바뀌지 않는 것은 기록 규칙이다. 업무 키가 있는 제목, 예·아니오로 확인하는 완료 조건, 닫는 키워드가 있는 PR, 양식을 지킨 결정 기록이 있어야 어느 규모에서든 이벤트 로그가 만들어진다.
API 호출 한도
| 인증 방식 | REST API | GraphQL API |
|---|---|---|
개인 토큰, gh auth login | 시간당 5,000회 | 시간당 5,000점 |
Actions의 GITHUB_TOKEN | 저장소당 시간당 1,000회 | 저장소당 시간당 1,000점 |
| GitHub App 설치 토큰 | 시간당 5,000회부터, 저장소·사용자 수에 따라 12,500회까지 | - |
이와 별도로 동시 요청 100개, REST 분당 900점, GraphQL 분당 2,000점의 보조 한도가 있다. 남은 한도는 아래 명령으로 확인한다.
gh api rate_limit --jq '.resources.core, .resources.graphql | "\(.remaining)/\(.limit), 초기화 \(.reset | todate)"'
| 스크립트 | 호출 수 | 업무 500건일 때 |
|---|---|---|
event-log.sh | 이슈 목록 1 + 업무마다 타임라인 1(사건 100개마다 1) + PR 목록 | REST 약 500회 |
branch-report.sh | 브랜치 100개마다 GraphQL 1 | 브랜치 수에 따른다 |
discussion-digest.sh | 댓글 목록(100개마다 1) + 토론 목록 1 + 기간 안 토론마다 1 + PR 목록 1 | 기간에 따른다 |
wiki-sync.sh | 마일스톤 1 + 댓글 목록(100개마다 1) + 토론 1 | 댓글 수에 따른다 |
event-log.sh를 날마다 전체로 돌리면 업무 수만큼 호출이 늘어난다. 업무가 수백 건을 넘으면 --since로 최근에 갱신된 업무만 다시 읽고, 마일스톤이나 달 단위로 파일을 나눠 pm-data 브랜치에 저장한다. 나눈 파일은 첫 줄(머리글)만 한 번 남기고 합친다.
{ head -1 pm-lab/logs/2026-10.csv; for f in pm-lab/logs/*.csv; do tail -n +2 "$f"; done; } > pm-lab/event-log-all.csv
기록 보관
- 저장소 이벤트 API는 최근 90일, 최대 300건만 돌려준다. 오래 남겨야 하는 기록은 이슈 타임라인처럼 사라지지 않는 곳에서 읽거나, 날마다 수집해 저장한다.
- Project 스냅숏은 항목 수에 비례해 커진다. 항목 하나를 1KB로 잡으면 항목 1,000개 × 평일 250일은 한 해 약 250MB다. Git은 비슷한 파일을 차이(delta)로 압축해 저장하지만, GitHub는 저장소를 1GB 아래, 적어도 5GB 아래로 유지하라고 권한다. 키트의 워크플로는 이슈 본문을 빼고 저장한다. 더 줄이려면 키와 Status만 남긴다.
- 마일스톤 마감일과 Project 필드 값은 현재 값만 남는다. 바꾼 이력은 결정 기록과 스냅숏이 대신한다.
jq -c '[.items[] | {n: .content.number, s: .status}]' snapshots/2026-11-11.json
여러 저장소와 사례 ID
저장소가 둘 이상이면 이슈 번호 #3이 저장소마다 있다. event-log.sh는 제목 앞의 업무 키([LP-03])를 사례 ID로 쓰고, 키가 없을 때만 #번호를 쓴다. 팀마다 다른 접두어(LP-, PAY-, OPS-)를 쓰면 로그를 합쳐도 사례가 섞이지 않는다. 한 업무가 여러 저장소의 PR로 끝나는 경우에는 업무 하나를 사례로 삼는 로그가 그 관계를 다 담지 못한다. 업무, PR, 리뷰를 각각 객체로 두는 객체 중심 이벤트 로그(OCEL)가 이런 경우를 다룬다.
자동화
| 항목 | 제약 | 조치 |
|---|---|---|
| 예약 실행 시각 | 정각 무렵에는 밀리거나 빠질 수 있다 | 정각을 피하고, gh run list로 빠진 날을 확인한다 |
| 예약 실행 정지 | 공개 저장소는 60일 동안 활동이 없으면 예약 실행이 꺼진다 | gh workflow enable으로 다시 켠다 |
| Project 읽기 권한 | GITHUB_TOKEN은 사용자나 조직 소유 Project를 읽지 못한다 | 개인 토큰이나 GitHub App 토큰을 비밀값으로 넣는다. 토큰에 만료일을 둔다 |
| Status 변경을 실시간으로 받기 | 스냅숏은 하루 단위다 | 조직 소유 Project는 projects_v2_item 웹훅으로 항목 변경을 받을 수 있다. 받는 서버가 필요하다 |
분석 도구로 넘기기
event-log.csv는 대부분의 프로세스 마이닝 도구가 읽는 형식이다. 표준 형식 XES가 필요하면 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")
pm4py.write_xes(log, "pm-lab/event-log.xes")
로그를 팀 밖으로 보낼 때는 사람 아이디가 든 resource 열을 뺀다.
cut -d, -f1-3,5 pm-lab/event-log.csv > pm-lab/event-log-anon.csv
마치며...
규모가 커지면 바뀌는 것은 호출 횟수, 저장 위치, 사례 ID를 정하는 방법이고, 분석의 순서(기록 → 이벤트 로그 → 발견 · 적합성 검사 · 대기 시간)는 그대로다. 어느 규모에서든 먼저 정할 것은 수집 주기와 보관 위치, 그리고 사람 단위 분석을 하지 않는다는 팀의 합의다.