HTTP/Guides/Protocol upgrade mechanism

김동현·2026년 3월 22일

프로토콜 업그레이드 메커니즘

HTTP/1.1 프로토콜Upgrade 헤더 필드를 사용하여 이미 설정된 연결을 다른 프로토콜로 업그레이드하는 데 사용할 수 있는 특별한 메커니즘을 제공해요.

이 메커니즘은 선택 사항이에요. 프로토콜 변경을 강요하는 데 사용할 수 없어요. 구현체는 새 프로토콜을 지원하더라도 업그레이드를 활용하지 않을 수 있고, 실제로 이 메커니즘은 주로 WebSocket 연결을 부트스트랩하는 데 사용돼요.

또한 HTTP/2는 이 메커니즘의 사용을 명시적으로 금지한다는 점도 유의하세요. 이것은 HTTP/1.1에만 해당되는 거예요.

이 문서의 내용

HTTP/1.1 연결 업그레이드하기

Upgrade 헤더 필드는 클라이언트가 서버에게 나열된 프로토콜 중 하나로 전환하도록 요청하는 데 사용되는데, 선호도가 높은 순서대로 내림차순으로 정렬돼요.

Upgrade는 홉별(hop-by-hop) 헤더이기 때문에, Connection 헤더 필드에도 나열되어야 해요. 즉, Upgrade를 포함하는 일반적인 요청은 다음과 같이 보일 거예요:

GET /index.html HTTP/1.1
Host: www.example.com
Connection: upgrade
Upgrade: example/1, foo/2

요청된 프로토콜에 따라 다른 헤더가 필요할 수 있어요. 예를 들어, WebSocket 업그레이드는 WebSocket 연결에 대한 세부 정보를 구성하고 연결을 열 때 어느 정도의 보안을 제공하기 위한 추가 헤더를 허용해요. 자세한 내용은 WebSocket 연결로 업그레이드하기를 참조하세요.

서버가 연결을 업그레이드하기로 결정하면, 전환할 프로토콜을 지정하는 Upgrade 헤더와 함께 101 Switching Protocols 응답 상태를 다시 보내요. 연결을 업그레이드하지 않거나 업그레이드할 수 없는 경우, Upgrade 헤더를 무시하고 일반 응답(예: 200 OK)을 보내요.

101 상태 코드를 보낸 직후, 서버는 필요에 따라 추가 프로토콜별 핸드셰이크를 수행하면서 새 프로토콜로 통신을 시작할 수 있어요. 사실상, 업그레이드된 응답이 완료되는 즉시 연결은 양방향 파이프가 되고, 업그레이드를 시작한 요청은 새 프로토콜을 통해 완료될 수 있어요.

💡 강사 팁: 프로토콜 업그레이드 메커니즘은 정말 영리한 설계예요. HTTP/1.1로 시작했다가 중간에 완전히 다른 프로토콜로 전환할 수 있다는 게 핵심이에요. 이렇게 하면 기존 웹 인프라(방화벽, 프록시 등)를 그대로 활용하면서도 새로운 프로토콜의 이점을 누릴 수 있어요. WebSocket이 80/443 포트를 사용할 수 있는 이유가 바로 이거예요!

이 메커니즘의 일반적인 사용 사례

여기서는 Upgrade 헤더의 가장 일반적인 사용 사례를 살펴볼 거예요.

WebSocket 연결로 업그레이드하기

단연코, HTTP 연결을 업그레이드하는 가장 일반적인 사용 사례는 WebSocket을 사용하는 거예요. WebSocket은 항상 HTTP 또는 HTTPS 연결을 업그레이드하여 구현돼요. WebSocket API를 사용하거나 WebSocket을 수행하는 라이브러리를 사용해서 새 연결을 여는 경우, 이 작업의 대부분 또는 전부가 자동으로 수행된다는 점을 명심하세요. 예를 들어, WebSocket 연결을 여는 것은 단일 메서드로 이루어져요:

webSocket = new WebSocket("ws://destination.server.ext", "optionalProtocol");

WebSocket() 생성자는 초기 HTTP/1.1 연결을 생성하고 핸드셰이킹 및 업그레이드 프로세스를 처리하는 모든 작업을 수행해요.

참고:
보안 WebSocket 연결을 열기 위해 "wss://" URL 스키마를 사용할 수도 있어요.

WebSocket 연결을 처음부터 만들어야 한다면, 핸드셰이킹 프로세스를 직접 처리해야 해요. 초기 HTTP/1.1 세션을 생성한 후, 다음과 같이 표준 요청에 UpgradeConnection 헤더를 추가하여 업그레이드를 요청해야 해요:

Connection: Upgrade
Upgrade: websocket

💡 강사 팁: 대부분의 경우 WebSocket 라이브러리를 사용하기 때문에 이런 저수준 핸드셰이크를 직접 구현할 일은 거의 없어요. 하지만 디버깅할 때는 이런 내부 동작을 아는 게 정말 중요해요. Chrome DevTools의 Network 탭에서 WebSocket 연결을 보면 처음에는 HTTP 요청으로 시작했다가 101 응답 이후 WS로 전환되는 걸 볼 수 있어요. 이게 바로 프로토콜 업그레이드가 일어나는 순간이에요!

WebSocket 전용 헤더

다음 헤더들은 WebSocket 업그레이드 프로세스에 관련되어 있어요. UpgradeConnection 헤더 외에, 나머지는 일반적으로 선택 사항이거나 브라우저와 서버가 서로 통신할 때 자동으로 처리돼요.

Sec-WebSocket-Extensions

서버가 사용하도록 요청할 하나 이상의 프로토콜 수준 WebSocket 확장을 지정해요. 요청에서 두 개 이상의 Sec-WebSocket-Extension 헤더를 사용하는 것이 허용돼요. 결과는 모든 나열된 확장을 하나의 헤더에 포함시킨 것과 같아요.

Sec-WebSocket-Extensions: extensions
  • extensions: IANA WebSocket Extension Name Registry에서 선택해야 하는 확장의 쉼표로 구분된 목록이에요. 매개변수를 사용하는 확장은 세미콜론으로 구분해요.

예를 들어, 이 헤더는 두 개의 커스텀 확장을 나타내요: superspeedcolormode(추가로 depth=16 매개변수를 가져요):

Sec-WebSocket-Extensions: superspeed, colormode; depth=16

Sec-WebSocket-Key

클라이언트가 WebSocket으로 업그레이드를 요청할 자격이 있는지 확인하기 위해 서버에 필요한 정보를 제공해요. 이 헤더는 안전하지 않은(HTTP) 클라이언트가 업그레이드하려고 할 때 사용할 수 있으며, 악용에 대한 어느 정도의 보호를 제공하기 위함이에요. 키의 값은 WebSocket 명세에 정의된 알고리즘을 사용하여 계산되므로, 이것이 보안을 제공하지는 않아요. 대신, WebSocket이 아닌 클라이언트가 실수로 또는 오용으로 WebSocket 연결을 요청하는 것을 방지하는 데 도움이 돼요. 본질적으로, 이 키는 "네, 정말로 WebSocket 연결을 열려고 해요"라고 확인하는 거예요.

이 헤더는 사용하기로 선택한 클라이언트에 의해 자동으로 추가돼요. fetch()XMLHttpRequest.setRequestHeader() 메서드를 사용해서 추가할 수 없어요.

Sec-WebSocket-Key: key
  • key: 이 업그레이드 요청을 위한 키예요. 클라이언트가 원하면 이것을 추가하고, 서버는 응답에 자체 키를 포함할 거예요. 클라이언트는 업그레이드 응답을 전달하기 전에 이를 검증할 거예요.

서버 응답의 Sec-WebSocket-Accept 헤더는 지정된 key를 기반으로 계산된 값을 가질 거예요.

💡 강사 팁: Sec-WebSocket-Key는 보안 메커니즘이 아니라는 점이 중요해요. 이건 단지 우연한 연결을 방지하는 거예요. 실제 보안은 wss:// (WebSocket Secure)를 사용해야 해요. 제가 본 초보자들의 흔한 오해가 이 키가 암호화나 인증을 제공한다고 생각하는 건데, 전혀 그렇지 않아요!

Sec-WebSocket-Protocol

Sec-WebSocket-Protocol 헤더는 선호도 순서대로 사용하려는 하나 이상의 WebSocket 프로토콜을 지정해요. 서버가 지원하는 첫 번째 프로토콜이 선택되고 응답에 포함된 Sec-WebSocket-Protocol 헤더에 의해 서버에서 반환될 거예요. 헤더에서 이것을 두 번 이상 사용할 수도 있어요. 결과는 단일 헤더에서 쉼표로 구분된 서브프로토콜 식별자 목록을 사용하는 것과 같아요.

Sec-WebSocket-Protocol: subprotocols
  • subprotocols: 선호도 순서대로 정렬된 서브프로토콜 이름의 쉼표로 구분된 목록이에요. 서브프로토콜은 IANA WebSocket Subprotocol Name Registry에서 선택하거나 클라이언트와 서버가 공동으로 이해하는 커스텀 이름일 수 있어요.

Sec-WebSocket-Version

요청 헤더

클라이언트가 사용하려는 WebSocket 프로토콜 버전을 지정해서, 서버가 해당 버전이 지원되는지 확인할 수 있게 해요.

Sec-WebSocket-Version: version
  • version: 클라이언트가 서버와 통신할 때 사용하려는 WebSocket 프로토콜 버전이에요. 이 번호는 IANA WebSocket Version Number Registry에 나열된 가장 최신 버전이어야 해요. WebSocket 프로토콜의 가장 최신 최종 버전은 버전 13이에요.
응답 헤더

서버가 지정된 WebSocket 프로토콜 버전을 사용하여 통신할 수 없는 경우, 헤더에 지원되는 프로토콜 버전의 쉼표로 구분된 목록이 포함된 Sec-WebSocket-Version 헤더와 함께 오류(예: 426 Upgrade Required)로 응답할 거예요. 서버가 요청된 프로토콜 버전을 지원하는 경우, 응답에 Sec-WebSocket-Version 헤더가 포함되지 않아요.

Sec-WebSocket-Version: supportedVersions
  • supportedVersions: 서버가 지원하는 WebSocket 프로토콜 버전의 쉼표로 구분된 목록이에요.

💡 강사 팁: 실무에서는 버전 불일치 문제를 거의 겪지 않아요. 왜냐하면 현대의 모든 브라우저와 서버가 버전 13을 지원하기 때문이에요. 하지만 레거시 시스템을 다룰 때는 이 헤더를 확인하는 게 중요해요. 제가 예전에 오래된 WebSocket 서버를 유지보수할 때 이 헤더 덕분에 호환성 문제를 빠르게 진단할 수 있었어요!

응답 전용 헤더

서버의 응답에는 이것들이 포함될 수 있어요.

Sec-WebSocket-Accept

서버가 WebSocket 연결을 시작할 의향이 있을 때 오프닝 핸드셰이크 프로세스 중에 서버의 응답 메시지에 포함돼요. 응답 헤더에 두 번 이상 나타나지 않을 거예요.

Sec-WebSocket-Accept: hash
  • hash: Sec-WebSocket-Key 헤더가 제공된 경우, 이 헤더의 값은 키의 값을 가져와서 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 문자열을 연결하고, 그 연결된 문자열의 SHA-1 해시를 취해서 20바이트 값을 얻어서 계산돼요. 그런 다음 그 값은 이 속성의 값을 얻기 위해 base64 인코딩돼요.

💡 강사 팁: 이 해시 계산 과정은 클라이언트가 서버의 응답이 정당한지 확인하는 방법이에요. 중간에 프록시가 잘못된 응답을 보내는 걸 방지하는 거죠. 실제로 WebSocket 라이브러리를 구현해본 적이 있다면 이 해시 계산 로직을 직접 구현해야 해요. 마법 문자열 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"은 WebSocket RFC에 정의된 상수예요!

명세

명세
The WebSocket Protocol
Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing
Hypertext Transfer Protocol Version 2 (HTTP/2)

참고 자료

profile
프론트에_가까운_풀스택_개발자

0개의 댓글