Search

“스케줄이 또 안 돌았어요” — 우리가 Temporal을 선택한 이유

URL
생성 일시
2026/02/08 12:24
최종 편집 일시
2026/02/08 12:24
태그
무신사
파일과 미디어
|| “스케줄이 또 안 돌았어요” — 우리가 Temporal을 선택한 이유 어느 날 아침, 슬랙 알림이 울렸습니다. “출고지시 스케줄이 실행 안 된 것 같은데 확인 부탁드려요.” Jenkins 콘솔을 열어보니 job이 멈춰있었고, 모니터링 job마저 함께 멈춰있었습니다. 급하게 수동으로 출고지시를 트리거하고 나서야 물류센터 작업이 시작될 수 있었습니다. 이런 일이 반복되면서 우리는 고민하게 되었습니다. “스케줄 하나 도는 게 왜 이렇게 불안할까?” 출고지시, 그리고 우리가 마주한 문제들 무신사 풀필먼트의 OMS는 주문부터 출고까지 물류 전반을 책임지는 시스템입니다. 그 중에서도 출고지시는 물류센터의 하루를 시작하는 신호와도 같습니다. 정해진 시간에 출고지시가 생성되지 않으면 물류센터 작업이 지연되고, 그것은 곧 배송 지연으로 이어집니다. 초기에는 Jenkins crontab으로 충분했습니다. 정해진 시간에 실행하기만 하면 됐습니다. 하지만 화주사가 늘고 출고 물량이 커지면서, 이 구조의 한계가 하나둘 보이기 시작했습니다. 1. 실패를 놓치는 순간들 Jenkins 기반 스케줄은 job이 멈춰도 알려주지 않았습니다. 그래서 우리는 별도의 모니터링 job을 만들어 실행했습니다. “스케줄이 실행됐는지 확인하는 스케줄”을 또 만드는 거죠. 문제는 이 모니터링 job도 언제든 멈출 수 있다는 것이었습니다. 실제로 Jenkins 자체에 문제가 생기면 스케줄과 모니터링이 함께 멈췄고, 우리는 뒤늦게야 알게 되곤 했습니다. 모니터링을 위한 모니터링을 또 만들 수는 없었고, 이 구조 자체가 근본적인 한계를 가지고 있다는 걸 깨달았습니다. 2. 로그 속에서 원인 찾기 출고지시에 문제가 생기면, 저희는 Jenkins 콘솔 로그를 시작으로 애플리케이션 로그, DB 이력을 차례로 확인해야 했습니다. “이번엔 어디서 실패한 거지?” “입력값은 뭐였지?” “결과는 어떻게 됐지?” 실행 이력을 한눈에 볼 수 있는 방법이 없었고, 문제를 분석하는 것보다 로그를 따라가는 데 더 많은 시간이 들었습니다. 빠른 대응이 필요한 순간일수록, 이런 가시성 부족은 운영 리스크를 키우는 요인이 되었습니다. 3. “다시 눌러주세요” 스케줄이 실패하면 저희가 직접 재실행해야 했습니다. 단순히 일시적인 네트워크 오류로 실패한 경우에도, 원인을 확인하고 수동으로 재실행하는 과정에서 시간이 소요되었습니다. 대응이 조금만 늦어도 출고 SLA에 영향을 주는 경우가 생겼고, 담당자의 부담은 자연스럽게 커질 수밖에 없었습니다. 실제로는 단순 재시도만으로 해결될 수 있는 케이스도 많았지만, 원인을 확인하는 절차 자체가 출고 지연으로 이어지는 경우가 반복되었습니다. 결국 이러한 구조에서는 안정적인 운영을 기대하기 어려워졌습니다. 4. 이벤트 기반으로의 확장 출고 도메인에서는 정해진 스케줄 외에도, 특정 이벤트를 기점으로 Workflow를 실행해야 하는 요구가 늘어나고 있었습니다. 하지만 Jenkins는 cron 기반 실행에 최적화되어 있었고, 이벤트 기반 트리거를 자연스럽게 처리하기에는 구조적인 한계가 있었습니다. 결국 저희는 깨달았습니다. “출고 물량과 요구사항이 늘어날수록, 기존 구조로는 안정적인 운영을 기대하기 어렵다.” 구조 자체를 다시 고민해야 할 시점이었습니다. 대안을 찾아서 저희는 여러가지 가능성을 열어두고 대안을 비교해보기 시작했습니다. Jenkins + 모니터링 Job 개선 이미 운영 중인 구조라 리스크는 적었지만, 근본적인 한계를 해결하기는 어려웠습니다. 모니터링을 아무리 촘촘하게 만들어도, Jenkins 자체의 고가용성 문제는 해결되지 않았습니다. Spring Batch + Quartz Spring Batch + Quartz는 배치 처리에 최적화된 구조였고, 팀에서도 익숙한 스택이었습니다. 하지만 Batch Job 실행 이력은 확인할 수 있어도, “주문이 어디서 멈췄고, 왜 실패했는지”와 같은 비즈니스 흐름은 보이지 않았습니다. 재시도 로직 구현은 가능했지만, “이 조건이면 재시도, 저 조건이면 스킵” 같은 의사결정 로직이 코드 곳곳에