1️⃣ 개요
프로젝트에서 알림 기능을 구현하기 위해 Firebase Cloud Messaging(FCM) Firebase Admin Java SDK 9.4.3 버전을 사용하던 중, HTTP/2 관련 오류가 발생했다. org.apache.hc.core5.http.ProtocolException: Header ‘Host: fcm.googleapis.com’ is illegal for HTTP/2 messages 이 오류는 채팅 메시지에 알림을 보내는 로직을 추가하면서 발생하였다. 사용자가 채팅을 연속으로 보낼 때, 각 메시지마다 FCM을 통해 알림이 전송되도록 구현했는데, 단기간에 많은 알림 요청이 발생하면서 위 오류가 발생했다. 이 오류가 발생한 이유를 파악하기 위해 HTTP/1.1과 HTTP/2의 차이점에 대해 정리했다.
2️⃣ HTTP 란?
HTTP(HyperText Transfer Protocol, 하이퍼텍스트 전송 프로토콜)는 웹에서 데이터를 주고받을 때 사용하는 규칙(프로토콜)입니다. 우리가 인터넷에서 웹사이트를 방문할 때, 웹 브라우저(예: 크롬, 엣지, 사파리 등)와 웹 서버 간의 통신이 HTTP를 통해 이루어집니다.
HTTP의 특징
✅ 비연결성(Connectionless)
요청과 응답이 끝나면 연결이 끊어져서 서버의 부담이 적습니다.
✅ 무상태성(Stateless)
이전 요청과 다음 요청이 서로 독립적이어서 상태를 유지하지 않습니다.
✅ 텍스트 기반 프로토콜
사람이 읽을 수 있는 형태로 데이터를 주고받기 때문에 디버깅이 쉽습니다.
3️⃣ HTTP/1.1 vs HTTP/2 차이점
3.1 HTTP/1.1 특징
HTTP/1.1은 1997년에 도입된 프로토콜로, 현재까지도 널리 사용되고 있다. 하지만 몇 가지 한계점이 있다. 지속적인 연결(Persistent Connection): Keep-Alive를 통해 연결을 유지하여 성능을 개선하지만, 여전히 요청이 순차적으로 처리됨. 헤더 크기 증가 문제: 요청마다 헤더가 포함되며, 중복 데이터가 많아 성능 저하를 유발함.
파이프라이닝(Pipelining) 지원하지만 HOLB(Head-of-Line Blocking) 문제 존재: 여러 요청을 한 번에 보낼 수 있지만, 응답이 순차적으로 와야 함. 하나의 요청이 지연되면 이후 요청도 지연됨.
텍스트 기반 프로토콜: 사람이 읽기 쉽지만, 데이터 전송이 비효율적.
3.2 HTTP/2 특징
HTTP/2는 2015년에 도입된 프로토콜로, HTTP/1.1의 문제점을 해결하여 성능을 대폭 향상시켰다. 멀티플렉싱(Multiplexing) 지원: 하나의 TCP 연결에서 여러 개의 요청과 응답을 동시에 처리 가능 → HOLB 문제 해결. 이진 프레이밍(Binary Framing) 사용: 텍스트 대신 이진 데이터를 사용하여 전송 효율 증가. 헤더 압축(HPACK 사용): 중복된 헤더 데이터를 줄여 성능 개선. 서버 푸시(Server Push) 지원: 클라이언트 요청 없이도 서버가 리소스를 미리 전송 가능. 우선순위 지정 가능: 중요한 요청을 먼저 처리하도록 조정 가능.
4️⃣ HTTP/2에서 발생한 오류 분석
4.1 오류 원인
Header 'Host: fcm.googleapis.com' is illegal for HTTP/2 messages
오류는 HTTP/2에서 Host 헤더를 명시적으로 추가할 수 없기 때문에 발생한다.
-
HTTP/1.1에서는 Host 헤더가 필수이지만, HTTP/2에서는 ``** Pseudo-header**가 대신 사용됨.(:authority 로 대체)
📌 Pseudo-header란? HTTP/2에서 사용되는 특수한 형태의 헤더로, 기존 HTTP/1.1의 일부 헤더(예: Host)를 대체하여 요청과 응답을 더 효율적으로 처리하는 역할을 합니다.
-
FCM API 요청 시, HTTP/1.1 스타일로 요청을 보내면서 Host 헤더가 포함되면 HTTP/2에서 오류 발생. ★ Firebase Admin Java SDK 9.4.0 버전부터 HTTP/2 지원 전송이 추가 되어 기본 HTTP 전송으로 사용됩니다.
4.2. 오류 해결 방법
FCM 요청 HTTP/2 -> HTTP/1.1로 강제하도록 수정하여 해결하였습니다.
// HTTP/1.1을 사용하도록 NetHttpTransport 적용
HttpTransport httpTransport = new ApacheHttpTransport();
// Firebase 옵션 설정
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setHttpTransport(httpTransport)
.build();