
프로그램 유지보수 비용, 다 만든 뒤 매달 무엇에 돈이 드나
9월 3일, 보험대리점(GA) 지사의 계약관리 시스템에 개인정보 회귀 검사를 넣고 커밋했습니다. 테스트 도구 한 줄이 같이 들어갔고, 제 컴퓨터의 설치 도구로는 문제가 없었습니다.
그날부터 이 시스템은 한 번도 배포에 성공하지 못했습니다. 그리고 아무도 몰랐습니다.
견적서의 유지보수 줄이 사는 것은 이런 고장을 먼저 찾아내고 고치는 시간입니다. 이 일이 어떻게 묻혔다가 드러났는지부터 옮기겠습니다.
아무도 몰랐던 배포 실패
다음 날 배포를 열어 보고서야 순서가 보였습니다. 시간 순으로 옮기면 이렇습니다.
| 시점 | 일어난 일 |
|---|---|
| 9월 3일 | 테스트 도구가 추가된 커밋 |
| 9월 3일 밤 | 서버 디스크 복구 재개, 복구 중엔 배포 관문이 배포를 막음 |
| 9월 4일 09:25 | 디스크 복구 완주, 미뤄 둔 배포 시작 |
| 1차 배포 | 패키지 설치 단계에서 실패 |
| 2차 배포 | 같은 자리에서 같은 에러 |
| 같은 날 | 빌드 단계에 npm 버전 한 줄 고정, 배포 통과 |
실패가 묻힌 이유는 단순합니다. 디스크 복구가 도는 동안 관문이 배포를 막았고, 그래서 누구도 배포를 시도하지 않았습니다.
첫 실패에서 저는 방금 올린 버전 번호 두 줄을 의심했습니다. 고쳐 올린 2차 배포도 같은 에러로 멈췄습니다.
전체 로그를 뽑고서야 원인이 보였습니다. 제 컴퓨터의 npm 11이 만든 잠금 파일(설치할 부품 목록을 고정한 파일)을 서버 빌드 이미지의 npm 10이 다르게 읽고 있었습니다.
고친 것은 빌드 단계에 npm 버전을 고정한 한 줄입니다. 운영 중 실행 환경은 건드리지 않았습니다.
기간은 하루 남짓이었습니다. 디스크 복구가 더 길었다면 이 고장은 더 급한 날 처음 드러났을 겁니다.
그 뒤로 이 시스템에는 배포 전에 잠금 파일을 서버와 같은 버전으로 미리 읽혀 보는 검사를 넣었습니다.
기능 바깥에서 드는 비용
만든 뒤에 돈이 드는 곳은 대개 화면 기능이 아닌 주변 조건에서 생깁니다.
제가 운영하는 시스템에서 실제로 생긴 일을 옮기면 이렇습니다.
| 시스템 바깥의 조건 | 실제로 생긴 일 |
|---|---|
| 개발 PC와 서버의 빌드 도구 차이 | 부품 하나를 추가하자 서버만 잠금 파일을 다르게 읽어 배포 중단 |
| 실행 런타임 지원 종료 | 종료 전에 서버를 깨우는 함수의 Node 20을 22로 미리 올림 |
| 클라우드 과금 조건 | 무료 크레딧이 9월에 소진돼 10월부터 실제 과금 |
이 표의 어느 줄도 고객이 요청한 기능이 아닙니다. 그래도 누군가 챙기지 않으면 화면은 멀쩡한 채로 배포나 청구에서 일이 생깁니다.
백업 복원 리허설
급식 납품업체 본사와 지점 10곳이 쓰는 매출 대시보드가 있습니다. 8월에 점검해 보니 문서에는 "매월 1일 자동 백업"이라고 적혀 있었습니다.
서버의 예약 작업 목록에는 백업 항목 자체가 없었습니다. 마지막 백업은 3개월 전이었습니다.
그래서 매일 백업으로 다시 짜고 무응답 감시를 붙였습니다. 백업이 끝나면 다른 장비에 신호를 남기고, 그 장비가 6시간마다 신호의 나이를 봅니다.
30시간 넘게 새 신호가 없으면 경보가 옵니다. 붙인 날 경보가 실제로 울리는지까지 시험했습니다.
이 감시는 곧 값을 했습니다. 8월 하순에 백업이 매일 실패했는데, 예약 작업 기록은 계속 "성공"이었습니다.
작업을 띄우는 실행기가 결과와 상관없이 0을 돌려줬기 때문입니다. 35시간째에 공백을 잡은 것은 무응답 감시였습니다.
원인을 찾아 같은 날 고치고 빠진 백업을 채웠습니다.
복원 리허설도 따로 했습니다. 운영 서버는 건드리지 않고, 임시 데이터베이스에 백업을 풀어 운영 쪽과 건수를 대조했습니다.
여기서 백업을 받아 두는 PC에 보관한 암호 해제 키가 열리지 않는다는 사실이 나왔습니다. 백업 파일은 있는데 최악의 날 열쇠가 맞지 않는 상태였습니다.
같은 날 키를 다시 만들고 실제로 풀리는지 확인했습니다.
반대로 범위를 줄인 것도 있습니다. 처음엔 거의 실시간에 가까운 재해복구까지 계획했습니다.
다시 보니 이 대시보드는 ERP가 원본이고 하루 한 번 데이터를 가져오는 구조였습니다. 데이터베이스를 통째로 잃어도 다시 가져오면 복구됩니다.
그래서 복원 리허설과 재적재 절차서로 계획을 줄였습니다.
백업에 드는 돈은 데이터가 어디서 오느냐로 정해집니다. 원본이 따로 있으면 다시 가져오면 되고, 사람이 직접 입력하는 시스템은 더 촘촘한 백업이 필요합니다.
감시는 몇 시에 켜져 있나
이 대시보드의 클라우드 서버는 비용을 줄이려고 밤에 꺼집니다. 업타임 감시도 그에 맞춰 평일 07~19시에만 돕니다.
밤에 뜨는 "시스템 작업 진행 중" 화면은 서버가 꺼져 있다는 표시입니다. 고객 업무가 낮에 몰려 있어서 고른 조합입니다.
대신 꺼진 시간에 생긴 문제는 아침에야 압니다.
꺼 두는 설정도 저절로 어긋납니다. 10월에 비용을 나눠 보다가 매일 0시에 서버가 켜지고 있는 것을 찾았습니다.
백업 작업의 "오늘 백업이 있으면 건너뛴다" 조건이 날짜 기준이었습니다. 0시 실행은 매번 새 날로 보고 서버를 깨웠고, 한 달에 약 30시간을 헛가동하고 있었습니다.
지금은 새벽에 서버가 꺼져 있고 어제 백업이 정상이면 깨우지 않습니다. 메모리 실사용이 약 480MB인 것을 보고, 한 단계 작은 서버로 바꾸는 작업도 업무 시간을 피해 예약해 두었습니다.
서버를 꺼 둬도 공인 IP 주소에는 요금이 붙습니다. 무료 크레딧도 9월에 끝나서 10월부터는 실제 요금이 청구됩니다.
이 밖에 매일 데이터 정합성 리포트가, 매주 성장 지표가 자동으로 옵니다. 숫자가 틀어졌을 때 고객보다 먼저 보려는 장치입니다.
계약서에서 먼저 읽을 줄
같은 대시보드의 유지보수 계약은 이런 틀이었습니다. 개발 직후 무상 기간이 있고, 끝난 다음 날 유상으로 넘어갑니다.
유상은 최소 6개월이고, 이후 6개월씩 자동으로 갱신됩니다. 끝내려면 만료 30일 전까지 알려야 합니다.
통지 방법은 서면만 인정됩니다. 내용증명이나 이메일이어야 하고, 카톡이나 문자는 효력이 없습니다.
이 조항은 양쪽 모두를 지켜 줍니다. 담당자가 바뀌어도 언제 무엇을 보내야 하는지가 종이에 남습니다.
대신 날짜를 놓치면 6개월이 그대로 다시 붙습니다.
금액은 유지보수비와 서버비가 따로 적혀 있었습니다. 유지보수비는 사람의 시간이고, 서버비는 클라우드 요금입니다.
둘이 한 줄로 뭉쳐 있으면 서버를 줄였을 때 무엇이 싸지는지 보이지 않습니다. 앞에서 말한 헛가동을 고쳐도 고객 쪽에서 그 차이를 확인할 방법이 없어집니다.
추가 기능은 별첨 합의서로 따로 정했습니다. 새 기능이 유지보수 범위에 들어가는지는 견적 단계에서 갈라 두는 편이 서로 편합니다.
이런 관제 화면이 어떻게 생겼는지는 가상 데이터로 옮긴 시연 대시보드에서 볼 수 있습니다. 견적서의 유지보수 줄은 그 화면 뒤에 붙는 백업과 감시를 가리킵니다.
계약이 끝난 다음 날
이 계약의 1차 유상 기간은 9월 30일까지였고, 갱신 없이 끝났습니다. 10월 6일에 고객 대화방에 종료를 다시 알렸습니다.
추가 유지보수가 필요하면 별도 계약이 필요하다는 내용이었습니다. 그날 바로 문의가 하나 들어왔습니다.
이번 달 전월 대비 차액이 이상하다는 내용이었습니다. 확인해 보니 버그는 없었습니다.
"전월 동기간"을 어떻게 잡느냐의 차이였고, 빠진 날의 매출만큼 원 단위로 맞았습니다. 그래서 설명만 드리고 코드는 고치지 않았습니다.
같은 날 화면에서 서버 운영비 메뉴도 내렸습니다.
이 선이 처음부터 문서에 있으면 종료 다음 날이 덜 어색합니다. 설명은 해 드리고 수정은 계약으로 받는다는 기준을 미리 적어 두세요.
매달 드는 돈을 줄이는 선택지
모든 업무에 서버가 필요하지는 않습니다. 몇 명이 쓰는지, 여러 곳에서 동시에 같은 숫자를 봐야 하는지에 따라 답이 갈립니다.
한 사람이 쓰는 장부라면 파일로 받아 바로 쓰는 엑셀 완제품이 맞을 수 있습니다. 서버가 없으니 서버비 줄도 생기지 않습니다.
본사와 지점이 같은 숫자를 봐야 한다면 서버와 감시가 필요하고, 그만큼 매달 비용이 생깁니다. 이때는 업무 시간에 맞춰 서버를 끄는 식으로 서버비를 낮출 수 있습니다.
어느 쪽이 맞을지 애매하면 지금 쓰는 엑셀과 사용 인원을 들고 대시보드 상담에 적어 보세요. 유지보수 줄이 필요한 구조인지가 거기서 먼저 갈립니다.
유지보수 견적서 체크리스트
- 월 금액에 서버비가 들어 있는지, 별도인지 확인하세요. 별도라면 서버를 줄이거나 밤에 끌 때 어느 줄이 내려가는지도 물어보세요.
- 최소 계약 기간과 자동 갱신 단위를 보세요. 6개월 자동 갱신에 30일 전 통지라면, 계약하는 날 통지 마감일부터 달력에 적어 두세요.
- 종료 통지를 어떤 방식으로 해야 효력이 있는지 확인하세요. 메신저로 끝을 알렸다고 생각했는데 서면 조항이 있으면 계약은 그대로 이어집니다.
- 백업이 복원되는지를 누가, 언제 확인하는지 물어보세요. 복원 리허설 기록이 있는지, 암호 키가 어디에 있는지까지 보세요.
- 백업이 멈췄을 때 누가 어떻게 알게 되는지 확인하세요. 예약 작업의 "성공" 표시만 보는 구조라면 실패가 며칠씩 묻힐 수 있습니다.
- 장애 감시가 몇 시부터 몇 시까지, 얼마 간격으로 도는지 적어 달라고 하세요. 업무 시간 감시라면 밤사이 장애는 아침에 알게 됩니다.
- 빌드 도구와 런타임 버전 관리, 지원 종료 대응이 범위에 있는지 물어보세요. 개발 PC에서 통과한 변경도 서버의 도구 버전이 다르면 배포가 깨질 수 있습니다.
- 기능 추가가 유지보수에 들어가는지, 별도 합의인지 갈라 두세요.
- 계약이 끝난 뒤 요청을 어떻게 받는지 정해 두세요. 설명까지는 해 주는지, 수정은 새 계약인지 문서에 적어 두세요.
이어서 읽기

사내 시스템이 갑자기 느려졌을 때, 문의 화면 폴링 한 줄이 범인이었던 사례
로그인에 8~21초가 걸리던 사내 그룹웨어를 진단한 기록입니다. 서버 사양 탓으로 보이던 느림 뒤에는 2.5초마다 도는 쓰기 한 줄과 디스크 점검의 겹침이 있었습니다. 두 번 틀린 측정과 그걸 바로잡은 진단 순서까지 담았습니다.

서버 이중화했는데 장애 때 안 넘어가는 이유와 점검법
복제 로그는 매번 OK였는데 대기 서버의 앱은 켜지지도 못하고 있었습니다. 사내 그룹웨어 이중화를 다시 뜯어보며 찾은 고장 지점과 고친 구조, 손실 범위를 1분으로 줄인 과정, 대기 서버가 정말 뜨는지 확인하는 4층 점검표를 적었습니다.

배포가 느릴 때 추측 말고 단계별로 재야 하는 이유
사내 그룹웨어 배포가 대략 9분(추정) 걸려 서버의 앱 빌드 222초를 8초로 줄였지만 배포는 여전히 5분을 넘겼습니다. 단계마다 시간을 찍어 진짜 병목을 찾고 540초를 332초로 줄인 과정과 일부러 남긴 단계의 기준을 담았습니다.