피곤핑
코딩일탈
피곤핑
전체 방문자
오늘
어제
  • 분류 전체보기
    • Kotlin & Java
    • Spring
      • Spring Security
      • Spring
    • 네트워크
    • JavaScript & Node js
    • Docker
    • Python3
    • Unity
    • 딥러닝
    • 객체지향프로그래밍
    • Error 보고서
    • 나의 이야기 & 회고
    • HTML & CSS
    • Archive
    • 독서

블로그 메뉴

  • 홈
  • 방명록

공지사항

인기 글

태그

  • Client
  • 티스토리챌린지
  • TiL
  • nodejs
  • 99클럽
  • 오블완
  • 코딩테스트준비
  • 항해99
  • 개발자취업
  • JavaScript

최근 댓글

hELLO · Designed By 정상우.
피곤핑

코딩일탈

HTTP Keep-Alive, 성능 최적화의 핵심일까? 쓸데없는 설정일까?
네트워크

HTTP Keep-Alive, 성능 최적화의 핵심일까? 쓸데없는 설정일까?

2025. 2. 16. 23:06
웹 성능을 최적화할 때 자주 등장하는 개념 중 하나가 HTTP Keep-Alive다. Keep-Alive는 TCP 연결을 유지해 여러 개의 HTTP 요청을 처리할 수 있도록 도와주지만, 모든 환경에서 무조건 좋은 선택이 되는 것은 아니다. 

이번 글에서 Keep-Alive의 개념, 동작 원리, 장단점을 분석해 이것이 성능 최적화의 필수 요소인지, 아니면 특정 환경에서는 피해야 할 설정인지 살펴보자.

 

Keep-Alive 가 뭔데?

https://hackernoon.com/http-made-easy-understanding-the-web-client-server-communication-yz783vg3

 

기본적으로 HTTP 는 요청과 응답이 끝나면 연결을 끊는 방식으로 동작한다.  하지만 Keep-Alive 가 활성화되면 한번 맺은 TCP 연결을 재사용할 수 있다. 

🌱 Keep-Alive 가 비활성화된 기본 HTTP 동작은 어떻게 하는거지?

1. 클라이언트가 서버에 요청을 보낸다.

2. 서버가 응답을 반환하고 TCP 연결을 종료한다.

3. 다음 요청이 오면 새로운 TCP 연결을 생성한다. 

🌱 Keep-Alive 가 활성화 되면 어떻게 동작할까?

1. 클라이언트가 서버에 요청을 보낸다.

2. 서버가 응답을 반환하지만 TCP 연결을 유지한다.

3. 이후 추가 요청이 같은 연결을 재사용한다. 

 

이 방식은 HTTP 1.1 부터 기본적으로 활성화되어 있으며, 수동으로 비활성화 하려면 Connection: close 를 명시해야 한다.

 

설정 방법

1. HTTP 1.0

- 기본적으로 연결이 요청-응답 후 종료됨

- Connection: Keep-Alive 헤더를 추가하면 지속적인 연결 유지 가능

GET / HTTP/1.0
Connection: Keep-Alive

 

위 헤더가 포함되면 서버는 TCP 연결을 닫지 않고 유지하려고 한다. 

기본적으로 HTTP/1.0 에서는 요청이 끝나면 연결이 닫히므로 반드시 명시적으로 Connection: Keep-Alive 를 추가해야 한다.

2. HTTP 1.1

- 기본적으로 Keep-Alive 가 활성화됨

- 연결을 종료하려면 Connection: close 헤더를 명시해야함

GET / HTTP/1.1
Connection: close

⊕ 서버 기준으로 설정하는 방법도 존재한다. 

서버에서도 Keep-Alive 를 관리할 수 있으며, 주요 설정 옵션은 다음과 같다. 

 

- Keep-Alive Timeout: 연결을 유지하는 시간 (ex. 5초)

- Max Requests: 하나의 연결에서 처리할 수 있는 최대 요청 개수

- Buffer Size: 데이터 전송 시 사용되는 버퍼크기

 

어떻게 연결을 유지하는 걸까?

Keep-Alive 가 활성화되면 클라이언트와 서버는 같은 TCP 연결을 재사용하는데 이를 유지하는 메커니즘은 다음과 같다. 

1) 클라이언트가 첫 요청을 보낼 때

- 클라이언트가 Connection: Keep-Alive 를 포함하여 HTTP 요청을 보낸다. (HTTP/1.0 인 경우)

- 서버가 이를 받아들이고, 응답 헤더에 Connection: Keep-Alive 를 포함하여 응답한다.

- TCP 연결은 닫히지 않고 계속 유지된다. 

2) 추가 요청을 같은 TCP 연결에서 전송

- 클라이언트는 새로운 HTTP 요청을 보낼 때 기존 TCP 연결을 재사용한다.

- 서버는 Keep-Alive 설정이 적용된 경우 같은 연결에서 응답을 보낸다.

3) Keep-Alive Timeout 시간이 지나면 연결 종료

- 클라이언트가 일정 시간동안 요청을 보내지 않고 타임아웃 시간이 지나면 서버는 연결을 닫는다.

- 이후 클라이언트가 새로운 요청을 보내면 다시 새로운 TCP 연결이 생성된다.

 

그래서 뭐가 좋은데?

✅  성능 최적화

- TCP 연결을 새로 맺는 과정 (3-way handshake) 이 생략되면서 요청-응답 속도가 향상된다.

- 브라우저에서 여러개의 리소스를 요청할 때 (ex. CSS, JS, 이미지) 빠르게 처리가능하다.

✅  네트워크 및 서버 부하 감소

- TCP 연결을 재사용하므로 불필요한 연결 생성 비용이 줄어든다.

- 초당 많은 요청을 처리하는 서버에서 효과적이다.

✅  페이지 로딩 속도 개선

- Keep-Alive 가 없으면 브라우저가 리소스를 가져올 때마다 새로운 연결을 맺어야 하지만 Keep-Alive 를 사용하면 병렬 다운로드가 더 효율적으로 이뤄진다.

 

그럼 Keep-Alive 의 사용이 무조건 좋은 것 아냐?

그렇다면 Keep-Alive 의 사용이 무조건 좋아보이는데 사용하지 않을 이유가 있을까? 심지어 HTTP/1.1 에서는 Keep-alive 의 사용이 기본설정이다. 

 

하지만 우리가 사용하고 개발하는 서비스는 모두 같은 인프라 환경을 가지고 있지 않다. 따라서 다음에 환경에서는 Keep-Alive 의 사용이 비효율적일 수 있다.

☑️ 서버 리소스 낭비

너무 많은 Keep-Alive 연결을 유지하면 메모리와 CPU 사용량이 증가할 수 있다. 따라서 timeout 값을 적절하게 조정하여 사용해야한다.

☑️ 로드 밸런서와의 충돌 (쿠버네티스 환경 포함)

단일 인스턴스로 운영되고 있는 환경이 아니라면,

Keep-Alive 가 활성화되면 클라이언트가 항상 같은 서버 인스턴스(=파드) 로 요청을 보낼 가능성이 높아진다.

 

쿠버네티스는 기본적으로 여러 개의 파드로 부하를 분산해야하지만 Keep-Alive 로 인해 특정 인스턴스에 부하가 집중될 수 있다.

 

그래서 위 단점을 개선하고자 한다면

 

1️⃣ timeout 을 짧게 설정

 

2️⃣ 로드 밸런서에서 연결을 강제로 끊고 새로운 서버로 라우팅, 쿠버네티스 환경이라면 keep alive 활성화가 되더라도 트래픽이 분산될 수 있도록 아키텍쳐 개선이 필요

 

3️⃣ HTTP/2 사용 (멀티플렉싱)

 

하는 등의 해결책이 있을 수 있다. 

⊕ 추가적으로 HTTP/2.0 에서 멀티플렉싱에 대해서 좀 더 알아보자

HTTP/1.1 에도 문제점이있다. HTTP/1.1 에서는 한 개의 TCP 연결에서 하나의 요청-응답만 처리할 수 있다. 따라서 여러개의 요청을 동시에 처리하려면 추가적인 TCP 연결을 만들어야한다. 브라우저는 성능을 위해 여러 개의 TCP 연결을 병렬로 사용하지만 이는 오버헤드가 발생하게된다. 따라서 HOL Blocking 문제가 발생한다.

* HOL Blocking(Head-of-Line Blocking) : 하나의 요청이 지연되면 같은 연결을 공유하는 다른 요청들도 영향을 받음

 

HTTP/2.0 에서의 멀티플렉싱은 하나의 연결에서 여러개의 요청을 동시처리한다. 

요청과 응답을 스트림 단위로 처리하여 각 요청이 독립적으로 병렬처리된다. 따라서 한 요청이 지연되더라도 다른 요청은 영향을 받지 않는다. 별도의 추가적인 TCP 연결을 만들 필요가 없다. 

=> 각 요청이 독립적인 스트림으로 전송되므로 동시에 응답을 받을 수 있다.!

 

따라서 HTTP/2.0 환경에서는 Keep-Alive 를 따로 설정할 필요가 없다.

 

쿠버네티스 환경에서 Keep-Alive 를 사용했을 때 실제로 어떤 일이 일어날 수 있을까?

 

무서운 실무이야기를 해보자면... 이 keep-alive 옵션으로 인해 운영에서 장애를 겪은 적이있다.

현재 쿠버네티스 환경에서 운영하고 있고 클라이언트(웹) 으로 부터 요청을 받는 server 가 있고 또 그 요청을 받아서 IDC 에 있는 디비와 connection 하는 내부 서버가 존재한다.

HTTP/1.1 을 사용하고 있고 서버와 내부서버들은 같은 클러스터에 존재하고 있다. (server 의 경우도 인스턴스가 여러대 있지만 간략한 설명을 위해 생략했다) 

 

 

 

0. 클라이언트로 부터 요청을 받음

1. HTTP/1.1 를 사용하여 기본적으로 Keep-Alive 설정이 켜져 있는 상황이고 timeout 은 30초로 설정되어 있음

   - 쿠버네티스 환경에서 부하분산이 제대로 이루어지지않아 내부서버 1 로만 요청이 몰리게 됨

2. 데이터 베이스와 커넥션

3. slow query 혹은 trigger lock 의 이슈로 DB 와 의 커넥션이 길어지게 됨

4. 하나의 요청이 지연되면서 같은 연결을 공유하는 요청들도 영향을 받게되고 DB 자체의 부하가 생기게 되면서 모든 요청들의 대한 이슈가 생김

   - 설상가상으로 클라이언트에서는 요청의 대한 응답이 오지 않았을 경우 30초마다 retry 실행

5. 부하분산이 제대로 이루어지지 않아 지속적인 요청과 타임아웃으로 인한 과부하 & 헬스체크 실패로 내부서버1 파드가 죽어버림

=> 데이터 베이스 문제였기 때문에 애플리케이션 단에서 바로 해결할 수는 없었지만 keep-Alive 활성화 설정으로 인해 내부 서버 2로의  fail over 가 제대로 이루어지지 않았음을 볼 수 있음

 

위와 같은 사례로 인해 현재 내부서버간에는 일단 keep-alive 옵션을 꺼두고 인프라 개선을 앞두고 있는 상황이다. (언제 개선할지는 모르겠지만ㅠㅠ) 그리고 HTTP/2 멀티플렉싱을 알게되었는데 요것도 한번 검토해볼 예정이다. 

▶️ 결론

따라서, Keep-Alive 가 항상 최적의 선택은 아니다. 

쿠버 네티스 환경에서는 Keep-Alive 가 단일 인스턴스에 부하를 집중시킬 가능성이 있기 때문에 로드 밸런싱과 함께 고려해야한다.

일반 웹 서버에서는 성능 최적화에 유리하지만 쿠버네티스처럼 부하 분산이 중요한 환경에서는 부작용이 발생할 수 있다. 

 

최신 웹에서는 HTTP/2 를 사용하면 Keep-Alive 설정을 고민할 필요가 없지만 아직 그래도 HTTP/1.1 을 사용하는 환경이 더 많은 것 같다 (우리회사도...)

 

    피곤핑
    피곤핑

    티스토리툴바