서버 판매를 자동화하는 SaaS 운영 콘솔 구축기 소개 영상
문제
HGNET는 오랫동안 고객의 서버 세팅을 프리랜서 방식으로 맡아 왔습니다. 주문이 들어오면 AWS 콘솔에 들어가 EC2 인스턴스를 띄우고 보안 그룹을 열고 SSH 키를 만들어 전달하고 매달 유지보수비를 따로 청구하는 일을 사람이 손으로 반복했습니다. 건당으로 보면 익숙한 작업이지만 건수가 늘수록 같은 절차를 매번 되풀이하는 소모가 커졌고 결제와 서버 생성이 사람의 손을 거치며 어긋날 여지도 남았습니다.
그래서 이 반복을 자체 브랜드 SaaS로 바꾸기로 했습니다. 고객이 서버 스펙과 유지보수 플랜을 직접 고르고 결제하면 운영자가 개입하지 않아도 EC2 서버가 자동으로 생성되어 전달되는 구조입니다. 원가에 가깝게 팔던 수작업 납품을 무인으로 도는 상품으로 전환해 손이 아니라 시스템이 서버를 파는 형태로 만드는 것이 목표였습니다.
문제는 이 전환이 화면 몇 개로 끝나지 않는다는 데 있었습니다. 결제와 서버 생성은 성격이 완전히 다른 두 작업입니다. 결제는 몇 초 안에 끝나지만 EC2 인스턴스는 실행 상태가 되기까지 1분을 훌쩍 넘깁니다. 매달 돌아오는 정기결제는 사람이 지켜보지 않아도 스스로 청구되고 실패를 처리해야 하며 결제가 끊긴 서버는 언젠가 정리되어야 합니다. 돈과 인프라가 얽힌 이 흐름을 어긋남 없이 자동으로 잇는 것이 이 프로젝트의 핵심 과제였습니다.
접근
기능
결제와 서버 생성을 떼어 놓은 비동기 구조
결제 요청은 몇 초 만에 끝나지만 EC2 인스턴스가 실행 상태에 도달하기까지는 1분에서 2분이 걸립니다. 이 긴 작업을 결제 응답 안에서 처리하면 요청이 타임아웃됩니다. 그래서 결제가 끝나면 구독만 먼저 만들고 서버 생성은 작업 큐에 넣어 백그라운드 워커가 이어받게 했습니다. 화면은 즉시 응답하고 무거운 프로비저닝은 뒤에서 따로 돕니다.
서버 생성은 한 번의 API 호출이 아니라 여러 단계의 연쇄입니다. 최신 Amazon Linux 이미지를 찾고 키페어를 만들고 보안 그룹을 열고 인스턴스를 띄운 뒤 실행 상태가 될 때까지 기다렸다가 고정 IP를 붙입니다. 이 중 한 단계라도 실패하면 앞서 만든 AWS 리소스가 그대로 남아 요금만 새어 나갑니다. 그래서 워커는 모든 재시도가 소진된 최종 실패에서 만들어 둔 리소스를 되짚어 정리하고 서버 기록을 실패로 표시합니다.
기능
스케줄러가 없는 결제사로 무인 정기결제 만들기
토스페이먼츠는 매달 알아서 청구해 주는 스케줄링 기능을 제공하지 않습니다. 청구 시점은 우리가 직접 관리해야 했습니다. 반복 작업 스케줄러가 매일 정해진 시각에 만기가 된 구독을 찾아 결제 작업을 만들고 각 작업은 그달에 한 번만 실행되도록 식별자로 중복을 막습니다. 결제가 성공하면 인보이스와 다음 청구 기간이 한 트랜잭션으로 갱신됩니다.
기능
구독의 일생을 상태 머신으로 관리
구독은 활성, 연체, 정지, 해지 예정, 종료의 다섯 상태를 오갑니다. 어떤 상태에서 어떤 상태로 갈 수 있는지를 코드로 못 박아 잘못된 전이를 아예 막았습니다. 결제가 밀린 구독은 정해진 유예 기간이 지나면 정지되고 더 오래 방치되면 서버까지 정리되며 종료됩니다. 이 판단을 매일 도는 생명주기 워커가 대신합니다.
기능
고객 서버 열쇠를 평문으로 두지 않기
서버에 접속하는 SSH 개인 키는 그 자체가 서버의 열쇠입니다. AWS가 키를 만들어 돌려주는 순간 암호화해서 저장하고 데이터베이스에는 평문을 절대 남기지 않습니다. 고객에게 전달할 때만 복호화하며 키를 내려받았는지도 기록해 관리합니다.
전체 구조는 다음과 같습니다.
글로 풀면 이렇습니다. 고객이 결제하면 토스에서 빌링키를 발급받아 첫 결제를 올리고 구독과 인보이스가 콘솔 DB에 원자적으로 저장됩니다. 서버 생성은 프로비저닝 큐로 넘어가 워커가 AWS EC2 리소스를 만들고 실행 상태를 확인한 뒤 고정 IP를 붙입니다. 여기에 정기결제 워커와 생명주기 워커가 매일 정해진 시각에 DB를 읽어 청구와 정지, 종료를 무인으로 처리합니다.
결과
이 콘솔은 아직 구축 단계이고 정식으로 문을 열지는 않았습니다. 다만 손으로 하던 서버 납품을 무인 파이프라인으로 옮기는 뼈대는 자리를 잡았습니다. 고객이 스펙과 플랜을 고르고 결제하면 구독이 만들어지고 백그라운드 워커가 EC2 리소스를 생성해 서버를 전달하며 매달 정기결제와 생명주기 정리가 사람 없이 도는 구조입니다.
무엇보다 결제와 인프라를 각각 원자적인 트랜잭션과 격리된 워커로 나눈 덕분에 한쪽이 실패해도 다른 쪽을 오염시키지 않고 되돌리거나 재시도할 수 있게 설계했습니다. 자체 서비스라 기술적 판단을 숨길 이유가 없어 구축 과정에서 내린 결정들을 아래 글에 그대로 풀었습니다.
- EC2 자동 프로비저닝을 백그라운드 워커로: 실패 시 리소스를 되짚어 정리하는 시퀀스
- 스케줄러 없는 결제사로 정기결제 만들기: 직접 크론과 멱등 청구 설계
- 구독 생명주기 상태 머신: 다섯 상태와 전이 규칙, 생명주기 워커
- 고객 SSH 키를 안전하게 저장하기: AES-256-GCM 저장과 1회 전달
- 서버 SDK 없는 토스 정기결제 연동: fetch와 빌링키, 웹훅 처리
- 1인 SaaS 스택 선정기: Prisma 7, BullMQ, Auth.js v5를 고른 이유
자주 묻는 질문
결제가 되자마자 서버가 바로 뜨나요?
결제와 서버 생성은 분리되어 있습니다. 결제가 끝나면 구독이 먼저 만들어지고 서버 생성은 백그라운드 작업으로 넘어갑니다. EC2 인스턴스가 실행 상태에 도달하기까지 1분에서 2분이 걸리기 때문에 이 시간을 결제 화면이 기다리게 하지 않고 준비가 끝나면 별도로 안내하는 방식으로 설계했습니다.
정기결제는 어떻게 자동으로 도나요?
토스페이먼츠는 정기 청구를 대신 스케줄링해 주지 않아 청구 시점을 직접 관리합니다. 매일 정해진 시각에 도는 워커가 만기가 된 구독을 찾아 결제하고 성공하면 다음 청구 기간을, 실패하면 연체 상태와 안내 메일을 남깁니다. 같은 달에 같은 구독이 두 번 청구되지 않도록 작업에 식별자를 붙여 중복을 막았습니다.
아직 구축 중이라고 했는데 어느 정도까지 만들어졌나요?
결제, 자동 프로비저닝, 정기결제, 구독 생명주기, 키 암호화 같은 핵심 흐름의 뼈대가 구현된 단계입니다. 정식 오픈과 실제 트래픽 운영은 아직이므로 이 글에서는 성과 수치 대신 어떤 문제를 어떤 설계로 풀었는지를 중심으로 정리했습니다.