$백엔드 개발자 Rueun의 기술 블로그|Java · Spring · 클린 아키텍처🌱
#Development

Gradle 전환과 미사용 코드 정리부터 시작한 이유

Gradle 전환과 미사용 코드 정리부터 시작한 이유

리팩토링을 시작할 때 가장 하고 싶었던 일은 분명했다. controller에 몰린 로직을 domain과 infra로 나누고, MongoTemplate 의존을 줄이고, /api/v1 구조를 준비하고, 점점 API 중심의 백엔드로 옮겨가고 싶었다.

그런데 실제로 손을 대기 시작하니, 가장 먼저 해야 할 일은 그런 구조적 개선이 아니었다. 그 전에 먼저 해결해야 할 두 가지가 있었다.

하나는 실행 기준을 다시 잡는 일, 다른 하나는 죽은 코드를 먼저 걷어내는 일이었다.

이번 글은 왜 리팩토링 초반에 Gradle 전환과 미사용 코드 정리를 먼저 했는지, 그리고 그 순서가 왜 생각보다 중요했는지를 정리한 글이다.

리팩토링은 "좋은 구조 만들기" 이전에 "실행 가능한 상태 유지하기"였다

운영 중인 서비스에서 리팩토링을 시작할 때, 가장 위험한 착각은 "이제부터 구조를 잘 만들면 된다"는 생각이다. 실제로는 그 전에 더 중요한 조건이 있다.

리팩토링을 하더라도 서비스는 계속 실행 가능해야 한다.

이건 당연한 말처럼 들리지만, 실제 작업에서는 자주 잊힌다. 파일을 옮기고, 빌드 구조를 바꾸고, 패키지를 나누는 과정에서 한 번 실행 기준을 잃어버리면, 그 뒤부터는 구조 개선과 복구 작업이 섞이기 시작한다. 그러면 코드가 깨진 이유가 "설계가 틀려서"인지, "단순히 이동 과정에서 빠뜨려서"인지 구분하기 어려워진다.

그래서 이번 작업에서는 처음부터 원칙을 하나 정했다.

"예쁘게 나누기보다 먼저, 지금 앱이 계속 켜지는 상태를 유지하자."

이 원칙 때문에 백엔드 리팩토링도 곧바로 도메인 분해로 들어가지 않고, 먼저 빌드와 실행 기준을 정리하는 쪽으로 방향이 잡혔다.

왜 Maven에서 Gradle로 먼저 옮겼나

백엔드 구조를 멀티모듈로 가져가기로 했으면, 결국 빌드 기준도 먼저 정리해야 했다. 처음에는 "일단 코드부터 정리하고 나중에 빌드 도구를 바꾸자"는 선택도 가능해 보였다. 하지만 그렇게 하면 리팩토링 중간에 다시 빌드 구조를 바꿔야 하고, 그 순간 변경 축이 두 개가 된다.

예를 들어 이런 식이다.

  • 기존 Maven 구조 위에서 코드 분리
  • 어느 정도 정리된 뒤 Gradle로 이동
  • 이후 다시 멀티모듈 의존 관계 정리

이렇게 가면 같은 코드를 여러 번 다시 만지게 된다. 구조를 나누는 문제와 빌드 구조를 바꾸는 문제가 겹치면서 오히려 일이 더 복잡해진다.

그래서 순서를 반대로 잡았다.

  1. 먼저 Gradle 기준을 만든다
  2. 멀티모듈 뼈대를 만든다
  3. 기존 앱은 일단 실행 가능한 모듈 안에 그대로 둔다
  4. 그다음 실제 코드를 하나씩 옮긴다

이 방식이 좋은 점은 명확했다. 앞으로 domain, infra, support, application 모듈을 어떻게 나누든, 그 작업은 이미 Gradle 구조 안에서 일어난다. 즉, 나중에 다시 빌드 구조를 바꾸느라 한 번 더 흔들릴 필요가 없다.

실제로는 "전환"보다 "수용"에 가까웠다

Gradle로 옮긴다고 해서 처음부터 코드를 다 나눠 넣은 것은 아니다. 오히려 초반 작업은 "새 구조로 이전"이라기보다 "새 구조 안에 기존 앱을 일단 수용"하는 느낌에 가까웠다.

기존 controller, service, resource를 일단 terms-backend라는 실행 모듈 안에 그대로 담고, 백엔드 루트를 /backend로 정리하고, 그 위에 core, support, application 같은 멀티모듈 뼈대를 만드는 식이었다.

이 방식의 장점은 분명했다.

  • 앱은 계속 켜진다
  • 기존 화면도 그대로 확인할 수 있다
  • Playwright 테스트도 바로 돌릴 수 있다
  • 코드 분해는 이후 단계로 미룰 수 있다

즉, 리팩토링 초반의 목표는 "좋은 구조 만들기"가 아니라 **"나중에 좋은 구조로 옮길 수 있는 안정된 기반 만들기"**였다.

그런데 빌드 구조보다 더 급했던 문제가 있었다

실행 기준을 어느 정도 정리하고 나니, 그다음으로 바로 마주친 문제는 미사용 코드였다.

이 프로젝트는 예전에 fork 떠온 코드 기반이 섞여 있었고, 오랜 시간 동안 여러 기능이 붙고 빠지는 과정을 거치면서 실제로는 더 이상 쓰지 않는 코드도 많이 남아 있었다. 문제는 이런 dead code가 "별도 보관 폴더"에 정리돼 있는 게 아니라, 현재 서비스 코드와 같은 경로 안에 그대로 남아 있다는 점이었다.

이 상태에서 리팩토링을 진행하면 두 가지가 동시에 힘들어진다.

첫째, 리팩토링 범위가 불필요하게 커진다.
둘째, 무엇을 남기고 무엇을 없애야 하는지 계속 헷갈린다.

예를 들어 controller 하나를 보더라도, 실제로 쓰지 않는 endpoint가 섞여 있으면 "이것도 구조를 나눠야 하나?"를 계속 고민하게 된다. 결국 해야 할 일은 도메인 분리인데, dead code까지 함께 옮기게 되면 리팩토링이 아니라 쓰레기 재배치가 된다.

그래서 나는 미사용 코드 정리를 "나중에 하면 좋은 일"이 아니라, 리팩토링 초반에 반드시 해야 하는 일로 보기 시작했다.

미사용 코드는 어떻게 판단했나

여기서 중요한 건 "안 쓰는 것 같아 보인다"가 아니라, 실제 서비스 기준으로 살아 있는 흐름인지를 확인하는 일이었다.

나는 대략 이런 기준으로 봤다.

  • 실제 템플릿에서 링크되는가
  • 실제 JS에서 호출되는가
  • 현재 화면에서 접근 가능한가
  • 관리자 대시보드나 공개 GNB에서 들어갈 수 있는가
  • Playwright로 현재 흐름을 돌렸을 때 살아 있는가

이 기준으로 보니, 유지해야 할 코드와 정리해도 되는 코드가 조금씩 분명해졌다. 예를 들어 어떤 controller는 파일은 남아 있어도 실제 화면에서 더 이상 접근하지 않고 있었고, 어떤 템플릿은 경로만 남아 있을 뿐 현재 흐름에서는 완전히 빠져 있었다.

반대로 "별로 중요해 보이지 않는 것 같은데 실제로는 살아 있는 흐름"도 있었다. 그래서 미사용 코드를 지우는 일은 생각보다 조심스럽고, 동시에 생각보다 중요했다.

실제로 제거한 것들

초기 정리 단계에서 먼저 손댄 것들은 비교적 명확했다.

  • 더 이상 쓰이지 않는 검색 controller
  • 연결이 끊긴 recent 페이지와 관련 JS
  • controller 안에 남아 있던 운영성 dead code
  • 지금 구조에서 의미 없는 화면 진입점

이런 코드는 그대로 두고 domain / infra 분리에 들어갈 이유가 없었다. 나중에 필요하지도 않을 코드를 새 구조로 옮기는 건 손해다.

그리고 실제로 이걸 먼저 지우고 나니, 이후 리팩토링 대상이 훨씬 또렷해졌다. 남은 코드가 "현재 서비스에 필요한 코드"라는 전제가 생기면, 도메인 분리도 훨씬 덜 흔들린다.

재미있었던 점: 테스트가 dead code 정리에도 도움이 됐다

Playwright를 먼저 깔아둔 덕분에, 미사용 코드 정리도 훨씬 덜 불안하게 진행할 수 있었다.

어떤 파일을 지우더라도 현재 공개 흐름, 관리자 흐름, mutation 흐름이 그대로 통과하면 일단 "이건 현재 기준선에는 영향이 없다"고 판단할 수 있었다. 물론 테스트가 모든 걸 보장하지는 않지만, 최소한 리팩토링 중간에 "혹시 이거 살아 있었던 코드 아니야?" 같은 불안을 크게 줄여줬다.

실제로는 이 과정에서 몇몇 테스트가 먼저 깨지면서, 어떤 선택자가 너무 넓었는지, 어떤 페이지는 접근은 되지만 실제로는 흐름에서 빠져 있었는지도 같이 보이기 시작했다. 결과적으로 테스트 보강과 dead code 정리는 서로를 돕는 작업이었다.

Gradle 전환과 dead code 정리가 끝나고 나서야, 진짜 리팩토링이 시작됐다

이 과정을 지나고 나서야 비로소 내가 처음에 하고 싶었던 리팩토링, 즉 도메인 분리와 인프라 분리, Spring Data MongoDB 도입 같은 작업을 시작할 수 있었다.

돌이켜보면 이 순서가 꽤 중요했다.

  • 실행 기준을 먼저 정리하지 않으면 이후 이동이 불안해진다
  • 미사용 코드를 먼저 지우지 않으면 리팩토링 범위가 계속 흔들린다
  • 테스트를 먼저 만들어두지 않으면 지금 깨진 게 원래부터 문제였는지, 이번에 깨진 건지 알기 어렵다

결국 "코드를 잘 고치는 일"은 그 전에 "리팩토링할 수 있는 상태를 만드는 일"이 있어야 가능했다.

마무리

이번 단계에서 내가 한 일은 겉으로 보면 화려하지 않다. Maven을 Gradle로 옮기고, 폴더 구조를 정리하고, 안 쓰는 코드를 지우고, 앱이 계속 켜지는지 확인하는 일의 반복이었다. 하지만 운영 중인 서비스를 리팩토링할 때는 이런 작업이 오히려 가장 중요하다고 생각한다.

좋은 구조는 한 번에 만들어지지 않는다. 먼저 실행 가능한 상태를 유지하고, 리팩토링 범위를 줄이고, 지금 살아 있는 코드만 남겨야 한다. 그걸 하고 나서야 비로소 domain과 infra를 나누는 일이 의미를 갖는다.

다음 글에서는 실제로 posting부터 domain과 infra를 나누기 시작한 과정, 그리고 왜 Spring Data MongoDB를 먼저 도입하기로 했는지를 정리해보려고 한다.

§ 목차