Home > AI > Frontend > 죽은 파일이 살아있는 파일보다 최신이었다

죽은 파일이 살아있는 파일보다 최신이었다
AI Claude Code 리팩터링 기술부채

감사의 목적은 번들 축소가 아니었다

죽은 코드 감사를 시작한 이유는 흔한 것 — 번들 크기나 파일 수 — 이 아니었다. “죽은 코드를 살아있다고 착각하고 거기에 작업하는 사고”를 막는 것이 목적이었다.

계기는 그 사고가 이미 여러 번 일어났기 때문이다. 조사해보니 2주 동안 같은 패턴이 5회 반복됐다.

원인: git log가 거짓말을 한다

가장 인상적인 사례는 이랬다.

라우터가 import하지 않아 어떤 경로로도 도달할 수 없는 파일이 있었다. 그 파일의 마지막 커밋은 7일 전이었다. 반면 실제로 화면에 그려지는, 살아있는 파일의 마지막 커밋은 1년 4개월 전이었다.

파일을 고치러 들어온 사람 입장에서 판단 근거는 보통 이렇다. 파일명이 비슷한 게 둘 있으면 최근에 손댄 쪽이 현행이겠거니 한다. 그런데 이 경우 죽은 파일이 훨씬 최신으로 보였다. 살아있는 파일은 1년 넘게 안정적으로 돌아가서 손댈 일이 없었을 뿐인데.

한 번 착각이 발생하면 자기강화된다. 누군가 죽은 파일을 고치면 그 파일의 타임스탬프가 또 갱신되고, 다음 사람은 더 확신을 갖고 같은 파일을 고친다. 5회 반복은 그렇게 쌓인 것으로 보인다.

실사용자 버그로도 이어졌다

죽은 파일에 작업하면 보통은 “아무 일도 안 일어난다”로 끝난다. 그런데 이번엔 실제 버그가 나왔다.

문제의 커밋이 죽은 파일에 data-guide 앵커를 추가했다. 이 프로젝트의 가이드 투어는 data-guide 속성을 타겟팅해서 화면 위에 툴팁을 띄우는 방식이다. 투어 정의는 새로 추가된 앵커를 가리키게 됐는데, 그 앵커가 붙은 컴포넌트는 렌더링되지 않는다. 투어가 영원히 타겟을 못 찾는다.

정합성 테스트가 이걸 잡아냈다. data-guide를 소스에서 정규식으로 스캔해 투어가 가리키는 타겟이 실제로 존재하는지 확인하는 테스트다. 여기서 한 가지 더 배운 게 있는데, 앵커를 prop으로 전달하는 방식(treeGuideAnchor 같은)으로 리팩터링하면 정규식 스캔이 인식을 못 해서 테스트가 오탐을 낸다. 앵커는 JSX 리터럴로 두는 게 이 테스트와 맞는다.

정리한 것

PR 여러 건에 나눠 진행했다.

대상 규모
도달 불가 파일 누적 102개
미사용 폰트·아이콘 약 11MB
배럴 파일에만 남은 고아 아이콘 49개 (약 522KB)
주석 처리된 죽은 코드 블록 수천 줄
도달 불가 분기 TEST_MODE 분기 158줄

부수 발견: 애초에 동작할 수 없던 API 2건

죽은 API 정의를 찾다가 “죽지는 않았는데 동작할 수 없는” 것들이 나왔다.

1) 405가 확정인 API. 프론트가 POST를 보내는데 백엔드에는 @PutMapping만 있었다. 호출하면 무조건 405 Method Not Allowed다. 아무도 신고하지 않았다는 건 이 기능이 실사용되지 않고 있었다는 뜻으로 보인다.

2) 템플릿 리터럴 보간이 빠진 API. 경로를 조립하는데 백틱을 안 쓰거나 ${}를 빠뜨려서, BASE_PATH라는 문자열 자체가 URL로 나가고 있었다.

둘 다 “코드가 존재한다”와 “코드가 동작한다” 사이의 간극이다.

지우면 안 되는 것들도 있었다

감사를 무작정 밀면 사고가 난다. 반례를 두 가지 만났다.

동명 함수 함정

postUnitProcess라는 엔드포인트가 두 파일에 있었다. unit-process/api.tsdata-management/api.ts다. 이름만 보고 “중복이네” 하고 지우면 화면이 죽는다.

실제로는 소비자가 있는 쪽이 data-management 쪽이었다. 그쪽이 만드는 usePostLcaUnitProcessMutationuseUnitProcess.tsx가 쓰고 있었다. unit-process 쪽만 usePostUnitProcessMutation 소비자가 0건이라 그것만 제거했다. 판단 기준을 함수 이름이 아니라 생성된 훅의 소비자 수로 잡아야 했다.

참조가 없는데 지우면 안 되는 진입점

참조 0건이라 죽은 것처럼 보이지만, 지우면 테스트 173개가 한꺼번에 무너지는 파일이 있었다. 프로덕션 코드에서 참조되지 않을 뿐 테스트의 진입점 역할을 하고 있었다. “정적 참조 0건 = 죽음”이라는 규칙이 항상 맞지는 않는다.

남는 교훈

죽은 코드 정리를 “청소”로 생각하면 우선순위가 낮아진다. 실제 위험은 용량이 아니라 판단 근거를 오염시키는 것이다. 죽은 파일이 최신 타임스탬프를 달고 있으면, 다음 사람이 어디를 고쳐야 하는지 판단하는 근거 자체가 거짓이 된다. 그리고 그 오류는 한 번 발생하면 스스로를 강화한다.

동시에 감사 자체도 검증이 필요했다. 동명 함수와 무참조 진입점 두 사례에서 “참조가 없다”는 신호가 각각 다른 이유로 틀렸다. 삭제는 되돌리기 쉽지만, 삭제 후 CI가 통과하는지까지는 확인하고 넘어가야 했다.