안녕하세요.
|
개발자 최민준입니다.

반복되는 운영 동선을 LLM에게 위임하다 — ZzolBot 도입기

"A4BX 방 멈췄어요." 이 다섯 글자가 올라오면 개발자는 하던 작업을 멈추고 그라파나를 열었다. joinCode로 검색하고, Tempo에서 마지막 요청 흐름을 따라가고, Redis Stream lag을 확인했다. 한 번에 10분, 하루에 몇 번씩 코드를 짜다 멈추…

지연 시간 0ms를 보장하는 Transactional Outbox 도입기

DB 커밋은 됐는데 이벤트가 사라진다 ZZOL은 실시간 멀티플레이어 게임 서비스다. 방 생성, 룰렛 스핀, 미니게임 결과 같은 핵심 비즈니스 이벤트를 Redis Stream으로 브로드캐스트해서 WebSocket 클라이언트에게 전달하는 구조로 되어 있다. 문제는 에 있…

비동기 컨텍스트 전파의 두 단계: 외부 브로커 경유와 내부 스레드풀 전파 분리하기

ZZOL의 모든 유저 액션은 Redis Stream을 경유한다. WebSocket으로 들어온 요청이 Redis Stream에 발행되고, 별도 스레드풀에서 소비되어 게임 로직이 실행된다. 이 구조에서 한 가지 문제가 있었다. 유저 요청의 시작부터 끝까지를 하나의 tra…

게임 이벤트가 100ms 늦게 도착하는데 아무도 몰랐다 — Redis Stream 관측 가능성 확보기

ZZOL의 모든 실시간 이벤트는 Redis Stream을 통해 흐른다. 방 입장, 카드 선택, 레이싱 탭 커맨드까지. 그런데 이 파이프라인에 대한 관측 지표가 하나도 없었다. 메시지가 100ms 늦게 도착해도, 스레드풀 큐가 80% 찼어도, 아무도 모르는 상태였다. …

모든 이벤트가 같은 스레드풀을 쓸 필요는 없다: 순서 보장과 처리량 사이의 딜레마

ZZOL의 모든 실시간 이벤트는 Redis Stream을 통해 흐른다. 방 생성, 입장, 카드 선택, 미니게임, 레이싱 탭 — 5개 스트림이 각각의 리스너로 메시지를 소비한다. 처음에는 하나의 스레드풀로 전부 처리했다. 그러다 특정 이벤트의 특성 때문에 스레드풀을 분…

분산 락의 함정: 락을 걸었는데도 이벤트가 두 번 처리된 이유

ZZOL에서 Redis Stream을 이벤트 버스로 사용하고 있다. 방 생성, 게임 시작, 룰렛 결과 등 거의 모든 도메인 이벤트가 Redis Stream을 통해 흐른다. 그런데 이벤트가 두 번 이상 도착할 수 있다는 사실을 인지했고, 이를 해결하기 위한 멱등성 처리…

"재시작하면 고쳐져요"라는 말을 없애기까지: 3단계 서버 자가 치유기

"장애가 발생했습니다. 서버 재시작할까요?" 서비스 초기, 개발자라면 누구나 한 번쯤 장애 알림을 보고 무의식적으로 재시작을 한 경험이 있을거다. 하지만 단일 인스턴스에서 강제 재시작은 곧 100% 다운타임을 의미하고, 이걸 어떻게 해결할까 고민했던 과정을 기록해본다…

HealthIndicator에는 무엇을 담아야 하는가: 상태 판별부터 자가 치유까지

ZZOL 서비스에 커스텀 HealthIndicator와 자가복구(Self-Recovery) 로직을 추가하고, Health Group을 분리했다. Spring Boot의 기본 헬스체크가 어디까지 커버하고, 어디서부터 커스텀이 필요한지, 장애 감지 후 Docker 재시작…

ZZOL의 효율적인 서버 자기보호 전략

ZZOL 서비스에 Rate Limiting을 적용했다. 단순히 "요청을 제한하자"가 아니라, 같은 서버 보호인데 HTTP와 WebSocket에서 왜 다른 전략을 써야 하는지, 각 설정값을 왜 그렇게 잡았는지에 대한 판단 과정을 기록한다. 왜 서버 자기보호가 필요했는가…

외부 서비스 장애로부터 살아남기

에서 외부 서비스 장애 대응 작업을 했다. Oracle Object Storage에 QR 코드를 업로드하는 과정에서, 외부 서비스가 불안정할 때 시스템 전체가 영향을 받는 문제가 있었다. 이 글에서는 Circuit Breaker와 Retry를 도입하면서 고민한 과정을…

© 2026 minjun.blog