대량 트래픽에서 병목은 어디서 생기고 어떻게 줄였는가

GS에서 맡은 Nike 관련 서비스는 대량 트래픽 상황을 실제로 체감하게 만든 계기였습니다. 이 글은 당시 운영하며 작성한 메모와 직접 보며 남긴 관찰 기준을 바탕으로, 병목이 어디에서 발생하고 어떤 식으로 줄여나갔는지를 정리한 기록입니다.
당시 관찰 축은 공개용 글에 맞게 다이어그램과 서술 중심으로 다시 정리했습니다.
병목은 코드 한 줄에서만 생기지 않았다
대량 트래픽에서 병목은 특정 메서드 하나보다 흐름 전체에서 드러나는 경우가 많았습니다.
- 조회 트래픽 증가
- 관리자 기능 지연
- 주문 처리 경쟁
- 재고 차감 집중
- 배치와 실시간 요청의 자원 경합
이런 것들이 동시에 겹치기 시작하면 특정 지점만 튜닝해서는 해결되지 않았습니다. 무엇이 먼저 막히고, 그 영향이 어디로 번지는지 흐름으로 봐야 했습니다.
대량 트래픽 상황에서 흔히 하는 실수는 가장 눈에 띄는 지표 하나만 붙잡는 것입니다. 하지만 실제로는 응답 지연 뒤에 재고 경쟁이 있을 수도 있고, 관리자 기능 지연 뒤에 배치 부하가 있을 수도 있습니다. 병목을 점으로 보는 게 아니라 흐름으로 봐야 했던 이유입니다.
운영하며 남긴 메모와 관찰 기준
운영하며 정리해둔 성능 개선 메모에는 주문 벌크 다운로드 개선 같은 수치가 남아 있습니다. 이런 메모는 단순히 “얼마나 빨라졌다”보다도, 어떤 기능이 병목 후보로 보였는지를 알려줍니다.
대량 트래픽 상황에서 직접 남긴 운영 캡처도 마찬가지였습니다. 화면 그 자체보다 중요한 건, 어떤 상황에서 무엇을 보고 있었는지입니다. 운영자가 어느 지표와 화면을 주시했는지 자체가 당시 병목 판단 기준을 보여줍니다.
운영 캡처는 나중에 다시 봐도 생각보다 많은 걸 알려줍니다. 시스템 문서에는 “이럴 때 무엇을 보라”가 잘 안 남는데, 캡처에는 실제로 무엇을 보고 있었는지가 드러나기 때문입니다.
무엇을 먼저 줄였는가
지금 기준으로 다시 정리하면, 우선순위는 보통 아래와 같았습니다.
- 재고와 주문처럼 경쟁이 직접 발생하는 흐름
- 운영자가 즉시 영향을 받는 관리자/조회 기능
- 대량 데이터 조회나 다운로드처럼 부하가 큰 작업
- 배치와 실시간 요청이 서로 간섭하는 구간
이 순서를 놓치면 개선 작업이 산발적으로 흩어지기 쉽습니다.
실제 수행 항목도 이런 우선순위와 크게 다르지 않았습니다. 브리즈 플랫폼 성능 고도화라는 큰 목표 아래에서 ProxySQL 기반의 Read/Write 분리 지점을 선별하고, 일부 트래픽을 write 경로로 유도하기 위한 트랜잭션 설정을 조정하고, 관리자 콘솔과 주문 조회 성능을 따로 개선하는 식으로 접근했습니다. 즉 병목을 한 군데에서만 해결하려 하지 않고, 읽기 분산, 쓰기 경쟁 완화, 운영자 체감 성능 개선을 함께 묶어서 본 셈입니다.
특히 가장 아팠던 건 재고 차감 구간의 DB 트랜잭션 경쟁이었습니다. 히트 상품 하나에 수십만 고객이 몰리면 결국 같은 재고 경계를 여러 요청이 동시에 두드리게 되고, DB 특성상 range lock 성격의 잠금 경합까지 겹치면서 지연이 크게 튀었습니다. 이 문제는 애플리케이션 코드만 보고는 설명되지 않았고, DB와 트랜잭션 경계를 같이 봐야 보였습니다.
그나마 ProxySQL 도입 이후에는 읽기 경로 분산 효과가 분명했습니다. 완전히 해결된 것은 아니었지만, 이전보다 병목이 터지는 시점을 늦추고 운영 안정성을 높이는 데는 꽤 도움이 됐습니다. 반대로 queue 기반 차감 완화는 당시에도 필요성을 강하게 느꼈지만, 미래 개선 과제로 남았고 실제 도입까지는 이어지지 못했습니다.
즉 병목 완화는 최적화 체크리스트를 하나씩 지우는 일이 아니라, 전체 흐름에서 가장 비싼 경쟁을 먼저 없애는 일에 가까웠습니다.
다음 글에서는 구조와 트래픽 문제를 조금 더 비즈니스 규칙 쪽으로 가져와, 부분반품 환불 금액을 어떻게 다시 계산해야 했는지 정리합니다: 부분반품 환불 금액은 어떻게 다시 계산해야 하는가