기술

같은 뜻의 상태값이 넷, 코드가 기다리는 표기는 데이터에 0건이었습니다

2026.09.229분 읽기

B2B 채권 분석 시스템을 운영하면서 개인회생 계좌 하나가 눈에 걸렸습니다. 입금은 매달 들어오는데 잔액이 8개월째 그대로였습니다. 월 자동처리가 그 계좌에 대해 개시도 상각도 만들지 않고 있었습니다.

그동안 에러 로그는 한 건도 없었습니다. 실패 알림도 없었습니다. 배치는 매달 정상 종료했고, 그 계좌만 조용히 빠져 있었습니다.


처리 실패가 아니라 입력 누락이었습니다

먼저 외부 기관의 사건 조회 화면을 열어 봤습니다. 법원 쪽에는 그 사건에 「폐지취소후 인가」가 찍혀 있었습니다. 폐지됐던 사건이 되살아나 있다는 뜻입니다.

그런데 우리 쪽 상태변경 로그 테이블에는 그 줄이 없었습니다. 마지막 두 줄은 2023-01-11 인가(데이터 이관 표시가 붙은 행)와 2025-12-17 폐지(월배치가 넣은 행)였습니다. 그 뒤로는 아무것도 없었습니다.

개인회생 마스터도 법원 상태가 폐지, 폐지일이 2025-12-15 였습니다. 그래서 2025-12-31 에 상환 중단 성격의 원장 이벤트가 찍혔고, 거기서 월 자동처리가 멈췄습니다.

여기서 문제의 성격이 바뀌었습니다. 「왜 안 도나」가 아니라 「무엇이 안 들어왔나」를 봐야 하는 문제였습니다.


같은 뜻의 표기가 넷, 살아 있는 건 둘

그럼 그 상태는 평소에 어떤 이름으로 들어오는지 봐야 했습니다. 법원 상태 컬럼을 값별로 세어 봤습니다. 「폐지취소」가 들어간 값이 넷 나왔습니다.

값 표기

건수

최근 등록

폐지취소결정 인가

5,144

2026-09-01 (계속 들어옴)

폐지취소결정 개시

336

2026-08-29 (계속 들어옴)

폐지취소결정후 개시

626

2024-07-30 (데이터 이관 때만)

폐지취소결정송달후 인가

8

2020-05-06 (데이터 이관 때만)

건수만 보면 넷 다 살아 있어 보입니다. 626건도 적은 수가 아닙니다. 그런데 최근 등록일을 붙이는 순간 둘로 갈렸습니다. 위의 둘은 지금도 계속 들어오고, 아래 둘은 데이터 이관 때 한 번 들어온 뒤로 멈춰 있는 화석이었습니다.

이 상태에서 운영 코드를 열어 봤습니다. 상환 대상을 거르는 필터 목록에 「폐지취소결정후 인가」가 들어 있었습니다. 이 표기는 위 넷 중 어디에도 없습니다. 어느 시점에도 들어온 적이 없는 다섯 번째 표기였습니다.

반대로 지금도 계속 들어오는 「폐지취소결정 개시」 336건은 그 목록에 아예 없었습니다. 코드는 오지 않는 값을 기다리고, 오는 값은 못 알아보고 있었습니다.

같은 뜻의 상태값이 넷, 코드가 기다리는 표기는 데이터에 0건이었습니다 그림 1


왜 8개월 동안 아무도 몰랐나

이 종류의 문제는 에러를 내지 않습니다. 필터가 0건을 돌려주는 것은 정상 동작입니다. 예외도 안 나고, 모니터링에도 안 걸립니다.

배치 입장에서는 「이번 달에 처리할 계좌가 없다」와 「처리해야 할 계좌를 못 알아봤다」가 같은 결과입니다. 둘 다 0건이고, 둘 다 정상 종료입니다.

그래서 멈춘 자리가 「해당 없음」으로 보였습니다. 로그를 아무리 봐도 실패한 줄이 없었습니다. 로그로는 찾을 수 없는 문제였습니다.

발견 신호는 다른 데서 왔습니다. 「입금은 들어오는데 잔액이 안 준다」는, 도메인이 보장해야 하는 관계가 깨진 것이었습니다. 사람이 잔액을 들여다보지 않았다면 이 계좌는 지금도 멈춰 있었을 겁니다.

같은 뜻의 상태값이 넷, 코드가 기다리는 표기는 데이터에 0건이었습니다 그림 2


로그 한 줄로 되살렸습니다

즉시 조치는 보정 한 줄이었습니다. 상태변경 로그에 2026-08-01 자로 「폐지취소결정 인가」 한 행을 넣었습니다. 그러자 파이프라인이 끝까지 이어졌습니다.

조정이력 생성

계좌 참조 생성

8/01 개시

8/28 입금 반영

8/31 상각

8개월 만의 재개였고, 그 달 상각까지 도달했습니다.

확인된 것이 하나 더 있습니다. 마스터는 여전히 폐지·폐지일 그대로인데 정상 개시됐습니다. 개인회생 판정 서비스는 그 달의 상태변경 로그만 읽고, 마스터의 법원 상태 컬럼은 보지 않습니다.

설계 문서에는 「마스터의 폐지는 사건이 되살아나도 외부 기관이 지우지 않는다」고 적혀 있었습니다. 서술로만 있던 문장이 실물 사례를 얻었습니다.

재수행에서 걸린 것도 적어 둡니다. 그 달 조정이력이 이미 있으면 조정 생성이 예외를 던집니다. 그 달치를 먼저 지우거나 덮어쓰기 옵션으로 돌려야 지나갑니다.

필터 목록 정리는 미결로 남겼습니다. 0건 표기를 빼고 실제로 들어오는 표기를 넣는 일은 한 계좌의 응급 처치와 위험도가 다릅니다. 판정 규칙을 건드리면 지금 정상으로 도는 계좌들의 판정이 같이 바뀝니다. 그래서 별건으로 분리했습니다.


조사는 쿼리 한 번이었습니다

코드가 참조하는 상태값 목록은 데이터와 대조해 검증할 수 있습니다. 상태값별로 건수와 최근 등록일을 묶는 쿼리 한 번이면 「코드에만 있는 값」과 「데이터에만 있는 값」이 동시에 나옵니다. 이번 조사도 그랬고, 몇 분이면 됐습니다.

SELECT 상태값, COUNT(*) AS 건수, MAX(등록일) AS 최근등록
  FROM 상태변경_로그
 GROUP BY 상태값
 ORDER BY 최근등록 DESC;

최근 등록일을 같이 세는 것이 핵심입니다. 건수만 보면 626건짜리 표기가 살아 있어 보입니다. 최근 등록일을 붙여야 「지금 들어오는 표기」와 「이관 때 한 번 들어온 표기」가 갈립니다.

표기가 갈리는 지점은 대개 데이터 이관입니다. 원천 시스템이 바뀌거나 이관 스크립트가 다르게 정규화하면, 같은 뜻이 두세 개 이름을 갖고 한 컬럼에 공존합니다. 그리고 그 뒤로 아무도 그걸 세지 않습니다.

같은 뜻의 상태값이 넷, 코드가 기다리는 표기는 데이터에 0건이었습니다 그림 3


0건이 다 버그는 아니고, 한 줄이 다 고친 것도 아닙니다

「코드에 있는데 데이터에 0건」이 항상 버그는 아닙니다. 아주 드물게만 오는 값이면 0건이 정상입니다. 최근 등록일과 전체 이력을 같이 봐야 화석·미사용·희귀가 갈립니다.

표기를 하나로 통합하는 것도 늘 옳지는 않습니다. 옛 표기가 「그 시점에 원천이 그렇게 줬다」는 사실 자체를 담고 있으면, 값을 덮어쓰는 순간 그 이력이 사라집니다. 이 건에서도 쓰는 쪽인 필터 목록을 넓히는 게 맞지, 데이터를 고칠 일이 아니었습니다.

로그 한 줄 삽입은 응급 처치입니다. 그 줄이 애초에 왜 안 들어왔는지는 아직 안 봤습니다. 수집 경로가 그 상태 변경을 아예 못 받는 건지, 받았는데 버린 건지 모릅니다. 같은 일이 다른 계좌에서 조용히 진행 중일 가능성이 그대로 남아 있습니다.

판정이 로그 테이블 단일 원천인 것도 양날입니다. 마스터를 안 보니 종료된 마스터에 발목 잡히지 않습니다. 대신 로그 한 줄만 잘못 들어가도 판정이 통째로 바뀝니다. 이번엔 그 성질을 복구에 썼지만, 같은 성질이 사고 경로이기도 합니다.


상태 기반 파이프라인을 운영한다면

코드가 참조하는 상태값 목록을 데이터의 실제 값 분포와 대조한 적이 있는가. 건수와 최근 등록일을 같이 본다

데이터 이관 뒤 같은 뜻의 표기가 몇 개로 갈렸는지 센 적이 있는가

필터가 0건을 돌려줄 때 「해당 없음」과 「못 알아봄」을 구분할 수단이 있는가

「입금은 있는데 잔액이 안 준다」처럼 도메인이 보장해야 하는 관계를 주기적으로 검사하는 자리가 있는가

목록에 없는 상태값이 들어오면 알리는 경로가 있는가. 외부 값이라 enum 으로 좁히기 어려워도 이 경로는 둘 수 있다


마무리

이 계좌는 8개월 동안 한 번도 실패하지 않았습니다. 매달 정상 종료한 배치 안에서 조용히 빠져 있었을 뿐입니다. 멈춤과 해당 없음이 같은 모양이면, 로그는 그 둘을 갈라 주지 않습니다.

그래서 발견은 불변량 쪽에서 옵니다. 상태값을 자유 문자열로 받는 시스템이라면, 코드가 기다리는 값과 데이터에 실제로 있는 값을 한 번 대조해 보는 편이 낫습니다. 쿼리 한 줄이면 되고, 안 하면 몇 달이 그냥 지나갑니다.


연작 「에러 없이 조용히 틀린다」

이 글은 다섯 편 중 2편입니다. 에러가 한 번도 안 났는데 실제로는 일이 안 되고 있던 사례를 이어서 다룹니다.

#상태값#표기불일치#데이터이관#조용한실패#배치운영#불변량검사