본문 바로가기
스프링/만들면서 배우는 실무 백엔드 개발

10. ArchUnit 으로 아키텍쳐 검사하기

by kdohyeon (김대니) 2023. 4. 5.
반응형

서비스 또는 시스템은 목적에 따라 아키텍쳐를 설계하고 이에 기반하여 구현이 됩니다. 여기서 이야기하는 아키텍쳐란 기능, 조직, 구현에 대한 법칙과 방법을 의미합니다. 깨진 유리창 이론도 있듯이 초기에 아키텍쳐가 잘 설계되어 있다 하더라도 시간이 지남에 따라 점점 망가질 수 있습니다. 이를 최대한 방지하기 위해 아키텍쳐적인 부분에 대해서도 검사를 할 필요성이 있으며, 그 방법 중에 하나가 ArchUnit 이라는 라이브러리를 활용하는 것입니다. 이번 글에서는 ArchUnit 을 활용하여 아키텍쳐 검사를 해보도록 하겠습니다.


Pull request: https://github.com/kdohyeon/crypto-labs/pull/18

의존성 추가

라이브러리 버전은 현재 기준 최신 버전인 1.0.1 을 사용했다.

dependencies {
    testImplementation 'com.tngtech.archunit:archunit:1.0.1'
}

아키텍쳐 소개

app-api 기준으로 살펴보면 입력 (input) port 에만 의존해야하고 나머지 모듈에는 의존을 하면 안된다. 이에 대한 아키텍쳐 테스트를 작성해보도록 하자.

아키텍쳐

테스트 생성

아키텍쳐 검사도 테스트의 일부분이기 때문에 test 디렉토리에 생성했고, app-api 에 대한 테스트이기 때문에 crypto-apps/app-api 모듈 내 위치시켰다.

ArchitectureTest 테스트 생성

ArchUnit 으로 다양한 테스트를 해볼 수 있지만 여기서는 패키지간 의존성 테스트만 해보려고 한다. 특정 클래스가 잘못된 패키지에 의존하고 있는 경우를 찾아낼 수 있다. 이를 통해 무분별한 import 를 방지할 수 있다.

먼저 아키텍쳐 테스트를 작성하려면 규칙이 있어야 한다. 예를 들어, app-api 패키지는 application-service 패키지에 의존할 수 없다 라는 규칙을 정했다면 다음과 같이 설정할 수 있다.

@ArchTest
val controller_should_not_depend_on_service_directly =
    noClasses().that()
        .resideInAPackage("..controller..").should().onlyDependOnClassesThat()
        .resideInAnyPackage("..service..")
        .`as`("controller 는 service 에 의존할 수 없습니다. 입력 port 를 사용해주세요.")

또한 순환 의존을 방지하기 위해 아래와 같이 테스트를 작성할 수도 있다.

@ArchTest
val cycle_check =
    SlicesRuleDefinition.slices().matching("kdohyeon.crypto.labs.(*)..")
        .should().beFreeOfCycles()

참고

반응형

댓글