안녕하세요! 프론트엔드 취업을 목표로 포트폴리오를 준비하시면서, 이렇게 MDN 공식 문서까지 꼼꼼히 챙겨 보시는 모습이 정말 훌륭합니다. 이력서에 이런 탄탄한 기본기를 녹여낸다면 면접관들에게 아주 좋은 인상을 줄 수 있을 거예요!
이번에 다룰 주제는 'HTTP 범위 요청(HTTP range requests)'입니다. 포트폴리오 웹사이트에 비디오 플레이어를 구현하거나 대용량 파일을 다루게 된다면 반드시 알아야 하는 아주 유용하고 매력적인 개념이죠. 원본의 내용을 하나도 빠짐없이, 이해하기 쉬운 구어체로 번역해 드릴게요. 중간중간 실무와 면접에 피가 되고 살이 되는 저만의 팁도 팍팍 넣어두었습니다. 자, 시작해 볼까요?
HTTP Range 요청은 서버에게 리소스의 '특정 부분(들)'만 클라이언트로 다시 보내달라고 요청하는 기능입니다.
범위 요청은 임의 접근(random access, 원하는 위치로 바로 이동하는 기능)을 지원하는 미디어 플레이어, 대용량 파일 중 일부만 필요한 데이터 도구, 그리고 사용자가 다운로드를 일시 정지했다가 이어서 받을 수 있게 해주는 다운로드 매니저 등 다양한 클라이언트 환경에서 아주 유용하게 쓰입니다.
💡 강사의 팁: 프론트엔드에서
<video>태그를 사용해서 영상을 재생해 본 적 있으신가요? 영상 중간으로 탐색 바(스크롤)를 훅 넘겼을 때 버퍼링 없이 바로 재생되는 이유가 바로 브라우저가 내부적으로 이 'Range 요청'을 서버로 보내기 때문이에요! 전체 동영상을 다 다운로드할 때까지 기다릴 필요 없이, 사용자가 보고 싶어 하는 특정 시간대의 바이트(byte) 데이터만 쏙쏙 골라서 요청하는 거죠.
Transfer-Encoding)과의 비교 (Comparison to chunked Transfer-Encoding)만약 HTTP 응답에 값이 none이 아닌 Accept-Ranges 헤더가 포함되어 있다면, 해당 서버는 범위 요청을 지원한다는 뜻입니다.
반대로 응답에 Accept-Ranges 헤더가 생략되어 있다면, 서버가 부분 요청을 지원하지 않음을 의미하죠.
범위 요청을 지원하지 않는다면 애플리케이션은 이 상황에 맞춰 동작을 조정할 수 있습니다. 예를 들어, 다운로드 매니저 프로그램은 범위 요청 기능에 의존하는 '다운로드 이어받기(일시 정지/재개)' 버튼을 비활성화할 수 있습니다.
서버가 범위 요청을 지원하는지 확인하려면, 리소스 전체를 다운로드하지 않고 헤더만 검사하기 위해 HEAD 요청을 보낼 수 있습니다.
curl을 사용한다면, -I 플래그를 붙여서 HEAD 요청을 만들 수 있어요:
curl -I [https://i.imgur.com/z4d4kWk.jpg](https://i.imgur.com/z4d4kWk.jpg)
이 명령어는 다음과 같은 HTTP 요청을 생성합니다:
HEAD /z4d4kWk.jpg HTTP/2
Host: i.imgur.com
User-Agent: curl/8.7.1
Accept: */*
이에 대한 응답은 본문(body)을 포함하지 않고 오직 헤더들만 포함하게 됩니다:
HTTP/2 200
content-type: image/jpeg
last-modified: Thu, 02 Feb 2017 11:15:53 GMT
…
accept-ranges: bytes
content-length: 146515
이 응답에서 Accept-Ranges: bytes는 범위를 정의하는 단위로 '바이트(bytes)'를 사용할 수 있음을 나타냅니다 (현재로서는 바이트 외에 다른 단위를 사용하는 것은 불가능합니다).
Content-Length 헤더 또한 매우 유용한데, 만약 우리가 HEAD 대신 GET 메서드를 사용해서 똑같은 요청을 보냈을 때 받게 될 해당 이미지의 '전체 크기'를 알려주기 때문입니다.
서버가 범위 요청을 지원한다는 것을 확인했다면, HTTP 요청에 Range 헤더를 포함시켜서 서버가 반환해 주었으면 하는 문서의 특정 부분(또는 여러 부분들)을 구체적으로 지정할 수 있습니다.
이해를 돕기 위해 curl을 사용해서 리소스의 단일 부분(하나의 범위)을 요청해 보겠습니다.
-H 옵션은 요청에 헤더 줄을 추가해 주는데, 이 경우에는 처음 1024 바이트를 요청하는 Range 헤더를 추가했습니다.
마지막에 있는 --output - 옵션은 바이너리(이진) 출력 결과를 터미널 화면에 그대로 보여주도록 하는 역할을 합니다:
curl [https://i.imgur.com/z4d4kWk.jpg](https://i.imgur.com/z4d4kWk.jpg) -i -H "Range: bytes=0-1023" --output -
발행된 요청의 모습은 다음과 같습니다:
GET /z4d4kWk.jpg HTTP/2
Host: i.imgur.com
User-Agent: curl/8.7.1
Accept: */*
Range: bytes=0-1023
서버는 206 Partial Content (부분 콘텐츠) 상태 코드로 응답합니다:
HTTP/2 206
content-type: image/jpeg
content-length: 1024
content-range: bytes 0-1023/146515
…
(binary content)
여기서 Content-Length 헤더는 이미지 전체의 크기가 아니라, 방금 '요청한 범위의 크기(1024 바이트)'를 나타냅니다.
그리고 Content-Range 응답 헤더는 전체 리소스 중에서 현재 전송된 이 부분 메시지가 도대체 '어디에 속하는지'를 정확히 알려줍니다 (예: 전체 146515 바이트 중 0~1023 바이트 부분).
💡 강사의 팁: 기술 면접에서 HTTP 상태 코드를 물어볼 때
200이나404같은 뻔한 것 말고, "206 상태 코드는 언제 발생하나요?"라고 묻는 면접관들이 종종 있습니다. 이때 "동영상 스트리밍이나 파일 분할 다운로드처럼, Range 헤더를 사용해 데이터의 일부분만 성공적으로 받아왔을 때 반환되는 상태 코드입니다!"라고 대답하시면 아주 좋은 점수를 받으실 수 있을 거예요.
Range 헤더를 사용하면 멀티파트 문서를 통해 한 번에 여러 개의 범위를 요청할 수도 있습니다. 각각의 범위들은 쉼표(,)로 구분해 주면 됩니다.
curl [http://www.example.com](http://www.example.com) -i -H "Range: bytes=0-50, 100-150"
이때 서버는 아래에 보이는 것처럼 206 Partial Content 상태 코드로 응답합니다.
응답에는 Content-Type 헤더가 포함되는데, 뒤이어 멀티파트 바이트 범위(multipart byterange) 형식의 데이터가 따라온다는 것을 나타내줍니다.
경계선 문자열(boundary string, 이 예시에서는 3d6b6a416f9b5)은 본문(body)의 각 부분들을 서로 분리해 주는 역할을 하며, 나뉜 각 부분들은 그들만의 고유한 Content-Type과 Content-Range 필드를 개별적으로 가지게 됩니다:
HTTP/1.1 206 Partial Content
Content-Type: multipart/byteranges; boundary=3d6b6a416f9b5
Content-Length: 282
--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 0-50/1270
<!doctype html>
<html lang="en-US">
<head>
<title>Example Do
--3d6b6a416f9b5
Content-Type: text/html
Content-Range: bytes 100-150/1270
eta http-equiv="Content-type" content="text/html; c
--3d6b6a416f9b5--
리소스를 이어서 받기 위해(예: 다운로드 재개) 추가적인 부분을 요청할 때, 클라이언트는 자신이 마지막 조각을 받은 이후로 서버에 저장된 원본 리소스가 수정되지 않았음을 확실히 보장받아야 합니다.
이럴 때 If-Range HTTP 요청 헤더를 사용하면 범위 요청을 '조건부'로 만들 수 있습니다: 만약 조건이 충족된다면(즉, 파일이 변하지 않았다면), 서버는 요청한 범위를 발행하여 적절한 본문 데이터와 함께 206 Partial Content 응답을 돌려줍니다. 하지만 만약 조건이 충족되지 않는다면(그 사이에 파일이 수정되었다면), 서버는 200 OK 상태 코드와 함께 리소스 '전체'를 다시 처음부터 돌려보냅니다. 이 헤더는 검증자(validator)로 Last-Modified나 ETag 중 하나와 함께 사용할 수 있지만, 두 개를 동시에 사용할 수는 없습니다.
If-Range: Wed, 21 Oct 2015 07:28:00 GMT
💡 강사의 팁: 포트폴리오 만드실 때, 혹시 "대용량 파일 업로드/다운로드 이어받기" 기능을 구현할 계획이 있으신가요? 중간에 네트워크가 끊겨서 다시 다운로드해야 할 때, 이
If-Range를 사용해서 파일이 변경되지 않았는지 확인하고 끊긴 지점부터 다시 Range 요청을 보내는 로직을 구현하시면 "네트워크 통신에 대한 이해도가 매우 깊은 지원자"라는 평가를 받을 수 있습니다!
범위 요청(Range requests)과 관련하여 작업할 때, 주로 마주치게 되는 세 가지 관련된 상태 코드가 있습니다:
Partial Content 상태 코드가 반환됩니다.Requested Range Not Satisfiable 상태 코드가 반환됩니다. 이는 요청한 범위 값 중 그 어떤 것도 실제 리소스의 범위와 겹치지 않는다는 것을 의미하죠. 예를 들어, 요청한 모든 범위의 시작 바이트 위치(first-byte-pos)가 리소스의 전체 길이보다 큰 경우가 이에 해당합니다.OK 상태 코드가 반환되며 응답 본문에는 리소스 전체가 전송됩니다.Transfer-Encoding)과의 비교 (Comparison to chunked Transfer-Encoding) Transfer-Encoding 헤더는 데이터를 조각내어 보내는 청크 인코딩(chunked encoding)을 허용합니다. 이는 서버가 클라이언트에게 방대한 양의 데이터를 전송해야 하는데, 요청 처리가 완전히 끝날 때까지 응답의 총크기(total size)를 미리 알 수 없는 경우에 매우 유용합니다. 서버는 전체 응답이 완성될 때까지 버퍼링(대기)하거나 정확한 총길이를 파악하느라 시간을 끄는 대신, 준비된 데이터 조각들을 클라이언트에게 곧바로 전송해 버리기 때문에 대기 시간(latency)을 크게 개선할 수 있습니다. 범위 요청(Range requests)과 청크 전송(chunking)은 서로 완벽하게 호환되며, 같이 사용하거나 따로 사용할 수도 있습니다.
💡 강사의 팁: 면접에서 가끔 이 둘의 차이점을 묻기도 하는데요. 간단히 정리하자면 이렇습니다.
- Range 요청: "파일의 100번 바이트부터 200번 바이트까지만 딱 잘라서 보내줘!" (클라이언트 주도, 특정 구간 요구)
- Chunked 전송: "파일 전체 크기는 아직 나도 잘 모르겠는데, 일단 준비되는 대로 잘게 쪼개서(chunk) 순서대로 계속 보내줄게!" (서버 주도, 스트리밍 방식 전송)