포스트

[업무일지] 참조 0건이 증명하지 못한 API 사용처

레거시 Spring API의 사용처를 여러 저장소에서 확인한 뒤, 호출이 없다고 판단한 엔드포인트를 지웠습니다. 다음 날 어드민 기능 하나가 404를 받았습니다. 관리자 저장소의 문자열 경로가 수동 인벤토리에서 빠져 있었고요. 해당 API만 복구하고, 삭제 후보의 사용 여부를 판단하는 범위를 다시 잡았습니다.

Java 17, Spring Boot 2.6.x, MyBatis를 쓰는 레거시 API 서버입니다. 이 서버를 호출하는 관리자 페이지는 PHP로 되어 있고, 저장소도 따로 나뉘어 있고요.

사용 중인 API부터 목록으로 만들었다

오래된 API 서버를 정리하고 있습니다. 컨트롤러는 남아 있는데 실제로 누가 호출하는지 알기 어려운 코드가 많았습니다. 서비스와 쿼리까지 따라가면 한 엔드포인트를 지우는 것만으로 파일 여러 개가 같이 없어지는 구조였고요.

바로 지우지는 않았습니다. 먼저 컨트롤러의 전체 엔드포인트를 목록으로 만들었습니다. 서버 내부 참조를 찾고, 이 서버를 호출하는 다른 API 서버와 관리자 저장소도 검색했습니다. 그 과정에서 외부 호출을 찾은 후보는 삭제 대상에서 뺐죠.

남은 후보를 지운 뒤에는 Java 컴파일도 확인했습니다. 라우팅되지 않는 요청을 잡기 위해 404 로그도 보강했고요. 요청 경로와 HTTP 메서드, 호출자 정보를 남기도록 했습니다.

여기까지 했을 때는 근거가 충분하다고 봤습니다. 서버 안에서 호출이 없고, 알고 있는 호출 저장소에서도 찾지 못했고, 컴파일도 통과했으니까요.

어드민의 수동 처리 화면이 404를 받았다

배포 뒤 운영자가 특정 정산 건을 수동으로 보정하는 화면이 동작하지 않았는데, 버튼을 누르면 삭제한 API로 요청이 나가고 있었습니다.

배포 전에 보강해둔 404 로그에도 그 요청이 남아 있었습니다. 그런데 정작 그 로그가 아니라 운영자 문의로 먼저 알게 됐고요.

서버 쪽에는 그 컨트롤러를 직접 부르는 코드가 없습니다. HTTP 엔드포인트니까 그게 정상입니다. 실제 호출은 오래된 관리자 저장소에 문자열 경로로 남아 있었습니다.

관리자 화면은 별도 애플리케이션이라 서버를 컴파일해도 이 호출은 검사되지 않고, Spring 컨트롤러 메서드의 사용처 찾기를 실행해도 당연히 나오지 않습니다.

제가 놓친 건 프레임워크 동작이 아니었습니다. 호출 그래프의 경계였죠. 서버 저장소 안에서 참조 0건이라는 사실을 시스템 전체에서 사용 0건으로 넓혀 받아들였습니다.

관리자 저장소도 조사 대상에는 들어 있었습니다. 그래서 더 정확히 말하면 검색을 아예 안 한 게 아니라, 사람이 검색 결과를 인벤토리로 옮기는 과정에서 한 경로가 빠졌습니다. 전체 엔드포인트와 모든 호출 문자열을 기계적으로 대조한 결과물이 없었는데, 완성된 목록처럼 다룬 겁니다.

확인된 호출 경로만 복구했다

팀에서는 삭제한 정리를 전부 되돌리지 않고, 실제 호출이 확인된 API만 복구했고요. 컨트롤러만 되살려서는 안 됐습니다. 서비스와 DAO, 요청·응답 모델, MyBatis 쿼리까지 한 호출 경로를 같이 복원했습니다.

전체 변경을 되돌리는 방법도 있었습니다. 그러면 장애 복구는 빨랐겠지만 사용하지 않는 것으로 확인한 코드까지 다시 돌아옵니다. 이미 분리해둔 삭제 근거도 사라지고요.

그래서 복구 범위를 호출 한 건에 필요한 경로로 제한했죠. 운영 기능을 먼저 살리고, 나머지 삭제 결과는 유지했습니다.

이 선택에도 비용은 있었습니다. 레거시 코드는 한 API가 여러 쿼리와 상태 변경을 묶고 있어서 복구 diff가 작지 않았습니다. 컨트롤러 메서드 한 개만 보고 영향 범위를 짐작할 수 없는 구조였죠.

참조 0건의 범위를 다시 정했다

바꾼 기준에서는 참조 0건만 적지 않습니다. 어느 저장소를 어떤 경로로 검색했는지를 같이 남깁니다. 같은 숫자라도 범위가 다르면 의미가 달라지거든요.

삭제 후보 하나를 볼 때 확인할 범위는 세 군데였습니다.

  • API 서버 내부의 메서드·서비스 참조
  • 해당 서버를 호출하는 다른 서버의 HTTP 경로
  • 관리자 페이지의 컨트롤러·화면에서 조합하는 경로

특히 오래된 관리자 코드는 공통 클라이언트에 URL을 넘기는 방식이라, Java 메서드 이름이 아니라 실제 요청 경로로 찾아야 했습니다. 메뉴가 남아 있는지, 그 메뉴에서 어떤 컨트롤러를 거쳐 요청을 만드는지도 같이 봤습니다.

404 로그는 보조 수단으로 남겼습니다. 삭제한 경로로 요청이 들어오면 어느 화면에서 왔는지 추적하는 데는 도움이 됩니다. 하지만 이건 요청이 실패한 뒤에야 알려주는 장치입니다. 삭제해도 된다는 사전 증명은 아니었습니다.

맺음말

이번 정리에서 틀린 건 사용처를 검색한다는 방향이 아니었습니다. 검색 범위와 결과를 연결하는 방식이 느슨했습니다. 여러 저장소를 훑었다는 사실만 있었고, 엔드포인트마다 호출 주체를 대조했다는 증거는 없었죠.

호출이 확인된 API만 복구하고 나머지 삭제는 유지했습니다. 전체 원복보다 범위를 좁힌 선택이었지만, 수동 인벤토리에 계속 기대는 한계는 그대로입니다.

OpenAPI 기반 클라이언트나 소비자 계약 테스트까지 만들지는 않았습니다. 관리자 페이지가 문자열로 경로를 조합하는 구조도 남아 있습니다. 정적 검색과 404 로그를 같이 보는 수준이라, 동적으로 만들어지는 경로나 드물게 쓰는 운영 기능은 다시 놓칠 수 있습니다.

이 대조를 자동화하지는 못했습니다. 다음 정리에서도 사람이 같은 확인을 반복해야 합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.