기존에 없던 AI 기반 서비스를 추가하면서,
토큰 관리가 생각보다 훨씬 복잡한 영역이라는 걸 체감하게 됐다.
특히 월간·연간 정기 결제와 일회성 결제가 함께 존재하다 보니
“배치(Job) 한 번 돌리면 되겠지”라는 단순한 접근으로는 해결되지 않았다.
여기에 향후 사용자 트래픽까지 고려해야 했지만,
소규모 팀에서 중장기적인 계획을 충분히 세우기보다는 빠른 출시가 우선되는 상황이었다.
혼자 설계를 진행하다 보니 미숙한 선택도 있었고,
그 과정에서 여러 시행착오를 겪게 됐다.
이 글에서는 실제 서비스 운영 과정에서 겪은 문제 상황을 바탕으로,
하루 1회 배치 구조에서 토큰을 어떻게 관리했는지, 그리고 그 과정에서 고민하게 된 더 효율적인 대안까지 정리해 본다.
토큰 관리를 추가하며 생긴 문제
구독형 서비스에서 발생하는 문제는 다음과 같다.
갱신일 당일에 토큰이 갑자기 0이 되는 문제
배치 시간 때문에 날짜 경계에서 오류가 발생하는 문제
만료된 사용자가 계속 배치 대상이 되는 문제
이런 문제는 대부분 토큰을 언제, 어디서 계산하느냐에서 시작된다.
토큰 관리하던 방식
당시에는 일정과 리소스 제약으로 인해 가장 직관적인 방식으로 설계를 진행했고,
하루 1회 배치로 토큰 상태를 관리하는 구조를 선택했다.
구조
- 하루 1회 배치 실행
- 배치에서 토큰 지급 또는 초기화
- 다음 갱신일 계산
이 방식은 구현 자체는 단순했지만,
운영 관점에서는 몇 가지 한계를 빠르게 드러냈다.
단점
- 매일 많은 UPDATE 발생
- 배치 로직이 복잡해짐
- 날짜 경계 처리 실수 시 하루 전체 장애로 이어질 수 있음
특히 사용자 수가 늘어날수록 이 구조를 그대로 유지하기 어렵겠다는 판단이 들었다.
그래서 고민하게 된 대안이, 토큰의 총량과 사용량을 분리해 관리하는 방식이다.
토큰의 총량과 사용량을 분리해 관리하는 방식
토큰의 총량과 사용량을 분리해 관리하는 방식의 핵심은, 토큰을 현재 값이 아니라 계산 결과로 다룬다는 점이다.
기존 방식에서는 토큰을 곧바로 현재 남아 있는 값으로 관리했다.
반면 신규 방식에서는 역할을 다음과 같이 분리한다.
토큰 총량: 이번 주기에 제공된 전체 토큰
토큰 사용량: 실제로 사용한 토큰
남은 토큰: 총량 − 사용량 (계산값)
이 구조에서는 배치가 직접 토큰 값을 계산하거나 차감하지 않는다.
배치는 오직 상태 전환만 담당하고, 실제 토큰 차감은 사용 시점에 처리한다.
결제 기간이 종료되면 어떻게 처리할까?
토큰 관리에서 결제 기간 종료는 토큰 값의 문제가 아니라 사용 권한의 문제에 가깝다.
따라서 결제 기간이 종료된 경우에도
배치에서 토큰을 직접 0으로 초기화하기보다는,
해당 구독을 만료 상태로 전환하는 방식이 더 명확하다.
실제 토큰 사용 가능 여부는
실시간 요청 시 구독 상태를 기준으로 판단한다.
변경된 배치의 역할
이로 인해 배치의 역할이 크게 단순해진다.
신규 구조에서 배치는 다음 두 가지만 판단하면 된다.
구독이 만료되었는가?
오늘이 갱신일인가?
배치 단계에서는 토큰을 얼마로 세팅할까? 0으로 초기화해야 할까? 같은 고민을 더 이상 하지 않는다.
그 결과 배치 UPDATE 대상이 크게 줄어들고, 날짜 경계로 인한 버그 위험도 함께 감소한다.
실시간 처리도 단순해진다
실시간 처리 로직 역시 단순해진다.
토큰 사용 시에는
아직 구독 기간 내인가?
사용량이 총량을 초과하지 않았는가?
이 두 가지만 확인하면 된다.
이 구조는 동시성 처리에 강하고, 사용 이력 추적이 쉬우며, 요금제 확장(추가 토큰, 이월 정책 등)에도 유리하다.
개인적인 결론
토큰 관리에서 가장 위험한 건 로직이 틀린 것보다 기준이 모호한 상태로 운영되는 것이다.
- 갱신일은 포함인가, 제외인가?
- 배치 시간은 언제인가?
- 토큰은 값인가, 계산 결과인가?
이 기준만 명확하다면 어떤 구조를 선택하든 안정적인 운영이 가능하다.
마치며
이 글은 특정 기술이나 프레임워크를 설명하기보다
실제 서비스 설계에서 겪게 되는 고민과 선택지를 정리한 글이다.
토큰 관리, 구독 관리, 배치 설계를 고민 중이라면
자신의 서비스 규모에 맞는 방식을 선택하는 데
조금이나마 도움이 되길 바란다.
✍️ 참고
이 글은 실무 경험을 바탕으로 한 일반적인 설계 논의이며,
실제 구현 방식은 서비스 환경에 따라 달라질 수 있다.
'Programmer > JAVA' 카테고리의 다른 글
| 업로드된 데이터의 카운트(count) 성능 문제, 왜 구조를 바꾸게 되었을까? (0) | 2026.02.09 |
|---|---|
| 다시 Selenium SessionNotCreatedException 발생으로 인한 다운로드 경로 정리(ChromeDriver 140 ~ ChromeDriver 147) (0) | 2026.02.06 |
| Java Gemini API 파일 업로드가 실패하는 이유: state ACTIVE 기다려야 하는 이유 (0) | 2026.01.06 |
| Chrome 143 업데이트 후 Selenium SessionNotCreatedException 해결 방법 (0) | 2026.01.02 |
| Java Selenium 파일 업로드 자동화 예제 (0) | 2025.12.30 |