지금까지 프로젝트를 진행하면서 단일 Spring 서버로 서비스를 운영했다.
기능적으로는 문제 없이 돌아갔다. 포트폴리오에 쓸 서비스가 EC2 프리티어를 감당하지 못할 정도의 트래픽이 쏠리지 않았었기 때문이다.
실제 앱스토어에 런칭한 경우에도 간단한 insert와 delete만 포함되어 있어 서버를 스케일링업 해야하는 상황을 겪지 못했었다.
하지만 이번 프로젝트 기획 상 트래픽이 많이 들어올 수 있을 거라 기대했다.
비용을 많이 들이지 않는 한에서 대비를 해보자는 생각에 서버에 트래픽이 몰렸을 때의 문제점과 해결방안을 공부해봤다.
우리 팀이 왜 단일 서버 다중 인스턴스 로드밸런싱을 진행하게 되었는지 설명하겠다.
문제 상황 : 트래픽 多
트래픽이 많아지면 무슨일이 생길까?
1. 요청이 느려짐
2. 일부 요청이 지연됨
3. 동시에 요청이 몰릴 때 불안정해짐
그럼 서버는 왜 느려질까
서버는 요청을 '무한히' 처리할 수 없다.
Spring 서버는 내부적으로 요청을 처리할 때 Thread 기반으로 동작한다.
그리고 그 Thread는 수가 제한되어 있다.
즉, 아래와 같이 요청을 처리한다.
요청 → Thread 할당 → 처리 → 반환
Thread Pool과 요청 처리 구조
서버는 일반적으로 Thread Pool을 사용하여 요청을 처리한다.
Thread Pool은 미리 일정 개수의 Thread를 생성하여 재사용하는 방식으로,
Thread 생성 및 제거에 따른 오버헤드를 줄일 수 있다.
또한 동시에 처리 가능한 요청 수를 제한하여 시스템 자원을 안정적으로 관리할 수 있다.
특히 Spring MVC와 같은 Blocking 기반 서버에서는,
동시에 처리 가능한 요청 수가 Thread 개수에 의해 제한된다.
Thread가 모두 사용되면 새로운 요청은 즉시 처리되지 않고 대기 상태로 들어가게 되며,
이로 인해 응답 지연이 발생할 수 있다.
요청 증가 → Thread 부족 → Queue 대기 → 응답 지연
예상 가능한 문제
트레픽이 몰리면 Thread가 모두 사용되고, 새로운 요청은 대기 상태로 들어가게 된다.
대기 시간은 길어지면서 전체 응답 속도가 느려지는 현상이 많이들 들었던 '병목(Bottleneck)'현상이다.
병목(Bottleneck)이란
시스템에서 가장 처리 속도가 느린 부분이 전체 성능을 제한하는 현상이다.
기존처럼 Spring 서버 1대만 존재했다면 트래픽이 몰릴경우 그 자체가 병목 지점이 된다.
모든 요청이 한 서버로 몰리기 때문에 그 서버가 전체 서버의 성능을 결정하게 되는 것이다.
해결 방안: Scale Up vs Scale Out
그럼 서버를 키우거나 늘리면 되는 일이다.
1. Scale Up (수직 확장)
- 서버의 스펙 사양을 높인다.
- 장점 : CPU 증가, 메모리 증가
- 단점 : 비용 증가, 물리적 한계 존재
2. Scale Out (수평 확장)
- 여러대의 서버를 추가로 설치한다.
- 장점 : 병목감소, 이론상 확장에 제한이 없음
- 단점 : 클러스터링 작업에 추가 비용이 발생, 관리하기가 까다로움
서버의 스펙에 한계가 있었으므로 Scale Out을 택했다.
그리고 여기서 필요한 것이 바로 로드밸런싱이다.
로드밸런싱이란

들어오는 요청을 여러 서버에 분산시키는 기술이다.
가장 기본적인 방식은 Round Robin 스케줄링을 사용하는 것이다.
요청 1 → 서버 A
요청 2 → 서버 B
요청 3 → 서버 A
요청 4 → 서버 B
이런식으로 요청을 번갈아 처리하면 특정 서버에 부하가 집중되는 것을 막을 수 있다.
Nginx를 선택한 이유
로드밸런서를 구현하기 위해 Nginx를 사용했다.
EC2에도 로드밸런서(ALB)가 구현되어 있는 걸 알았지만 제공받은 EC2의 대시보드에 접근할 수가 없다는 문제가 있었다.
해당 사항을 떠나 생각했을 때에도 금액 부담이 크고, 설정이 복잡하다는 단점이 잔재했다.
나는 인프라를 처음 경험해보는 상태이므로 관리 부담이 적은 Nginx를 사용하기로 했다.
소규모, 저비용을 추구하는 팀프로젝트에서는 적합한 선택이었다고 판단한다.
Nginx가 저비용이라고 해서 기능이 부족한 건 아니었다.
- Reverse Proxy 기능 제공
- L7 (Application Layer) 기반 로드밸런싱 지원
트래픽 분산 처리를 원했던 우리 팀의 첫 선택으로 적합했다고 생각한다.
L4 vs L7 로드밸런싱
로드밸런싱을 도입하고자 했을 때 이 두가지 방법이 존재한다는 사실에 조금 헷갈렸으므로 정리해본다.
L4는 IP + Port(Trasport Layer) 기준으로 요청을 분산하는 방식이다.
HTTP 내용을 안 보고 연결을 어디로 보낼지만 결정한다.
예를들면, AWS의 NLB(Network Load Balancer)가 있다. Nginx도 stream 모듈을 이용하여 구현할 수 있다.
| L4 | L7 | |
| 기준 | IP + Port | HTTP 내용 |
| 속도 | 빠름 | 상대적으로 느림 |
| 기능 | 단순 분산 | 경로 기반 분산 |
| 제어 | 제한적 | 매우 유연 |
하지만 L4를 적용하기에는 우리 서비스에 걸리는 단점들이 많았다.
1. HTTP 내용을 못본다.
우리 서비스에는 SSE를 사용하는 api가 있는데, 이 요청의 경우 Thread를 오랫동안 잡고 있었다.
이를 처리하기 위해 HTTP 내용을 확인할 필요가 있다.
2. 헤더 기반 제어가 불가
우리는 유저 인증을 위해 JWT 기반 처리를 하므로 인증 흐름을 제어할 수 없게 된다.
3. Reverse Proxy 기능 없음
리버스 프록시는 요청을 받아서 가공하고 제어한 뒤 서버로 넘기는 역할이다.
우리 서비스에서는 SSL를 종료하거나 헤더 추가하는 등 요청을 통제해야하는 상황이다.
L4를 사용할 경우 이러한 기능을 사용할 수 없게된다.
따라서 우리는 Nginx의 http 레이어에서 upstream을 정의하고 proxy_pass를 통해 요청을 분산하여 L7 로드밸런싱을 구현했다.
Reverse Proxy란
Nginx는 로드밸런서이기 이전에 Reverse Proxy다.
아래와 같은 구조로 Client와 Spring 서버 사이에서 클라이언트 대신 요청을 전달해준다.
이로인해 서버 구조를 숨길 수 있고, 로드밸런싱도 수행할수 있는 것이다.
Client → Nginx → Spring 서버
좀더 쉽게 정리하자면 아래와 같은 순서로 Reverse Proxy가 일을 한다.
1. 요청을 대신 받는다.
- Client → Reverse Proxy → Backend Server
- 이를 통해 서버를 직접 노출하지 않음
2. 요청을 분석하고 판단한다.
- URL, Header, 쿠키 확인
3. 요청을 가공한다
- 헤더 추가, 요청 수정, 인증 처리
4. 적절한 서버로 보낸다
- 로드밸런싱, 라우티
5. 응답도 다시 가공해서 반환한다
- 압축, 캐싱, 헤더 추가
Reverse Proxy 사용하는 목적
그리고 이런 상황에서 사용한다.
1. 서버 보안 (Security)
- 서버 IP 숨김 (직접 노출 방지)
- 외부 접근 통제
2. 트래픽 제어 (Traffic Control)
- 로드밸런싱, Rate Limiting
3. 구조 분리 (Decoupling)
- 프론트 /백 분리
- 마이크로서비스 라우팅
4. 성능 최적화 (Performance)
- 캐싱, 압축
5. SSL 처리 (SSL Termination)
- HTTPS 요청을 Proxy에서 처리
- 내부 서버는 HTTP로 단순화
6. 요청/응답 제어 (Request Handling)
- 헤더 전달 (X-Forwarded-For 등)
- 요청 변형 (Rewrite, Redirect)
즉 Reverse Proxy는 보호 + 분산 + 제어 를 처리할 수 있다.
적용 결과
로드밸런싱을 적용한 후 요청이 특정 서버에 몰리지 않았다.
부하 테스트를 곧 해보면서 응답 속도가 안정화 되었는지, 트래픽 증가 상황에서도 서비스가 유지되는 지를 확인하여 업데이트 하도록하겠다.
마무리
이번 인프라 경험을 통해 서버 구조, 트래픽 흐름, 시스템 설계를 고민하게 되었다.
아직 문제가 없는 부분이라고 우선순위를 미루기만 해서는 학습할 기회를 놓치는 것 같다.
좀더 서비스를 위한 구조를 고민하고 공부해야겠다고 생각한다.
'개발 > CS' 카테고리의 다른 글
| 그래서 분산이 뭔데 (1) | 2026.02.26 |
|---|---|
| 채팅 기능 구현을 위한 WebSocket + STOMP 구조 정리 (5) | 2026.02.17 |