리팩터링 도구나 YAGNI 감사에서 가장 매력적인 숫자는 호출자 0이다.

호출자가 없으면 지워도 될 것처럼 보인다.

실제로 많은 경우 그렇다.

하지만 항상 그런 것은 아니다.

어떤 코드는 호출되지 않는 것이 정상이고, 오히려 그 사실을 증명하기 위해 존재한다.

이 글에서는 이런 성격의 코드를 편의상 Negative-Space Guard라고 부르겠다. 표준화된 분류명이라기보다, “정상 상태에서는 호출되지 않지만 금지된 상태를 명시하는 코드”를 설명하기 위한 표현이다.

이 성격을 이해하지 못하면 코드 줄 수는 줄지만 시스템의 안전성도 같이 줄어드는 리팩터링을 할 수 있다.


1. “사용되지 않는다”와 “도달하면 안 된다”는 다르다

예를 들어 상태 enum에 “정상 경로에서는 절대 만들어지면 안 되는 값”이 하나 있다고 하자.

그런데 값 자체는 정의돼 있다.

그리고 테스트는 이렇게 말한다.

이 값은 정상 경로에서 절대 나타나면 안 된다.
만약 나타나면 즉시 실패해야 한다.

호출자 수만 보면 죽은 코드다.

하지만 의미는 정반대다.

이 타입은 금지된 상태를 표현하는 안전장치다.


2. Negative-Space Guard란

일반 코드는 “무엇이 일어나야 하는가”를 표현한다.

Negative-space guard는 반대다.

이 타입은 생성되면 안 된다.
이 분기는 도달하면 안 된다.
도달하면 즉시 실패해야 한다.
이 값은 노출되면 안 된다.

즉 존재 이유가 실행이 아니라 금지된 상태를 명시적으로 표현하는 것이다.


3. 왜 자동 감사가 잘못 분류하는가

Knip, ts-prune, Vulture, ESLint의 no-unused-vars 같은 도구가 보는 지표는 대체로 다음과 같다.

  • caller count
  • reference count
  • coverage
  • duplication
  • complexity

Negative-space guard는 이 모든 지표에서 불리하다.

호출자가 적다
정상 경로에서는 실행되지 않는다
coverage가 낮을 수 있다

그래서 자동으로 “삭제 후보”에 올라온다.


4. 테스트를 먼저 읽어야 한다

삭제 후보가 올라왔을 때 가장 먼저 볼 것은 호출 그래프가 아니라 테스트일 수 있다.

특히 이런 테스트는 강한 신호다.

언어·도구“도달하면 안 된다”를 적는 방법
TypeScriptassertNever(x) — 남은 케이스가 있으면 타입 검사에서 걸린다
Python 3.11+typing.assert_never(x)
Rustunreachable!()
Javadefault: throw new AssertionError(x)
pytestpytest.raises(...) — 실패하는 것이 정상인 경로

이런 테스트는 “있어야 한다”가 아니라 “없어야 한다”를 검증한다. 도달 불가를 타입 검사기가 아는 형태로 적어 두면(assertNever 계열) 케이스가 늘어났을 때 빌드가 먼저 알려준다 — 런타임에 터지기 전에.


5. 보안 경계에서도 자주 나타난다

Negative-space guard는 보안 코드에서 특히 중요하다.

예를 들면:

민감정보가 로그에 나타나면 안 된다.
특정 상태에서 권한이 상승하면 안 된다.
금지된 값이 처리 대상에 들어오면 안 된다.

이런 invariant는 정상 운영에서는 실행 흔적이 거의 없다.

그래서 단순 usage 기반 감사로 보면 불필요해 보인다.


6. 삭제 전 질문

호출자 0이라는 이유로 지우기 전에 다음을 확인한다.

[ ] impossible state를 표현하는가
[ ] fail-loud 역할을 하는가
[ ] security boundary인가
[ ] forbidden path를 타입으로 표현하는가
[ ] 테스트가 "없어야 함"을 검증하는가
[ ] 삭제하면 회귀 검증 능력이 사라지는가

7. YAGNI 규칙에 추가할 예외

YAGNI 감사에서는 다음 범주를 별도로 분류하는 것이 좋다.

negative-space guard
impossible-state representation
fail-closed branch
security boundary
forbidden-path representation
invariant enforcement

이런 코드는 “얼마나 쓰이는가”가 아니라 “무엇을 막는가”를 봐야 한다.


결론

호출자 0은 강한 신호지만 판정은 아니다.

Dead code와 negative-space guard는 외형이 매우 비슷하다.

차이는 실행 빈도가 아니라 설계 의미다.

리팩터링할 때는 항상 한 번 더 물어야 한다.

이 코드를 없애면 어떤 잘못된 상태가 더 이상 명시적으로 금지되지 않는가?

📦추천 상품🔒 개인정보 수집 없음

쿠팡 로켓배송 특가 & 가성비 추천 제품 확인

이 글에서 소개하거나 추천하는 IT 기기, 가공식품, 가성비 필수 꿀템을 쿠팡 로켓배송 최저가로 빠르게 만나보세요.

쿠팡 최저가 확인하기 →