Skip to content
Go back

Dependency-cruiser로 GitLab MR 변경 영향 분석 봇 만들기

리뷰어가 MR 하나를 받으면 가장 먼저 묻는 질문은 늘 같다 — “이 변경이 어디까지 번지나?” 모노레포에 앱이 둘, 패키지가 일곱이고 FSD(Feature-Sliced Design) 레이어까지 끼면 그 답을 머리로 그리기 어렵다. 결국 매번 grep 으로 import 사용처를 훑으면서 시간이 깎였다.

이번에 그 작업을 자동화하는 봇을 붙였다. MR이 열리면 dependency-cruiser 가 변경 파일의 역방향 의존 트리를 walk 해서 영향받는 모듈을 마크다운 코멘트로 등록한다. 푸시할 때마다 갱신되고, 노이즈를 줄이는 디테일 몇 개를 잡고 나니 의외로 쓸 만한 수준이 나왔다.

무엇을 만들었나

/create-mr로 MR을 만들면 영향 분석 코멘트가 같이 달리고, 푸시 후 /impact만 다시 치면 같은 자리에 갱신된다.

디테일 1 — Barrel(index.ts)을 어떻게 다룰지

가장 처음 막힌 부분이 barrel 처리였다. FSD 슬라이스마다 index.ts로 re-export 가 깔려 있는데, 영향 분석 결과에 entities/foo/index.ts만 잔뜩 찍히면 의미가 없다. 어떤 UI가 영향받는가를 봐야 한다.

처음엔 dep-cruise exclude 로 index.ts 자체를 빼봤는데, 그러면 import { x } from '@entities/foo' 체인이 그래프에서 끊겨서 UI 파일이 영향 트리에 아예 안 잡혔다. 결론은 그래프엔 남기고, 출력에서만 빼고, 그래프 walk 시엔 통과시키기.

// lib/analyze-impact.mjs (요약)
function resolveBarrelDependents(graph, source, visited = new Set()) {
  // index.ts 의 dependent 가 또 index.ts 면 재귀로 펼친다
  for (const dep of mod.dependents) {
    if (isIndexFile(dep)) {
      result.push(...resolveBarrelDependents(graph, dep, visited));
    } else {
      result.push(dep);
    }
  }
}

디테일 2 — “터미널” 노드만 보여주기

레이어 dep graph 는 깊이 3만 walk 해도 수십 개가 잡힌다. feature/use-foo.ts를 바꾸면 그걸 쓰는 container, container 를 쓰는 page 까지 줄줄이 따라온다. 셋 다 보여주면 봐야 할 것이 뭔지 흐려진다.

진짜 봐야 할 건 최종 도달점 — 다른 영향 모듈을 dependent 로 가지지 않는 노드. 그래서 BFS 후 한 번 더 필터를 걸었다.

// "다른 영향 모듈에 의존되는" 중간 노드는 제외, 끝점만 남김
const isTerminal = (entry) => {
  const realDependents = resolveBarrelDependents(graph, appLocalPath);
  return !realDependents.some((dep) =>
    dedupedKeys.has(`${entry.app}:apps/${entry.app}/${dep}`),
  );
};

이 한 단계로 영향 모듈 수가 1/3 수준으로 줄었다. 노이즈 대신 페이지·widget 만 깔끔히 남는다.

디테일 3 — 트리거 중심 그룹핑

영향 모듈을 평면 리스트로 나열하면 어떤 변경 때문에 어디가 영향받는지 매핑이 안 보인다. 그래서 출력 구조를 뒤집었다. 트리거(원인 변경 파일) → 영향 모듈[] 역인덱스로 만든 뒤, 영향 많은 트리거 순으로 섹션을 쌓는다.

### 🔸 `shared/config/menu.ts`
**14 모듈 영향** — admin app 13, widget 1
- 📄 `admin` · `app/(protected)/layout.tsx`
- 📄 `admin` · `app/(protected)/home/page.tsx`
- ...

실제로 MR 에 붙은 코멘트는 이렇게 보인다.

MR 영향 분석 코멘트 예시

리뷰어가 “왜 이게 영향받지?”를 묻는 대신 “이 변경이 이만큼 번진다”가 한눈에 들어온다. TL;DR 의 주요 변경 영역 + 영향 범위 두 줄, 그 아래 트리거별 영향 모듈 — 푸시 후 코멘트 하나만 보면 리뷰 출발점이 잡힌다.

디테일 4 — 코멘트 upsert (HTML 마커)

푸시할 때마다 새 코멘트가 쌓이면 MR 노트가 금방 오염된다. GitLab Notes API 는 update 가 되니까, 본문 첫 줄에 <!-- impact-analysis-bot:v1 --> 마커를 박고 그걸로 기존 코멘트를 찾아 PUT 한다. 마커가 없으면 POST.

같은 봇이 여러 번 돌아도 코멘트는 늘 한 개다. 버전 번호는 마커에 박아 둬서, 포맷이 크게 바뀌면 v2 로 올려 새 코멘트로 갈아끼울 여지를 남겼다.

회고

의존성 그래프는 만들기보다 쓸모 있게 보여주는 것이 훨씬 어렵다.

dep-cruise 자체는 한 시간이면 붙는다. 그런데 결과를 그대로 토해 내면 누구도 안 본다. barrel 통과, 터미널 필터, 트리거 그룹핑 — 이 세 단계가 “그래프 데이터”를 “리뷰 보조 도구”로 바꿨다. 첫 코멘트가 MR 에 붙고 동료가 “오 이거 좋은데” 라고 한 줄 달았을 때, 그 차이가 결국 디테일에서 나온다는 걸 다시 확인했다.

다음으로 할 만한 건 — (1) 영향 페이지에 자동으로 스모크 E2E 태깅, (2) packages 변경 시 회귀 테스트 추천을 코멘트에 추가. 일단 v1 은 여기까지.


Share this post on:

Comments


Next Post
퀀트 플랫폼 개발기 #5 — Hysteresis 청산 룰 자동 도출과 자본 균등 분배