모든 글

관리비 고지서에서 연체 도메인을 발견한 순간

평범한 고지서에서 상환 모델의 빠진 개념을 발견하고, 현실의 단서를 검증 가능한 소프트웨어 경계로 바꾸는 과정을 살펴본다.

출처 및 AI 안내: 이 글은 제미니의 개발실무 유튜브를 기반으로 작성되었습니다. gpt-5.6-sol 모델을 사용해 생성·편집했습니다.

도메인 모델에 빠진 개념의 이름을 찾기 전부터 무언가 잘못됐다는 느낌은 들 수 있다. 상환 흐름에는 성공과 실패가 있고, 실패하면 재시도하며, 미납 뒤에는 추가 이자가 생길 수 있다. 이 규칙을 상태와 조건으로 구현할 수는 있지만 전체 모델은 계속 불편하게 느껴질 수 있다.

서비스 공개를 준비하며 개념의 경계와 우선순위를 정리해도 이 불편함이 사라지지 않을 수 있다. 상환 실패와 재시도, 추가 이자를 한곳에 붙여 놓은 모양이 어색하지만 무엇이 빠졌는지는 아직 설명하기 어렵다. 이때 상태를 하나 더 추가해 감각을 덮기보다, 왜 정상 상환과 실패 이후의 규칙이 함께 놓일 때 어색한지 질문을 남겨 둘 필요가 있다.

이 사례를 풀어 준 단서는 새로운 패턴이나 더 복잡한 그림이 아니었다. 관리비 고지서였다.

세 가지 금액에서 서로 다른 사실을 보다

고지서에는 납기 안에 내야 할 금액, 미납액, 미납 연체료가 따로 표시되어 있었다. 이 항목을 나누어 보니 차이가 구체적으로 드러났다.

  • 납기 금액은 일정에 따라 지금 내야 할 돈이다.
  • 미납액은 이전에 냈어야 하지만 내지 않은 돈이다.
  • 연체료는 그 미납 때문에 새로 생긴 추가 비용이다.

평소라면 납기 금액만 확인하고 지나갈 수 있는 항목들이다. 그러나 상환 모델을 계속 고민하던 상태에서는 미납액미납 연체료가 별도 줄에 있다는 사실이 눈에 들어온다. 미납액은 과거의 의무가 아직 남아 있다는 정보이고, 연체료는 그 의무를 제때 이행하지 않아 새로 발생한 결과다. 금액이 모두 돈이라는 이유만으로 하나의 상태에 넣기에는 발생 시점과 이유가 다르다.

이 구조는 상환 문제와 닮아 있었다. 정상 상환 일정, 연체된 금액, 추가 연체 비용을 모두 하나의 상환 상태에 눌러 담을 필요는 없었다. 고지서는 연체를 별도의 개념으로 생각하게 만든 직접적인 계기가 됐다.

상환 시도의 실패는 특정 시점에 일어난 사건이지만, 내지 못한 의무는 그 시도가 끝난 뒤에도 남는다. 추가 비용 역시 실패 순간과 같은 것이 아니라 미납이 지속되어 생긴다. 생명주기가 다르다면 Repayment 하나가 일정, 실행 시도, 실패, 남은 의무와 추가 비용을 모두 뜻하게 만들기보다 연체가 별도 책임을 가질 수 있는지 살펴볼 이유가 생긴다.

그렇다고 관리비와 대출이 같은 도메인이라는 뜻은 아니다. 둘은 다르다. 현실에 이미 드러난 구분에서 아이디어를 얻었을 뿐이다.

계산식과 납기 정책, 재시도 규칙은 서로 다를 수 있다. 관리비 항목을 그대로 대출 클래스나 테이블로 옮기는 것은 모델링이 아니다. 가져올 수 있는 것은 정상 일정과 과거의 미이행, 그로 인한 새 비용을 현실이 분리해서 설명한다는 구조다. 이 구조가 실제 대출 규칙에도 맞는지는 별도로 확인해야 한다.

더 나은 질문을 가지고 코드로 돌아가기

코드에도 신호는 있었다. 실패와 재시도, 추가 이자는 정상 상환과 완전히 같은 방식으로 연결되지 않았다. 고지서가 차이를 눈에 보이게 만들자 이 관심사를 별도의 연체 경계로 볼 수 있는지 묻기 쉬워졌다.

예를 들어 실패 뒤의 재시도나 추가 이자를 다루는 코드가 정상 상환 흐름과 직접 관계가 적고 따로 바뀐다면 현실에서 본 구분을 지지하는 증거가 된다. 반대로 실제 정책상 두 책임이 언제나 함께 생성되고 함께 사라진다면 독립 개념으로 나눌 근거는 약할 수 있다. 비유 하나로 결론을 닫지 않고 코드의 변경 패턴과 실패 사례를 함께 봐야 한다.

고지서가 완성된 구현을 알려 준 것은 아니다. 질문이 바뀌었다. 상환에 조건 하나를 더 넣는 방법을 묻는 대신, 어디까지가 상환이고 상환 실패 뒤 무엇이 새로 시작되는지를 생각할 수 있게 됐다.

그 질문에서 상환은 예정된 의무와 그 실행을 맡고, 연체는 아직 내지 않은 금액과 그로 인해 생긴 추가 금액을 맡는 구성을 검토할 수 있다. 재시도는 두 개념의 규칙에 따라 이어진다. 거창한 아키텍처를 먼저 도입하지 않아도 한 객체가 지나치게 많은 시간대와 책임을 대표하던 문제를 줄일 수 있다.

막혀 있던 모델을 움직이는 데는 이런 변화만으로도 도움이 된다. 현실의 사례가 불편함에 이름을 붙여 주면 그 아이디어가 코드에도 맞는지 다시 확인할 수 있다.

현실에는 우리가 지원하는 과정이 이미 있다

소프트웨어는 코드 밖에서 이미 벌어지는 일을 지원하는 경우가 많다. 관리비 고지서, 휴대전화 요금의 연체, 늦은 반납에 붙는 페널티에는 사람들이 오래 다뤄 온 결과가 표현되어 있다. 이런 과정을 겪거나 본 사람은 그 구조를 더 빨리 떠올릴 수 있다.

문서의 칸과 줄은 대개 아무 이유 없이 나뉘지 않는다. 누군가 값을 별도로 계산하고, 사용자에게 설명하고, 수납하거나 정산해야 해서 분리했을 수 있다. 신청서는 두 사건이 다른 시점에 일어남을 보여주고, 운영자의 작업표는 주 애플리케이션이 아직 이름 붙이지 않은 책임을 드러낼 수 있다.

일부러 좋지 않은 경험을 해보라는 뜻은 아니다. 평범한 문서와 일상적인 절차도 좋은 단서가 될 수 있다는 말이다. 코드를 계속 들여다봐도 풀리지 않는다면 현실에서 비슷한 의무, 지연, 페널티를 어떻게 표현하는지 살펴보자.

경험이 중요한 이유도 여기에 있다. 비교할 수 있는 현실의 재료가 늘어난다. 풀리지 않는 소프트웨어 문제가 머릿속에 남아 있는 동안 일상의 물건이 익숙한 구조를 갑자기 보여줄 수 있다.

관찰을 설계로 연결할 때는 순서를 둘 수 있다.

  1. 비즈니스 과정과 가까운 문서나 작업 흐름을 찾는다.
  2. 어떤 값과 날짜, 행동을 따로 기록하는지 본다.
  3. 서로 다른 책임이나 생명주기가 그 구분을 만들었는지 묻는다.
  4. 현재 코드에도 같은 분리의 신호가 있는지 비교한다.
  5. 실제 정책과 실패 사례로 제안한 경계를 검증한다.

이 과정을 거치면 현실은 정답지가 아니라 가설을 만드는 출처가 된다. 코드만 볼 때 막혔던 질문을 바꿔 주고, 그 질문의 답은 다시 제품의 규칙에서 찾는다.

비유는 증명이 아니라 단서로 사용한다

고지서는 모델링 아이디어를 시작하게 할 수 있지만 그 설계가 옳은지는 실제 비즈니스 규칙이 결정한다. 대출 상환과 관리비 납부는 중요한 부분에서 다를 수 있다. 경계를 채택하기 전에 코드와 서비스의 실제 동작이 그 구분을 뒷받침하는지 확인해야 한다.

책상에서 코드를 읽는 일과 현실을 관찰하는 일은 어느 하나를 고르는 관계가 아니다. 현실에서 발견한 이름을 코드의 책임과 변경 패턴에 대입하고, 코드에서 생긴 의문을 다시 실제 문서와 업무 규칙에 묻는 왕복이 필요하다. 그 과정에서 처음의 불편함이 검증 가능한 설계 질문으로 바뀐다.

교훈은 거창하지 않다. 도메인 통찰이 언제나 대단한 기법에서 오는 것은 아니다. 계속 마음에 걸리던 모델이 현실에서 같은 종류의 사실을 나누는 방식을 발견한 순간 분명해질 수 있다. 화면 밖에서 단서를 찾고, 다시 코드로 가져와 모델링하기 어려웠던 동작을 설명하는지 살펴보자.