solyrion

트래픽 분산 본문

DevOps

트래픽 분산

ert1015 2025. 12. 27. 17:14

부하테스트 대회에서 배운 “트래픽 분산”의 관점 (로깅/모니터링 포함)

최근 부트캠프에서 2일간 부하테스트 대회를 진행했습니다.

이전까지는 프로젝트를 할 때 구현에 집중하거나, 부하를 고려한다고 해도 실제로 많은 트래픽을 받는 상황을 만들어보기는 쉽지 않았습니다.

그런데 이번에는 제한된 시간 안에 실제로 트래픽을 밀어 넣고, 병목을 찾고, 개선하고, 다시 측정하는 경험을 할 수 있었습니다.

이번 글에서는 그 과정에서 정리한 트래픽을 분산하는 방법들을 공유해보려고 합니다.


트래픽이 많으면 서버만 늘리면 되지 않나?가 부족했던 이유

보통 트래픽 분산이라고 하면 “서버를 여러 대로 늘린다”를 먼저 떠올립니다.

저도 실제로 면접에서 “트래픽이 많으면 어떻게 대응할 건가요?”라는 질문을 받으면 분산 서버(Scale-out) 로 답변하곤 했습니다.

틀린 대답은 아니지만, 대회에서 직접 부하를 걸어보니 이런 생각이 들었습니다.

  • 트래픽이 많으면 “그냥 분산하면 된다”는 건 너무 포괄적이다.
  • 어디가 병목인지 모르면 서버를 늘려도 효과가 없거나 비용만 늘 수 있다.
  • 결국 “분산”은 서버만이 아니라 여러 계층에서 일어날 수 있다.

그래서 부하테스트 대회를 진행하며 정리했던 내용을 기준으로, 트래픽 분산을 크게 3가지 관점으로 나눠봤습니다.


트래픽 분산

1) 코드(애플리케이션) 레벨 최적화

가장 먼저 고려해야 하는 건 코드 단에서의 최적화라고 생각했습니다.

예를 들어,

  • 이미지 처리 방식(리사이징/압축/업로드 흐름 등) 최적화
  • 로직 상의 불필요한 연산 제거
  • DB 쿼리 최적화(N+1 방지, 인덱스, 조회 범위 최소화 등)

같은 것들이 먼저 잡혀야, 그 다음 단계(서버/DB 분산)가 의미가 생긴다고 생각합니다.

코드에서 해결 가능한 병목이라면, 불필요하게 비용을 들여 서버 수를 늘리지 않아도 될 수 있습니다.


2) 서버(애플리케이션) 분산

가장 먼저 떠올리는 방식은 애플리케이션 서버를 여러 대로 늘리고, ALB 같은 로드밸런서를 통해 트래픽을 대상 그룹(Target Group) 으로 분산하는 방법입니다.

ALB는 요청을 대상 그룹으로 라우팅한 뒤, 헬스체크를 통과한 인스턴스들에 요청을 분산해줍니다.

이 방식의 핵심은 서버를 여러 대로 늘렸을 때, 각 서버가 동일하게 요청을 처리할 수 있어야 한다는 점입니다.

여기서 중요한 조건이 하나 생깁니다.

애플리케이션 서버는 Stateless(무상태)여야 한다.

모든 요청이 여러 인스턴스로 분산되기 때문에, 특정 정보를 로컬에 저장해버리면 다음 요청이 같은 인스턴스로 들어온다는 보장이 없습니다.

즉, 상태(세션/캐시/파일 등)는 서버 로컬이 아니라 외부 시스템에 있어야 안정적으로 동작합니다.

실제로 제가 제공받은 코드에서는,

  • 이미지가 로컬 저장 방식
  • 채팅(소켓) 관련 정보도 로컬 캐싱 방식

이었고, 부하 환경에서 안정적으로 운영하려면 구조를 바꿔야 했습니다. 그래서

  • 이미지 저장은 S3
  • 실시간 이벤트 처리는 Redis Pub/Sub

처럼  상태를 외부로 분리하는 방식으로 변경했습니다.


3) DB 분산

애플리케이션 서버를 분산해도, 트래픽이 늘면 결국 DB가 병목이 되는 경우가 많습니다.

특히 읽기/쓰기 요청이 모두 DB로 몰리면, 애플리케이션 서버를 늘려도 DB가 버티지 못해 전체 성능이 제한될 수 있습니다.

제가 떠올렸던 DB 분산 방식은 크게 두 가지였습니다.

(1) Primary–Secondary(Replica) 구성: 읽기/쓰기 분리

  • 쓰기(Write) 는 Primary로
  • 읽기(Read) 는 Secondary(Replica)로

분리해서 DB 부하를 나누는 방식입니다.

일반적으로 트래픽이 증가할 때 읽기 요청이 더 많아지는 경우가 많기 때문에, Replica를 늘려 읽기 부하를 수평 확장하는 전략이 유효합니다.

또한 장애 상황에서는 구성에 따라 failover(승격) 로 Primary를 대체할 수 있습니다.

저희 팀은 RDS를 사용하지 않았고 제한된 시간 내에 가능한 범위에서, Primary–Secondary 구성으로 읽기 부하를 분산하는 형태로 구성했습니다.

(2) 샤딩(Sharding): 데이터 자체를 나눠 저장

샤딩은 특정 key를 기준으로 데이터를 분할 저장하는 방식입니다.

예를 들어,

  • 특정 범위(id 1~100은 DB1, 101~200은 DB2)
  • 또는 hash 기반(userId % N) 등

데이터를 아예 여러 DB로 나눠 저장해 쓰기/읽기 모두를 분산할 수 있습니다.

다만 이번 대회에서는 시간과 운영 복잡도를 고려했을 때 샤딩까지 적용하기는 어려웠고, 개념적으로 “다음 단계”로 정리해두는 정도로 마무리했습니다.


관측(모니터링/로깅)이 없으면 “분산”도 방향을 잃는다

이번 대회에서 가장 크게 배운 점은 이거였습니다.

분산은 ‘서버를 늘리는 선택지’가 아니라, 관측(모니터링/로깅)으로 병목을 찾고, 병목을 적절한 계층에서 해소하는 과정이다.

실제로 저희는 서버별 주요 metric을

  • Prometheus + Grafana
  • CloudWatch

등으로 확인하면서 CPU, 메모리, 요청 처리량, 응답 시간 변화 등을 추적했습니다.

아쉬웠던 부분은 로깅을 도입하지 않아서 명확한 에러 위치를 파악하기 어려웠습니다.

특히 E2E 테스트에서 계속 오류가 나는 구간이 있었는데, 그 부분에 로깅이 충분히 적용되어 있지 않아서 에러 지점을 특정하기가 어려웠고, 해결하는 데 시간이 많이 소요됐습니다.

이 경험으로 로깅의 중요성을 체감할 수 있었고 이후 해커톤에서는 로깅을 바로 적용할 수 있었습니다.

만약 이번 부하테스트에서도 로깅을 도입했다면,

  • 어느 구간에서 지연이 발생하는지
  • 어느 요청이 실패하는지
  • 어떤 리소스가 먼저 한계에 도달하는지

가 빠르게 보였을거고, 그 다음부터는 “서버를 늘릴지 / 캐시를 둘지 / DB를 분리할지” 같은 판단을 훨씬 근거 있게 할 수 있을 것 같았던 아쉬움이 남습니다.


마무리

이번 부하테스트 대회를 통해, 단순히 “트래픽이 많으면 서버를 분산한다”를 넘어

코드/서버/DB 등 다양한 계층에서 트래픽을 분산하는 방법이 존재한다는 것을 체감했습니다.

그리고 무엇보다, 어디에 트래픽이 집중되는지를 명확히 알기 위한 모니터링과 로깅의 중요성을 배웠습니다.

'DevOps' 카테고리의 다른 글

무중단 배포  (0) 2026.01.24
Spring Boot 빌드: GitHub Actions vs Dockerfile  (0) 2025.11.29
Comments