기술

제외 조건은 이름 기준, 유니크 제약은 코드 기준이었습니다

2026.09.066분 읽기

운영 화면 하나가 사용자에게 이런 상태였습니다. 목록에는 아직 처리하지 않은 작업이 남아 있다고 표시됩니다. 그 항목을 누르면 시스템이 이미 처리한 건이라며 저장을 거부합니다. 어느 쪽도 거짓말을 하고 있지는 않았습니다. 두 판단이 서로 다른 기준으로 내려지고 있었을 뿐입니다.

프랜차이즈 ERP에서 겪은 일입니다. 여러 외부 시스템에서 들어온 거래처를 우리 쪽 매장에 연결하는 미확인 매핑 화면이 있습니다. 새로 붙인 외부 회계 SaaS의 거래처를 이 화면에 얹는 작업을 하다가, 기존 외부 공급사 시스템 쪽에 있던 결함이 같이 드러났습니다.


목록은 이름으로 걸렀고 저장은 코드로 막았습니다

어떤 외부 시스템은 같은 거래처를 여러 행으로 내려줍니다. 코드는 동일한데 이름 표기가 흔들리거나 예전 이름이 그대로 남아 있는 경우입니다. 원천에서는 정상적인 데이터입니다. 문제는 우리 쪽 조회 조건이었습니다.

미확인 매핑 목록은 이미 매핑된 것을 제외하는 조건을 이름 기준으로 걸고 있었습니다. 같은 코드라도 이름이 다른 행은 제외 대상에 걸리지 않고 목록에 그대로 남았습니다. 반면 저장 쪽 유니크 제약은 코드 기준이었습니다. 사용자가 목록에 남아 있는 그 행을 눌러 매핑하려는 순간 중복키 에러가 났습니다.

눌러야만 터지는 지뢰였습니다. 조회 시점에는 아무 문제가 없고, 저장 시점에만 실패합니다. 그래서 처음에는 저장 로직을 의심했습니다. 하지만 저장 쪽 제약은 정확했습니다. 같은 거래처를 두 번 매핑하면 안 된다는 규칙은 맞습니다. 틀린 쪽은 아직 안 한 것의 정의였습니다.

130-1.jpg

나머지 두 외부 시스템은 같은 화면을 쓰면서도 멀쩡했습니다. 그쪽은 제외 조건이 처음부터 코드 기준이었습니다. 결함은 두 곳에만 있었고, 화면 전체의 설계가 잘못된 것은 아니었습니다.


세 가지 선택지를 놓고 골랐습니다

고칠 방법은 하나가 아니었습니다. 목록을 제약에 맞출 수도 있고, 제약을 목록에 맞출 수도 있습니다.

선택지

기대 효과

포기한 이유

이름 기준을 유지하고 문제 행만 예외 처리

당장 이 화면의 에러는 사라짐

같은 결함이 다음 외부 시스템에서 또 발생

유니크 제약을 이름까지 확장

목록과 제약이 서로 맞아짐

같은 코드의 다른 이름이 각각 매핑돼 데이터가 갈라짐

제외 조건을 유니크 제약과 같은 키로 통일 (채택)

두 판단이 하나의 기준을 공유

코드가 안정적이라는 전제가 필요

두 번째 선택지가 특히 위험했습니다. 화면의 증상은 사라지지만, 같은 거래처가 표기별로 여러 매핑을 갖게 됩니다. 나중에 금액을 집계하는 사람이 그 사실을 모른 채 합계를 냅니다. 증상을 없애면서 원인을 데이터 안쪽으로 밀어넣는 방식이었습니다.

채택한 방식은 제외 조건을 코드 기준으로 바꾸고, 조회를 코드로 그룹핑해 코드당 한 행만 나오게 한 것입니다. 목록에 이미 매핑된 항목이 더 이상 뜨지 않았고, 클릭 시 에러도 사라졌습니다.

130-2.jpg


자동으로 붙일 것과 사람에게 남길 것을 나눴습니다

같은 날 반대편에서는 외부 회계 SaaS 거래처 392건을 우리 매장에 붙이는 작업을 했습니다. 지점명을 정규화해 매칭하는 일이었는데, 전부 자동으로 붙일 생각은 하지 않았습니다. 자동 매칭을 신뢰도에 따라 등급으로 나눴습니다.

정규화한 이름이 정확히 일치하는 것 — 327건, 자동 매핑

접두어가 우리 쪽 매장 하나에만 유일하게 걸리는 것 — 41건, 자동 매핑

사람이 후보를 보고 안전하다고 판단한 것 — 12건, 수동 확인 후 매핑

후보가 여럿이라 모호하거나 대응 매장이 아예 없는 것 — 12건, 손대지 않음

결과적으로 392건 중 380건을 붙였고 12건을 남겼습니다. 남은 12건을 0으로 만들려는 시도는 하지 않았습니다. 후보가 여럿인 것과 대상이 아예 없는 것은 성질이 다르고, 둘 다 사람이 봐야 판단이 됩니다. 억지로 붙인 매핑은 화면에서는 깔끔해 보이지만 나중에 금액을 틀리게 만듭니다.

130-3.jpg


분류 테이블을 만들기 전에 데이터로 걸러봤습니다

외부 회계 시스템의 거래처에는 매장이 아닌 상대가 섞여 있습니다. 공급사, 기관 같은 것들입니다. 처음에는 매장인지 아닌지를 표시하는 분류 테이블을 하나 만들 생각을 했습니다. 그런데 업무적으로 자명한 조건이 이미 데이터에 있었습니다. 채권 잔액이 잡히는 거래처만 대상으로 삼으니 나머지가 자연스럽게 걸러졌습니다. 컬럼 하나와 그 컬럼을 관리하는 화면 하나를 만들지 않았습니다.

반대로 우회한 것도 있습니다. 매핑 테이블에 브랜드 컬럼이 NOT NULL로 잡혀 있었습니다. 회계 거래처는 브랜드와 무관한데 값을 채워야 해서, 브랜드 없는 경로용 값으로 라우팅했습니다. 저장은 됩니다. 다만 브랜드 없음이라는 상태가 데이터 모델에 정식으로 존재하지 않는 채로 남았습니다. 언젠가 그 값으로 브랜드별 집계를 내는 사람이 나옵니다. 그래서 이 우회는 코드가 아니라 문서에 기록해뒀습니다.


같은 함정을 다시 만들지 않으려면

이번 건에서 남은 판단 기준을 정리하면 이렇습니다. 목록 화면에서 나는 저장 에러는 저장 로직보다 조회 조건을 먼저 의심하는 편이 빠릅니다. 저장 쪽 제약은 대개 오래전에 검토를 거쳐 들어가 있고, 나중에 붙은 조회 조건이 그 제약을 모르고 작성되는 경우가 많습니다.

이미 처리한 것을 제외하는 조건과 중복을 막는 제약이 같은 키를 쓰고 있는지 확인합니다

외부 시스템이 같은 개체를 여러 표기로 내려준다고 가정합니다. 이름은 표시용, 코드는 키용으로 역할을 나눕니다

자동 매칭은 신뢰도 등급으로 나누고, 낮은 등급은 자동화하지 않습니다

분류 컬럼을 추가하기 전에 기존 데이터만으로 걸러지는 업무 조건이 있는지 먼저 봅니다

NOT NULL을 우회하려고 특수값을 넣었다면 그 사실을 어딘가에 남깁니다

한계도 같이 적어둡니다. 코드 기준 그룹핑은 이름이 여럿일 때 어느 이름을 보여줄지의 문제를 남기고, 그 선택이 화면에 드러나지 않습니다. 코드를 재발급하거나 재사용하는 원천 시스템이라면 이 결론 자체가 뒤집힙니다. 접두어 유일 매칭도 지점명 체계가 규칙적일 때만 통하는 방법입니다.

수동으로 남긴 12건은 누가 언제 처리하는지의 흐름을 만들어두지 않으면 영원히 그 자리에 남습니다. 자동화 비율을 세는 것보다 남긴 건의 처리 경로를 정하는 쪽이 실제로는 더 오래 걸립니다.

#매칭키#유니크제약#데이터매핑#자동매칭#도메인모델링