이번에는 Lighthouse 또 다른 진단항목 HTTP/2를 사용하세요을 살펴보겠습니다.
기존에는 모든 정적 파일들에 대한 통신은 HTTP/1.1로 하고 있었습니다.
권장 사항으로 HTTP/2로 사용하라고 하네요.
Lighthouse로 측정해보고 HTTP/1.1과 HTTP/2의 경우, 어떠한 차이가 나는지 비교해보겠습니다.
우선 기존 HTTP/1.1일 때, Lighthouse 측정한 결과와 성능탭과 네트워크 탭으로 확인한 결과입니다.
FCP 0.2초,LCP 0.5초 ,SI 0.2초로 측정됩니다.
성능 탭 측정시, 리소스 요청/응답 타이밍
네트워크 탭, 리소스 요청 지표
우선 HTTP/1.1과 HTTP/2에 대해서 이론적으로 살펴보겠습니다.
기본적으로 한 번에 한 요청만 처리할 수 있습니다.
하지만 지속 연결을 통해 여러 리소스를 동일한 연결에서 처리할 수 있습니다.
Connection: keep-alive 헤더를 통해 하나의 TCP 연결을 유지하며 여러 리소스를 순차적으로 요청 및 응답받을 수 있습니다.
위 이미지처럼 동일한 연결 ID(예: 269946)에서 여러 리소스 요청이 이루어질 수 있습니다.
위처럼 하나의 TCP 연결을 유지하며 여러 리소스를 요청가능하지만 응답은 요청 순서대로 도착해야 합니다. 예를 들어, 요청 1 → 요청 2 → 요청 3이 순서대로 보내졌다면, 응답 1 → 응답 2 → 응답 3이 순서대로 와야 합니다.
때문에 헤드 오브 라인 블로킹 (Head-of-Line Blocking)라는 문제가 발생합니다.
헤드 오브 라인 블로킹 (Head-of-Line Blocking)이란 앞선 요청의 응답이 지연되면, 그 뒤에 있는 응답들도 대기하게 되는 현상을 말합니다.
위 네트워크 탭 폭포 컬럼에서 확인해보면 같은 연결 ID(예:269946)에 여러 리소스를 요청하긴 하지만 리소스 응답후에 요청을 하는 것을 확인할 수 있습니다.
각 요청마다 헤더에 같은 내용이 있더라도, 반복해서 같은 헤더의 내용을 보내게 됩니다.
HTTP/1.1은 텍스트 형식으로 요청과 응답 데이터를 전송합니다.
GET /index.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: */*
헤더는 문자열로 전송되기 때문에 공백, 대소문자, 줄바꿈 등 불필요한 문자가 많습니다.
또한 서버와 클라이언트는 이 텍스트를 파싱(분석)해서 의미를 해석해야 합니다.
HTTP/2는 하나의 TCP 연결에서 여러 요청과 응답을 동시에 처리할 수 있습니다.
HTTP/1.1에서는 하나의 요청이 처리될 때까지 다음 요청이 대기해야 하는 Head-of-Line Blocking 문제가 있었지만, HTTP/2에서는 이를 해결하여 더 빠르게 리소스들을 요청 응답할 수 있습니다.
위는 HTTP/2로 변경한 뒤, 네트워크 탭에서 확인한 결과인데요.
연결 ID 컬럼을 확인해보면 같은 연결 ID(257741)로 리소스들을 병렬로 요청하는 것을 확인할 수 있습니다.HTTP/1.1과 달리 다른 리소스 응답 순서에 관계없이 응답이 오는 것을 확인할 수 있죠.
HTTP/2에서도 요청마다 헤더를 보내지만, HPACK 알고리즘을 통해 중복된 헤더를 효율적으로 압축하기 때문에 전송 크기가 매우 줄어듭니다.
즉, 중복 헤더를 전송하긴 하지만, 중복된 부분은 다시 보내지 않도록 최적화됩니다.
HTTP/2는 HPACK이라는 헤더 압축 알고리즘을 사용합니다. HPACK은 클라이언트와 서버가 동기화된 헤더 테이블을 유지하면서 중복된 헤더를 재사용합니다.
헤더 테이블은 클라이언트와 서버가 공유하는 메모리 공간입니다.
테이블에 이미 저장된 헤더 값은 인덱스 번호로 참조만 하면 됩니다.
헤더가 중복되더라도, 전체 내용을 매번 보내는 것이 아니라
이를 통해, HTTP/1.1에서 매번 같은 헤더를 보내는 비효율을 해결합니다.
HTTP/2는 데이터를 텍스트 기반(HTTP/1.1) 대신 바이너리 형식으로 전송합니다.
바이너리란 0과 1로 이루어진 코드입니다.
바이너리 데이터는 컴퓨터가 직접 이해하고 처리하기 때문에 텍스트보다 빠르게 해석됩니다.
특징 | HTTP/1.1 | HTTP/2 |
---|---|---|
요청 처리 방식 | 한 연결당 하나의 요청 | 한 연결에서 여러 요청 병렬 처리 (멀티플렉싱) |
헤더 | 매 요청마다 전체 헤더 전송 | 헤더 압축 (HPACK) |
데이터 형식 | 텍스트 기반 | 바이너리 기반 |
연결 효율성 | 여러 TCP 연결 사용 (비효율적) | 하나의 TCP 연결 사용 (효율적) |
성능 | 느림 (대기 시간 발생) | 빠름 (병목 현상 감소) |
그렇다면 이제 HTTP/2를 저의 서버에 적용해주도록 하겠습니다.
설정하는 것은 매우 간단했는데요. 서버 인스턴스에 접속하여 nginx 설정만 살짝 변경해주면 끝입니다.
순서는 다음과 같습니다.
# 인스턴스 접속
ssh -i pem.key ubuntu@~~~
# Nginx 설정 파일 편집
sudo vi /etc/nginx/sites-available/default
Before
server{
...
listen [::]:443 ssl ipv6only=on; # managed by Certbot
listen 443 ssl ; # managed by Certbot
...
}
설정 코드 중 위와 같은 부분이 있습니다.
위 부분에서 http2를 넣어줍니다.
After
server{
...
listen [::]:443 ssl http2 ipv6only=on; # managed by Certbot
listen 443 ssl http2; # managed by Certbot
...
}
sudo nginx -t
sudo systemctl restart nginx
curl -I -k --http2 https://thedevlounge.com
4-1. 브라우저에서 아직 http/1.1로 통신된다면 캐시를 한번 비워줍니다.
4-2. 브라우저에서 재확인해봅니다.
Lighthouse에서 재측정을 해보면 다음과 같이 HTTP/2 사용하라는 진단 항목이 없어진 것을 확인하고 통과한 감사에 있는 것을 확인할 수 있습니다.
또한 LCP 시간도 이전(0.5) 대비 0.4초로 0.1초 감소한 것을 확인할 수 있었습니다.
네트워크 탭에서 프로토콜 컬럼과 폭포(waterfall),연결 ID 컬럼들을 활성화시켜보면 HTTP/1.1과 달리 병렬로 요청 응답하는 것을 확인할 수 있고 프로토콜도 h1.1->h2로 변경된 것을 확인할 수 있습니다.
이렇게 HTTP/2로 변경함으로써 Lighthouse에서 진단한 항목 HTTP/2를 사용하세요 부분을 해결할 수 있었고 리소스 요청 응답에 대한 시간을 단축할 수 있었습니다.