기술

검수 도구와 제품이 다르게 그룹핑하는 줄 알았는데 날짜 비교가 월 단위였습니다

2026.10.068분 읽기

B2B 채권 분석 시스템에서 변제 계획을 검수하는 도구와 실제 운영 경로가 같은 데이터를 다르게 묶는 것처럼 보였습니다. 여기서 묶는 단위는 '세대'입니다. 연속된 변제 조건(총회차·변제총액·변제시작일·종료일·삭제일)이 같으면 한 묶음으로 보는 도메인 개념입니다.

결함 후보를 판정하는 작업에서 이 항목을 따로 떼어내 2주 넘게 열어 뒀습니다. 작업 제목에는 아예 '도구가 기준인데 제품이 안 맞는 둘'이라고 적혀 있었습니다. 둘의 정의가 갈라져 있으니 맞추는 일이 남았다는 뜻으로 적은 제목입니다.

그 제목이 틀렸습니다. 둘은 애초에 같은 코드를 쓰고 있었습니다.


열어 둘 때 경계를 하나 같이 적어 뒀습니다

항목을 떼어낼 때 함께 적어 둔 문장이 있습니다. '도구가 기준이니 제품을 맞춘다'를 전제로 두지 않는다는 것입니다. 검수 도구가 틀린 축을 보고 있던 경우를 전에 이미 겪었기 때문입니다.

검수 도구는 심리적으로 기준처럼 취급됩니다. 틀린 것을 찾으려고 만든 물건이라 그렇습니다. 그런데 도구도 결국 코드고, 제품보다 늦게 따라가거나 처음부터 다른 축을 보고 있을 수 있습니다. 이번에는 그 경계가 다른 방향으로 값을 했습니다. 도구도 제품도 틀리지 않았고, 둘이 같이 쓰는 코드가 틀렸습니다.

두 화면이 같은 데이터를 다르게 보는 상황은 전에도 한 번 정리했습니다. 그때는 기준이 실제로 갈라져 있던 경우였습니다.

같은 원본을 두 경로가 다르게 판정했던 사례는 따로 적어 뒀습니다.


전제가 먼저 무너졌습니다

재조사는 단순했습니다. 두 경로의 세대 묶음 코드를 나란히 놓고 읽었습니다. 검수 도구의 세대 빌더가 운영 코드의 그룹핑 클래스를 그대로 가져다 쓰고 있었습니다. 그 클래스 주석에 재사용한다고 적혀 있었습니다.

그룹핑 기준은 한 벌이었습니다. 두 벌이라고 적어 둔 제목은 증상을 받아 적은 것이었고, 2주 동안 그 제목이 조사의 방향을 정하고 있었습니다. 기준이 다르다고 믿으면 할 일은 '어느 쪽에 맞출지 결정하기'가 됩니다. 기준이 한 벌이면 할 일은 '그 한 벌의 어디가 틀렸나'가 됩니다. 완전히 다른 작업입니다.

확인에 든 시간은 코드 두 군데를 열어 보는 정도였습니다. 고치는 데 든 시간보다 짧았습니다.

두 경로가 같은 그룹핑 클래스를 공유하고 있어 원인이 공유 코드로 좁혀지는 구조도


그러면 안 맞을 자리는 공유 코드뿐입니다

두 경로가 같은 코드를 부르는데 결과가 다르다면 범위가 좁아집니다. 입력이 다르거나, 그 공유 코드 자체가 상황에 따라 다른 답을 내는 것입니다. 이번 건은 두 번째였습니다.

그룹핑 비교가 호출하던 날짜 동등 비교 유틸이 월 단위로만 비교하고 있었습니다. 변제시작일이 같은 달에 들어 있으면 서로 다른 세대가 한 세대로 합쳐졌습니다. 같은 달 안에서 조건이 바뀐 계좌에서만 이 현상이 나타났습니다.

월 단위 비교는 대부분의 데이터에서 맞는 답을 냅니다. 변제 조건이 바뀌는 간격이 보통 한 달보다 길기 때문입니다. 그래서 이런 비교는 오래 살아남습니다. 테스트도 통과하고, 운영 화면도 멀쩡해 보이고, 같은 달 안에서 두 번 바뀐 소수의 계좌에서만 틀립니다.

시간 값의 동등 비교에는 항상 '어느 단위로 같은가'가 숨어 있습니다. 날짜·시각 비교 유틸을 쓸 때 그 단위를 확인하지 않으면, 함수 이름이 같아 보여서 그냥 지나갑니다.

같은 달에 시작한 두 세대가 월 단위 비교에서 하나로 합쳐지고 일 단위 비교에서 둘로 나뉘는 비교 그림


한 곳을 고치니 양쪽에 같이 반영됐습니다

수정은 날짜 동등 비교를 일 단위로 가르는 함수를 두고, 그룹핑 비교가 그쪽을 부르게 한 것입니다. 공유 코드라 검수 도구와 제품 경로에 동시에 반영됐습니다. 경로마다 따로 보정하는 것보다 작은 수정이고, 한쪽만 고치면 다른 쪽은 계속 틀린 채로 남습니다.

다만 공유 코드 수정은 반대 방향으로 위험합니다. 양쪽에 동시에 반영되니 회귀 범위도 두 배입니다. 두 경로의 판정 결과를 같이 다시 재지 않으면 멀쩡했던 한쪽을 조용히 망가뜨립니다. 그래서 고친 뒤에 확인한 것은 '합쳐지던 세대가 나뉘었나'만이 아니라 '나뉘지 않아야 할 묶음이 그대로인가'였습니다.

기존 비교 함수를 지우지 않고 일 단위 함수를 따로 둔 이유도 여기 있습니다. 회차가 월 단위로 도는 도메인이라면 월 단위 비교가 맞는 자리도 있습니다. 주석과 요구사항을 확인한 뒤에 갈라야 합니다.


남은 차이는 하나로 좁혀졌습니다

공유 코드를 고치고 나니 두 경로의 차이가 한 가지로 줄었습니다. 파생값을 다시 계산하는 함수의 호출 여부입니다. 검수 도구는 그 함수를 부르지 않고, 그 이유가 코드 주석에 적혀 있었습니다. 제품 경로는 부릅니다.

그 함수는 변제종료일을 계획표의 회차 기준으로 덮어씁니다. 이것이 같은 조건인데 계획표가 매주 다시 저장되던 동작의 원인이었습니다. 파생값을 같음 판정의 키에 넣으면 같은 입력인데 계속 달라 보입니다. 저장 트리거가 흔들리고, 원인은 데이터가 아니라 판정에 있습니다.

대응은 동등 판정에서 파생값(변제종료일·총회차) 비교를 빼는 쪽으로 했습니다. 그러고 나니 같은 조건에 대한 재저장이 멈췄습니다.

열어 뒀던 항목도 다시 세웠습니다. '정의를 맞추는 일'에서 '파생값 재계산이 제품 경로에서만 값을 덮는 것이 맞나'로 바꿨습니다. 그대로 두는 것이 맞다고 판단하면 항목을 닫아도 되는 상태가 됐습니다.


두 경로가 안 맞을 때 보는 순서

두 경로가 같은 코드를 공유하는지 먼저 확인합니다. 클래스 주석에 이미 적혀 있을 수 있습니다

공유하고 있으면 원인은 정의가 아니라 그 공유 코드입니다. 어느 쪽에 맞출지 고민하는 단계로 넘어가지 않습니다

검증 도구를 기준으로 못 박지 않습니다. 도구도 코드고, 도구가 틀린 축을 볼 수도 있습니다

시간 값의 동등 비교는 단위를 확인합니다. 월 단위인지 일 단위인지가 함수 이름에 안 적혀 있을 수 있습니다

같음 판정의 키에 파생값이 들어 있는지 봅니다. 들어 있으면 같은 입력이 계속 달라 보입니다

공유 코드를 고친 뒤에는 두 경로의 판정 결과를 같이 다시 잽니다. 회귀 범위가 두 배입니다


전제 확인이 항상 싼 것은 아닙니다

이번 건에서 전제를 확인하는 데 든 시간은 고치는 시간보다 짧았습니다. 그렇다고 전제 확인이 늘 값싸다고 적을 수는 없습니다. 같이 열어 뒀던 두 번째 항목은 손도 못 댔습니다. 정상 사유 판정 범위가 도구와 제품에서 다르다는 항목이었고, 마감 날짜가 그날이었는데 그대로 지났습니다.

그리고 이 글의 결론이 안 통하는 경우가 있습니다. 두 구현이 진짜로 갈라져 있는 경우입니다. 그때는 정말 정의를 맞추는 일이고, 어느 쪽을 기준으로 삼을지 정해야 합니다. 그래서 재사용 여부 확인이 맨 앞에 옵니다. 그 확인이 두 갈래 중 어느 작업을 할지를 정합니다.

'두 기준이 다르다'는 문장은 결론처럼 생겼지만 가설입니다. 작업 제목으로 적어 두면 그 가설이 조사의 방향을 정합니다. 2주 동안 제가 그렇게 두고 있었습니다.

두 경로가 안 맞을 때 코드 공유 여부로 갈라지는 두 갈래 판단 흐름도
#그룹핑#날짜비교#공유코드#검수도구#근본원인