일기 slecs

HSQLDB 종속성 제거로 의존성 관리 개선

목차

HSQLDB가 빌드 파일에 남아 있다는 걸 인지한 건 의존성 트리를 한번 훑어보다가였다. 실제로 코드에서 직접 참조하는 곳이 있나 찾아봤는데 없었다. 이미 오래전에 다른 걸로 갈아탔거나, 아니면 처음부터 명시적으로 추가한 게 아니라 어느 라이브러리의 전이 의존성으로 딸려 들어온 뒤 그냥 남아 있던 케이스. 어느 쪽이든 지금 기준으로는 필요 없는 상태였다.

HSQLDB를 빼야 하는 이유

임베디드 Java DB 시장에서 HSQLDB는 오랫동안 H2와 함께 양대 선택지였다. 테스트 환경에서 실 DB 대신 인메모리 DB를 쓰는 패턴이 굳어지던 시기에 많이 퍼졌는데, 지금 기준으로는 H2가 사실상 표준에 가깝다. Spring Boot도 spring-boot-starter-test에 H2를 먼저 권장하는 구조고, JPA 관련 자동 설정 상당수도 H2 기준으로 동작한다. HSQLDB를 굳이 쓸 이유가 없는 상황에서 그게 classpath에 올라가 있으면 몇 가지 문제가 따라온다.

  • 빌드 시간 소폭 증가 - 쓰지 않는 jar가 다운로드·캐시 대상에 포함됨
  • 버전 충돌 리스크 - 다른 의존성이 HSQLDB를 전이로 당기는 버전이 다를 때 conflict 발생 가능
  • 보안 취약점 노출면 확대 - 사용하지 않는 라이브러리라도 취약점 스캔에 걸리면 대응 비용이 생김
  • 가독성 저하 - build.gradle이나 pom.xml 읽는 사람이 "이거 왜 있지?" 하고 멈추게 됨

특히 세 번째가 생각보다 실질적인 비용이다. CVE 하나 나왔을 때 "우린 실제로 안 쓰는데요"가 통하는 맥락도 있지만, 자동화된 보안 게이트가 버전 기준으로 막아버리는 환경이면 쓰든 안 쓰든 대응해야 한다. 안 쓰는 라이브러리 하나 때문에 릴리즈 파이프라인이 막히는 건 꽤 허무한 상황이다.

제거 작업

수정 파일은 1개였다. 의존성 선언 파일에서 HSQLDB 항목을 지우고, 테스트가 H2로 정상 동작하는지 확인하는 게 전부. 실제로 HSQLDB를 참조하는 코드가 없었으니 런타임 동작 변경은 없었고, 테스트 통과 여부만 체크하면 됐다.

// 제거 전
dependencies {
    testRuntimeOnly 'org.hsqldb:hsqldb:2.7.2'
    testRuntimeOnly 'com.h2database:h2'
}

// 제거 후 - H2만 남김
dependencies {
    testRuntimeOnly 'com.h2database:h2'
}

명시적으로 선언돼 있던 케이스라 이렇게 한 줄 지우는 게 전부였다. 만약 전이 의존성으로 여러 군데서 당겨지고 있었다면 exclude로 강제로 빼야 하는데, 그 경우엔 어디서 오는지 역추적하는 과정이 추가된다.

# 전이 의존성으로 딸려 들어오는 경우 의존성 트리 확인
./gradlew dependencies --configuration testRuntimeClasspath | grep hsqldb

이렇게 근원 의존성을 파악한 뒤, 해당 라이브러리 버전을 올리면 자연스럽게 빠지는 경우도 있고, 계속 당기면 configurations.all { exclude ... } 로 강제 제외해야 하는 경우도 있다. 근원 의존성 버전 업그레이드로 해결되면 그쪽이 더 깔끔하다.

의존성 관리를 어떻게 가져갈 건가

이번 작업은 단순한 한 줄 삭제였지만, 그 계기로 의존성 관리 전반을 다시 생각하게 됐다. 잘 정비된 의존성 목록이 주는 이점은 꽤 실질적이다.

상황 미정비 상태 정비된 상태
보안 패치 대응 쓰는 건지 몰라서 일단 올려야 함 영향 범위 즉시 파악 가능
신규 팀원 온보딩 "이 의존성 왜 있어요?" 반복 질문 의도가 명확해서 설명 비용 낮음
버전 업그레이드 불필요한 것까지 전부 추적 실제 쓰는 것만 관리
빌드 속도 저하 추적 원인 파악이 어려움 최적화 여지 파악 용이

자동화 측면에서 Dependabot이나 Renovate 같은 툴을 붙여두면 버전 업데이트 PR을 자동으로 올려준다. 이게 설정해두면 잊어버릴 수 있는 게 아니라, 쏟아지는 PR을 얼마나 효율적으로 처리할 것인가가 오히려 과제가 된다. 전략 없이 켜두면 PR 폭탄이 되니 그룹핑이나 auto-merge 조건을 잘 설계해야 한다. patch 업데이트는 테스트 통과 시 자동 머지, minor는 리뷰 후 머지, major는 별도 태스크로 분리하는 식으로 계층을 두는 게 현실적이다.

의존성 업데이트를 오래 미뤄두면 나중에 한꺼번에 올려야 하는데, 그때는 breaking change가 복수로 겹쳐서 원인 파악이 어려워진다. 어떤 라이브러리 때문에 테스트가 터진 건지 bisect 하는 시간이 오히려 더 걸리는 상황. 이번 작업을 계기로 정기 점검 루틴을 만들기로 했다. 스프린트마다 의존성 트리 훑기, 분기마다 ./gradlew dependencyUpdates로 버전 gap 확인, 연 1회 이상 실제로 쓰는지 여부까지 재검토. 완벽하게 할 순 없지만 루틴이 있는 것과 없는 것의 차이는 1~2년 뒤에 드러난다.

이번 변경은 파일 하나에 한 줄 삭제였다. 실제 작업 시간은 10분도 안 걸렸는데, 그게 "별거 아니다"가 아니라 잘 관리된 환경이 이 정도 변경을 10분짜리로 유지해줬다는 의미로 읽힌다. 엉켜 있는 상태였으면 영향 범위 파악에 오히려 더 걸렸을 거다. 인프라나 환경 정비에 투자하는 시간은 복리처럼 돌아온다는 말이 진짜인 게, 쌓일수록 단순한 작업이 단순하게 유지된다.

댓글 0

첫 댓글 달아줘.