티스토리 뷰
서버 운영 리스크를 낮추고 비즈니스 신뢰도를 높이기 위해 노력한 한 달
정산 서버의 DB 프로시저 전환 작업이 진행 중이었다. 새로운 계기로 비즈니스 기여에 대해 더욱 고민해보기로 결정하였다.
이 과정에서의 노력들이 개인적으로 의미가 깊었기에 기록해두고자 글을 작성한다.
1. 당시 업무 상황과 고민한 방향성
깊은 고민의 엔지니어링에서 희열을 느끼던 나로서는 개인적으로 새로운 변화와 챌린지였다.
최대 1000줄 가량의 총 8개의 DB 프로시저 전환 과제 중, 비즈니스 영향도가 가장 높은 3개 프로시저를 우선 대상으로 삼아 작업이 진행되고 있었고 그 중 2개는 구현이 거의 완료되어 테스트 단계, 나머지 1개는 아직 착수하지 않은 상태였다. 이 3개 프로시저 마무리까지를 내 개인적 목표를 갖고 비즈니스적 고민을 해보고자 했다.
남은 1개 프로시저를 새로 설계·구현하고, 기존 2개를 포함해 우선 목표로 설정된 3개 프로시저 전환 작업을 라이브 서버까지 반영하는 역할을 해야했다.
단순히 진행 중이던 작업을 마무리하는 것이 아니라, 왜 이 구조로 전환하고 있는지, 그리고 이 선택이 정산 마감과 운영 상황에서도 유효한지를 다시 점검하는 데 집중하고자 했다.
2. 문제 정의와 구체 목표 설정
기존 정산 로직은 최대 약 750줄 규모의 프로시저 3개로 구성되어 있었고, 각 프로시저 내부에서 최대 20회 가량 서로 다른 DBMS 간 DB to DB 통신이 이루어지고 있었다.
이 구조의 가장 큰 문제는 복잡성 자체보다 실패가 발생했을 때 대응 가능한 선택지가 거의 없다는 점이었다.
에러가 자주 발생하는 구조는 아니었지만, 한 번 문제가 발생하면 재시도를 위해 평균 3시간 30분 이상을 기다려야 했고 사실상 재실행 외에는 뚜렷한 대응 방법이 없었다.
특히 이러한 문제가 발생한다면, 다음 항목을 예측하거나 설명하기 어려웠다.
- 현재 어떤 단계에서 실패했는지
- 다시 시도할 수 있는 시점이 언제인지
- 정산 마감 일정에 영향을 주는지
이는 단순한 구현상의 불편함이 아니라, 업무 전반의 신뢰도를 떨어뜨릴 수 있는 운영 리스크라고 판단했다.
3. 설계 방향성 재점검
구조를 그대로 옮기는 방식은 문제를 해결하지 못한다고 판단했다.
지금의 전환 방향이 정산 운영 관점에서도 적절한지부터 다시 점검했다.
- 로직을 서버 코드로 이전하여 테스트와 디버깅이 가능한 구조로 전환
- 쿼리를 기능 단위로 분리·통합하여 책임을 명확히 함
- 장애 상황 시 신속히 원인 파악 가능하도록 재구성
특히 아직 착수하지 않았던 1개 프로시저는 기존 문제를 반영해 로직 흐름과 책임을 처음부터 다시 설계했다.
이를 통해 정산 로직이 한 번에 성공해야 하는 블랙박스가 아니라 단계별로 상태를 파악할 수 있는 구조가 되도록 하는 것을 목표로 했다.
4. DB to DB 통신 제거와 분산 트랜잭션 판단
기존 DB to DB 통신을 서버 간 API 호출로 전환하면서 다음과 같은 흐름을 반복하는 구조가 되었다.
- begin transaction → DB 작업 → 데이터 저장 API 호출 → DB에 처리 상태 갱신 → commit
API로 인해 서로 다른 트랜잭션에서 수행되는 DB 작업 때문에 분산 트랜잭션 이슈를 어떻게 다룰지에 대한 판단이 필요했다. 이 과정에서 다음과 같은 선택지를 검토했으나 선택하지 않았다.
- 아웃박스 패턴
- 처리 상태 갱신 로직을 별도 API로 분리
결과적으로 현재 환경에서는 try-catch 기반으로 실패 시 롤백 API를 호출하는 방식을 선택했고, 장애 발생 시 즉시 인지할 수 있도록 에러 메일을 추가했다.
완벽한 해법이라기보다, 정산 배치 특성과 팀의 운영 맥락에서 가장 단순하고 통제 가능한 선택이라고 판단했다.
5. 구현과 성능 검증
설계 이후 서버 로직을 구현하며 쿼리 구조를 재정리했고, 로직 구조와 쿼리에 대해 리뷰를 받아 보완했다.
3개 프로시저 전환 작업을 모두 완료한 뒤, 라이브 서버에 반영된 상태에서 요구 처리량을 기준으로 성능을 측정했다.
그 결과 기존 3시간 32분 가량 소요되던 로직이 평균 1분 13초에 처리가 가능했고, 기존 프로시저 기반 구조 대비 약 174배의 성능 개선을 확인했다.
테스트 환경이 아닌 실제 운영 환경에서 측정된 결과였기 때문에, 정산 배치 운영 시 처리 시간에 대한 예측 가능성을 높이는 데 의미가 있었다.
성능 개선의 핵심은 복잡한 프로시저 내부 흐름을 단순화하고 일부 쿼리 개선과 불필요한 DB 간 통신을 제거한 구조적 변화였다.
6. 운영 관점 확장과 가시성 개선
구현 이후에는 “배포된 뒤, 개발자와 정산 담당자가 어떤 정보를 기준으로 상황을 판단할 수 있을까”를 기준으로 추가 작업을 진행했다.
API Latency 가시화
기존 API 로그에는 소요 시간이 기록되지 않아 병목 지점을 파악하기 어려웠다.
각 API에 duration(ms) 로그를 추가하고, Kibana에서 직접 Latency 대시보드를 구성했다.
설정 방법과 활용 방식을 매뉴얼로 정리해 공유했고, 평균 10초 이상 소요되는 API 목록을 팀 내에서 확인할 수 있는 환경을 만들었다.
API Latency 확인이 가능해졌기에 앞으로 서버 병목을 파악하고 개선 대상을 파악하는데 도움이 될 것이라 기대한다.
에러 메일 재분류
전체 에러 메일 중 약 25.3%를 차지하지만 실질적으로 대응이 필요하지 않은 예외들이 있었다.
해당 케이스를 재분류하여 불필요한 메일 발송을 줄였고, 정산 운영 중 중요한 신호가 묻히지 않도록 개선했다.
스케줄러 및 배포 안정성
배포 시점에 예기치 않은 중단이 발생하지 않도록, 운영 리스크를 사전에 줄이는 데 집중했다.
- 스프링 스케줄러 기본 스레드풀 설정으로 인해 작업이 밀릴 수 있음을 파악했다.
- 스케줄러 스레드풀 설정법과 비동기 방식 등 여러 가능한 해결 방법과 함께 정리해 팀 내에 공유했다.
- 서버 재배포 시 실행 중이던 스케줄러·API 스레드가 강제 종료되는 문제를 확인하였다.
- 단순히 스프링부트에 Graceful Shutdown 설정만 하지 않고 배포 스크립트의 systemctl restart 로 인한 SIGTERM 타임아웃 설정 값을 확인해 문제 없음을 검증한 후 설정해 장애 반복을 방지했다.
7. 공유와 커뮤니케이션
작업 결과는 컨플루언스 문서로 정리해 공유했고, 팀 주간 회의에서 데이터 기반으로 설명하고자 노력했다.
단순히 결과만 전달하기보다, 정산 운영 관점에서 왜 이런 선택을 했는지에 대한 문맥을 남기는 것을 중요하게 생각했다.
8. 팀 문화에 대한 작은 시도
팀의 코드 리뷰 문화가 더 건설적으로 발전할 수 있는 여지가 있다고 느꼈다.
점심 시간에 남은 팀 예산에 대한 이야기가 나오던 중, 코드 리뷰에 대한 공통된 기준을 맞추는 데 도움이 될 것이라 생각해 “Looks Good To Me” 라는 코드 리뷰 관련 서적을 구매하자는 의견을 제안했고, 팀에서 이를 반영해주었다.
강요가 아닌 팀 맥락에 맞는 제안을 통해, 작은 개선을 시도하고자 했다.
9. 회고
한 달간의 작업을 통해 이전된 정산 로직은 “한 번에 성공해야 하는 작업”이 아니라, 상황을 설명하고 판단할 수 있는 구조로 한 단계 개선되었다고 생각한다.
앞으로도 기능 구현마다 실패했을 때 어떤 정보를 제공할 수 있는지, 운영 중 어떤 기준으로 의사결정을 도울 수 있는지를 함께 고민하는 개발자가 되고자 한다.
가장 중요한건 내가 속한 조직의 핵심 가치와 방향성을 인지하고 그에 맞게 상황에 맞는 업무를 할 줄 아는 개발자가 되는 것이라고 느꼈다.
개발만 잘하는 개발자가 아니라 일을 잘하는 개발자가 되는 방향에 대한 조그마한 힌트를 얻은 기분이다.