dependency-visualizer

패키지 순환 참조는 왜 문제라는 걸까? 일단 기술을 써보면서 파악하기로 결정.

순환 참조의 개념을 처음 접하게 되다.

김우근 선생님의 ‘자바/스프링 개발자를 위한 실용주의 프로그래밍’이라는 책에서 순환 참조가 어떤 문제를 일으키는 지 난생 처음 접하게 되었다.

특히 이 책에서는 JPA 양방향 매핑이 어떤 문제를 일으키는지 강조하여 소개되었던 것으로 기억한다.

toString()을 양쪽 엔티티에서 호출할 때 일어날 수 있는 무한 루프 등의 예시가 있었다.

책이 워낙 친절해서 그런지 클래스 간 순환 참조가 어떤 문제를 일으키는지에 대해 직관적으로 이해할 수 있었다.

패키지 간 순환 참조에 대해서도 마지막에 간략하게 소개되는데, 이 부분은 추상적이어서 크게 와닿지는 않았다. 그 상태로 다음 챕터로 넘어가게 되었다.

패키지 간 순환 참조를 검출해주는 ArchUnit을 접하게 되다.

회사에서 동료들과의 논의 끝에 레이어드 아키텍처로 시작해서 오랜 기간 개발되어 비대해져 버린 프로젝트의 폴더 구조를 도메인 top-level 구조로 리팩토링하기로 했다.

리팩토링 과정에서 동료가 ArchUnit이라는 기술을 도입하면 어떨지 제안을 해주었다.

들어봤지만 자세히 알지 못했기에 docs를 찾아보았다.

아키텍처 전반에서 팀의 규칙을 정하고, 그. 규칙을 테스트코드로 강제할 수 있는 tool이었다.

docs에서 유즈케이스를 훑다가 cycle 탐지 테스트를 접하게 되었고, 우리의 리팩토링에 상당히 유용할 것이라는 직감이 들었다.

예를 들어 아래와 같이 패키지 간 순환이 생기면 테스트코드가 실패한다.

스크린샷 2026-08-02 오전 9.52.45

이 cycle 테스트는 클래스 간 순환 참조 뿐만 아니라 패키지간 순환 참조까지 검출하고 있었다.

유용할 것이라는 직감은 들었지만 패키지 간 순환이 왜 문제인지 정확하게 말로 설명하기가 어려웠다. (기술 도입은 설득이 선제되어야 하기 때문에)

AI에게 나의 상황을 설명했더니 로버트 마틴의 클린 아키텍처에서 ADP (Acyclic Dependencies Principle) 부분을 읽어보라는 조언을 얻게 되었다.

마침 집에 책이 있어서 찾아보았다. ‘숙취증후군‘이라는 용어를 접하게 되었고, 패키지 간 순환 참조가 얼마나 지독한 문제인지 강한 어조로 시사하고 있었다.

그런데 우리 상황에 대입해보니 패키지 순환 참조가 왜 프로젝트를 조용히 폐허로 만들게 되는 것인지 직관적으로 와닿지는 않았다.

우리 프로젝트는 모듈 별로 따로 배포하는 멀티 모듈 구조도 아니었고, 단위테스트에서는 그저 필요한 의존성을 mocking하면 된다는 생각이 들어 뭐가 문제인지 잘 모르겠는 상태였다.

그래서 일단 ArchUnit을 돌려보면서 무엇이 문제인지 알아보기로 결정했다.

일단 테스트를 돌려보자.

ArchUnit docs를 보고 아래와 같은 테스트코드를 만들어 일단 돌려보았다.

	@Test
	@DisplayName("패키지 간 순환참조가 없다")
	void packagesShouldBeFreeOfCycles() {
		slices()
			.matching("com.miraclecoat.homepage.(**)..")
			.should().beFreeOfCycles()
			.check(PRODUCTION_CLASSES);
	}

총 108개의 순환이 검출되었다.

108

수치는 생각보다 어마무시했지만, 이 수치가 그래서 우리 프로젝트에 어떤 악영향을 끼치고 있는지 파악이 어렵다면 결국에는 ArchUnit 자체가 섣부른 도입이 되어버린다.

테스트를 원복시키고 108개의 순환을 유지할지, 108개의 순환을 걷어내고 테스트를 통과시킬지 결단을 내리려면 저 순환들이 도대체 어떻게 나타나고 있는지 그림을 그려 파악해야 했다.

그런데 막상 파악해보려고 하니 쉽지 않았다.

‘Cycle detected’라는 문구로 시작하고 있는 에러메시지를 보고 도통 어디를 끊어야 하는것인지 파악하기가 어려웠다.

물론 저 에러메시지를 AI에게 던져주면 알아서 해결해줄 것이라는 기대감도 있었지만,

적어도 어디 패키지에서 순환이 일어나고 있는지 머릿속에 그릴 수 있어야만 AI가 나에게 던져줄 해결방향이 적합한지 판단할 수 있겠다는 생각이 들었다.

그리고 머릿속에 그리려면 시각화된 그래프가 하나 있으면 좋겠다는 생각이 들었다.

직접 시각화 tool을 만들어볼까?

AI가 지금의 프로젝트에 국한하여 당장 해결해야하는 순환 참조 고리들을 파악해줄 수도 있겠지만, 시각화해주는 tool 자체를 만드는 데에 AI를 사용한다면 다른 프로젝트에서도 같은 문제가 생겼을 때. 두고두고 재사용할 수 있겠다는 생각이 들었다.

그래서 머메이드로 패키지 순환을 시각화해서 보여주는 gradle plugin을 개발해보기로 결심했다.

ArchUnit은 도입의 초기 고정 비용이 거의 없었다. 테스트코드 한줄만 추가하면 되니까. (테스트를 유지하는데 드는 비용은 다를 수 있음. 불필요하게 테스트를 통과를 못하는 PR이 생기면 그게 다 비용이 되니까 그 검토가 필요한 상황인 것이고.)

하지만 gradle plugin을 개발하려면 고정 비용이 상대적으로 크다.

그럼에도 불구하고 팀 논의 편의, tool의 재사용성까지 고려하면 감수할만한 비용이라고 판단했다.

그 초기 비용까지 최소화하기 위해 AI의 도움을 적극 받아보기로 결정했다.

comments 2
안녕하세요스타트업에있는개발자입니다#37
"감수할만한 비용이라고 판단했다"가 제일 좋았습니다. 보통 좋더라에서 끝나는데 뭐를 지불하고 뭐를 받았는지가 적혀 있어서요. 다음 글도 기다리겠습니다!
블로그 주인입니다#38
의외의 부분에서 긍정적인 피드백을 주신 것 같아 감동입니다! 다음 글도 열심히 써보겠습니다. 방문 감사합니다 :)
댓글 작성
이름·비밀번호는 이 댓글에만 사용됩니다