← 모든 글
PG 플랫폼

한 시스템을 두 번 갈아엎고 떠났다

ASP 기반 PG를 Spring Boot 모놀리스로 옮기고 1년도 안 돼 다시 셋으로 나눴다. 그 판단이 지금도 맞다고 보는 이유와, 끝내지 못하고 두고 온 것을 적었다.

회사의 사업 방향이 달라졌고, 바뀐 방향에서 내가 이어서 할 일이 남아 있지 않았다. 더 다니고 싶었지만 그럴 수 있는 상황이 아니었다. 그래서 이 글은 떠나는 이유가 아니라 두고 오는 것에 대한 기록이다.

2023년 6월에 들어가 2년 5개월을 있었다. 기록을 남기기 시작한 것은 2024년 4월부터라 앞의 열 달은 이 블로그에 없다. 그 뒤로 이 시스템은 구조가 두 번 바뀌었다. ASP 기반에서 Spring Boot 모놀리스로, 그리고 다시 셋으로 나뉘었다.

맡았을 때의 상태

결제와 가맹점과 정산과 관리자 기능이 한 시스템 안에 함께 있었다. 오래 운영된 시스템이라 기능은 다 돌았는데, 한 영역을 고치면 어디까지 영향이 가는지 예측하기 어려웠다.

데이터베이스를 먼저 열었다. 테이블이 300개였고 컬럼이 100개를 넘는 테이블도 여럿이었다. 무엇이 아직 쓰이는지부터 코드와 저장 이력으로 확인해야 했다.

2024.04  ASP 기반 PG를 맡음, 테이블 300개
2024.05  Spring Boot 모놀리스로 전환 시작, 한 번에 바꾸지 않기로
2024.06  결제 API 응답 30% 개선
2024.07  배포 45분에서 3분으로
2024.08  고객이 먼저 알려주던 장애를 지표와 알람으로
2024.09  개인정보 전체 암호화, 테스트 범위 확정
2024.10  보안 심사 104건
2025.01  모놀리스를 회원·결제·정산 셋으로 분리
2025.02  분리가 만든 정합성 문제
2025.03  실패한 메시지 처리 기준
2025.04  결제와 정산 대사
2025.06  정기 결제 중복 승인
2025.07  세션, 예외 구조, 테스트 격리

모놀리스를 먼저 고른 것

전환을 시작할 때 처음부터 서비스를 나누자는 이야기가 있었다. 나누지 않았다. 도메인 경계가 아직 불분명했고 운영 기준도 정리되지 않은 상태였다.

대신 하나의 애플리케이션 안에서 모듈로 나누고 서로의 테이블을 직접 쓰지 않게 했다. 같은 프로세스 안이라 호출 비용은 작았지만, 나뉜 것처럼 입력과 출력을 명시했다. 이 단계에서는 경계를 자주 바꿀 수 있었다.

물리적으로 나눈 뒤에 잘못된 경계를 고치는 비용은 훨씬 크다. 경계를 시험할 수 있는 구간을 먼저 두고 싶었다.

1년도 안 돼 다시 나눴다

2025년에 회원과 결제와 정산을 서로 다른 서비스로 나눴다. 모놀리스로 간 지 아홉 달쯤이었다.

이걸 두고 처음 판단이 틀렸다고 보지 않는다. 2024년에는 경계를 확인하는 일이 급했고 모놀리스가 그걸 빠르게 해 줬다. 2025년에는 배포 충돌과 장애 영향이 더 큰 문제가 됐고, 그때는 나누는 쪽이 답이었다. 같은 시스템에 다른 답이 나온 것은 시점이 달라서다.

아키텍처 이름을 먼저 고르지 않은 것이 이 기간에 한 판단 중 가장 값이 나갔다. 모놀리스와 MSA를 정답처럼 비교하지 않게 됐다.

flowchart LR
  A["ASP 기반 단일 시스템"] --> B["Spring Boot 모놀리스
모듈 경계만 분리"] B --> C["회원 · 결제 · 정산
세 서비스"]

나누고 나서 늘어난 일

나누자 하나의 트랜잭션으로 덮여 있던 문제가 드러났다. 결제는 승인됐는데 정산 대상이 늦게 생기거나, 같은 이벤트가 두 번 처리되거나, 처리가 끝났는데 결과가 맞는지 확인할 방법이 없었다.

그래서 멱등성과 재처리와 대사를 붙였다. 나누기로 한 이상 피할 수 있는 비용이 아니라 처음부터 같이 설계해야 하는 부분이었다. 이걸 분리 계획에 넣지 않았다면 나누고 나서 한참을 헤맸을 것이다.

손이 많이 간 것과 효과가 컸던 것이 달랐다

시간을 가장 많이 쓴 것은 개인정보 암호화였다. 컬럼을 바꾸는 작업으로 시작했는데 검색과 정렬과 중복 확인과 조인이 한꺼번에 깨졌다. 그 값을 읽는 모든 경로를 다시 확인해야 했다.

반면 효과가 가장 빨리 나온 것은 배포였다. 45분이 3분이 되자 하루에 고칠 수 있는 횟수 자체가 달라졌다. 뒤의 모든 작업이 그 위에서 돌았다.

항목맡았을 때떠날 때
구조단일 시스템회원·결제·정산 세 서비스
배포수동 확인 45분파이프라인 3분
장애 인지고객 문의지표와 알람
정합성 확인없음멱등성·재처리·대사
테스트 범위성공 경로 위주실패·중복·취소·타임아웃 포함

끝내지 못한 것

이 블로그의 글마다 마지막 절이 하나씩 있다. 대부분 그 글에서 다루지 못한 것을 적어 둔 자리인데, 떠나는 시점에 그게 그대로 목록이 됐다.

대사에 걸리는 건수를 줄이는 일          발견 장치는 만들었지만 원인은 덜 줄였다
선점한 채 죽은 건의 회수 시간           되돌리는 처리는 뒀지만 그동안 결제가 늦어진다
생산자가 같은 사건을 두 번 만드는 경우   소비자 멱등성으로는 못 막는다
암호화하며 되살리지 않은 조회 화면       편의를 위해 있던 것들을 접었다
설정 서버 의존성                        지우지 않고 주석으로 막아 뒀다

이걸 남은 사람이 이어서 볼 수 있게 각 글에 적어 뒀다. 코드에 주석으로만 있으면 왜 그렇게 뒀는지가 몇 달 만에 사라진다.

글로 남긴 이유

일하면서 내린 판단은 대부분 코드에 결과만 남는다. 왜 그 선택을 했고 무엇을 포기했는지는 남지 않는다. 나중에 그 코드를 보는 사람은 결과만 보고 이유를 짐작해야 한다.

그래서 판단이 있었던 자리마다 적어 두려고 했다. 모놀리스를 먼저 고른 이유, 나눌 때 결제와 정산부터 고른 이유, 자동 처리와 사람 판단의 경계를 어디에 그었는지 같은 것들이다.

이 기록이 실제로 도움이 됐는지는 확인하지 못했다. 다만 나눈 판단을 아홉 달 뒤에 뒤집으면서도 앞의 판단이 틀렸다고 하지 않을 수 있었던 것은, 그때 무엇을 보고 그렇게 정했는지가 적혀 있었기 때문이다.

다음에도 같은 순서로 할 것 같다

경계가 안 보일 때 먼저 나누지 않는 것, 나누기 전에 확인하는 방법부터 만드는 것, 되돌릴 수 있는 크기로 바꾸는 것. 이 셋은 도메인이 달라져도 그대로 쓸 것 같다.

반대로 이 시스템에서만 통한 것도 있다. 결제는 실패하면 나가지 않으면 그만이라 되돌리기가 비교적 단순했다. 이미 나간 돈을 다루는 흐름이었다면 같은 방식이 통했을지는 모르겠다.

두 번 갈아엎는 동안 가장 오래 걸린 일은 코드를 바꾸는 것이 아니었다. 지금 이 시스템이 무엇을 보장하고 무엇을 보장하지 않는지 문장으로 적는 일이었다. 그게 적혀 있으면 다음 판단이 빨라졌고, 안 적혀 있으면 매번 처음부터 다시 셌다.