# HTTP
- HTTP(HyperText Transfer Protocol): 클라이언트와 서버 간 데이터를 주고받기 위한 애플리케이션 계층 프로토콜
- 초기에는 HTML 문서 전송 목적으로 설계되었으나, 현재는 텍스트, 이미지, 영상, JSON, XML 등 거의 모든 형태의 데이터 전송에 사용됨
- 요청(Request) / 응답(Response) 구조로 동작
- 기본적으로 무상태(Stateless) 프로토콜
- 기반 프로토콜(Underlying Protocol)은 특정 프로토콜이 동작하기 위해 아래 계층에서 사용하는 프로토콜이다.
- TCP: HTTP/1.1, HTTP/2의 기반 프로토콜
- UDP: HTTP/3의 기반 프로토콜 (정확히는 UDP 위에서 동작하는 QUIC 프로토콜을 사용)
- HTTP는 클라이언트 서버 구조로 데이터를 주고받는다.
- 클라이언트는 서버에 요청을 보내고 대기
- 서버는 요청에 대한 결과를 만들어 응답
- 무상태 프로토콜 (Stateless)
- 서버가 클라이언트의 상태를 보존하지 않음
- 어느 서버가 요청을 받아도 처리 가능하므로 수평 확장(Scale Out)이 용이함
- 서버가 맥락을 기억하지 않으므로, 클라이언트는 매 요청마다 필요한 정보를 전부 담아 전송해야 함
- 쿠키, 세션, JWT 토큰 등으로 보완
- TCP는 HTTP의 기반 프로토콜로서 TCP가 제공하는 전송 기능을 HTTP가 빌려서 사용할 뿐이다.
- 연결 지향 / connectionless에 대해서는 관심사가 서로 다르다.
- TCP는 데이터 유실없이 전송이 되었는지, HTTP는 요청 응답의 형식과 의미에 관심이 있기 때문에 요청 응답 사이클이 끝나면 논리적 연결을 끊는다.
- HTTP 메시지는 TCP 연결 위에서 송수신된다. HTTP 버전에 따라 TCP 연결을 맺는 방식이 달라진다.
- HTTP/1.0: 요청마다 TCP 연결을 새로 수립하고 종료 → 3-way handshake 비용 반복 발생
- HTTP/1.1: Keep-Alive로 TCP 연결 재사용. 단, 순차 처리로 인한 HOL Blocking 발생 → 브라우저가 도메인당 최대 6개의 병렬 TCP 연결로 우회
- HTTP/2: 멀티플렉싱 도입. TCP 연결 1개로 여러 요청을 동시에 처리
- HTTP의 비연결성 특성으로 초단위 이하 시간 아래 빠르게 응답을 처리한다.
- 서버 자원을 점유하지 않기 때문에 효율적인 자원 사용이 가능하다.
- HTTP 비연결성의 한계
- 요청마다 TCP 연결을 새로 수립해야 하므로 3-way handshake 오버헤드가 반복 발생
- 현대 웹은 HTML, CSS, JS, 이미지 등 수많은 리소스를 한 번에 받아야 하므로 연결 반복 수립 비용이 큼
- 해결책: HTTP Persistent Connections (지속 연결)
- TCP 연결을 재사용하여 handshake 오버헤드를 줄임
- HTTP/1.1부터 기본 적용 (Keep-Alive)
- OS가 소켓(클라이언트 IP:Port ↔ 서버 IP:Port)으로 TCP 연결을 식별하고, Keep-Alive 설정(timeout, max) 범위 내에서 해당 연결을 재사용해 여러 HTTP 요청을 처리한다.
- HTTP 메시지의 구성은 다음과 같다.
- 시작 라인
- 요청:
METHOD request-target HTTP-version CRLF- 예:
GET /search?q=hello HTTP/1.1 - request-target은 대상 리소스의 경로로, path+query에 해당한다.
- 예:
- 응답:
HTTP-version status-code reason-phrase CRLF- 예:
HTTP/1.1 200 OK
- 예:
- 요청:
- HTTP 헤더
- HTTP 전송에 필요한 모든 부가정보
- 형식:
field-name: field-value CRLF - HTTP 전송에 필요한 모든 부가 정보를 담으며, 필요 시 임의의 헤더를 추가할 수 있음
- 요청 헤더:
Host(필수),User-Agent,Accept,Authorization등 - 응답 헤더:
Server,Location(3xx 리다이렉션 시) 등 - 공통 헤더:
Content-Type(바디 미디어 타입),Content-Length(바디 크기),Connection,Cache-Control등
- 메시지 바디
- 실제 전송할 데이터
- 시작 라인
// 요청
GET /search?q=hello HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0
Accept: text/html
// 응답
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 1234
<html>...</html>
# HTTP 메서드
- API URI는 리소스 식별이 중요하다.
/read-member와 같은 동작은 중요하지 않다.- 회원이라는 리소스만 식별하면 된다.
/members/membersPOST/members/${id}GET/members/${id}PUT, PATCH/members/${id}DELETE
- 리소스와 행위를 분리하는 것이 중요하다.
- 행위를 표현하는 것이 HTTP 메서드이다.
- HTTP 메서드는 다음과 같다.
- GET: 리소스 조회
- POST: 요청 데이터 처리 / 등록에 주로 사용
- PUT: 리소스 대체, 없으면 생성
- POST는 id를 만들어서 반환해주지만, PUT은 클라이언트가 이미 저장 위치 및 아이디를 알고있음
- 리소스를 완전히 대체하므로 서버상에 저장된 특정 필드가 삭제될 수 있다.
- PATCH: 리소스 부분 변경
- DELETE: 리소스 삭제
- 위의 메서드 외에 다양한 메서드가 더 존재한다.
- HEAD: GET과 동일하지만 메시지를 제외하고 헤더만 반환
- OPTIONS: 대상 리소스에 대한 통신 가능 옵션을 확인 (CORS에서 주로 사용)
- CONNECT: 대상 리소스로 식별되는 서버에 대한 터널 설정 (HTTPS 통신 시 브라우저가 프록시에게 내부적으로 쓰는 것)
- TRACE: 대상 리소스 경로를 따라 메시지 루프백 테스트 수행 (경로 디버깅, 보안 취약점으로 대부분 막아놓음)
- HTTP 각 메서드는 속성들이 존재한다. 메서드별로 해당 속성을 만족하는지는 다르다.
- 안전성
- 호출해도 리소스를 변경하지 않는것
- 멱등성
- 몇번 호출하든 결과가 같음
- GET, PUT, DELETE는 동일. PUT은 결과를 대체, DELETE는 삭제하기 때문. POST는 중복하여 특정 프로세스를 수행할 수도 있음
- 멱등성은 GET 중간에 다른 요인의 개입으로 결과가 변경되는 것까지 고려하지는 않음
- 캐시가능성
- 결과 리소스를 캐시해서 사용해도 되는가
- GET, HEAD, POST, PATCH는 가능
- 실제로는 GET, HEAD정도만 사용
- POST PATCH는 본문 내용까지 캐시 키로 고려해야하는데, 구현이 어렵다.
- HTTP 헤더, 메서드, URL이 같더라도 메시지 바디가 다르면 다른 메시지임.
- 메시지 바디까지 조회하며 메시지 동일성 판단을 구현하기가 어려움.
- 안전성
# HTTP 메서드 활용
- 클라이언트에서 서버로 데이터를 전송하는 방식은 크게 두가지가 있다.
- GET + 쿼리 파라미터를 통해 데이터 전송
- 정렬 필터, 검색어 기능에 사용
- 메시지 바디를 통한 전송
- POST, PUT, PATCH
- 리소스 등록 및 변경 등
- GET + 쿼리 파라미터를 통해 데이터 전송
- 정적, 동적 데이터 조회 및 HTML Form 태그를 통한 데이터 전송, HTTP API를 통한 데이터 전송등의 상황이 존재한다.
- HTTP form 데이터 전송
- POST, GET 전송 가능
Content-Type: application/x-www-form-urlencoded사용- form 내용을 메시지 바디에 담아 전송
Content-Type: mulitpart/form-data를 사용하면 바이너리 데이터 전송 가능
- HTTP API 데이터 전송
- 자바스크립트를 통한 통신에 사용
- 서버 to 서버, 앱 클라이언트 통신
- POST, PUT, PATCH: 메시지 바디를 통해 데이터 전송
- GET: 조회 / 쿼리 파라미터로 데이터 전달
Content-Type: application/json주로 사용
# HTTP 상태코드
- 클라이언트가 보낸 요청 처리상태를 응답에서 알려주는 기능
- 1XX(Informational): 요청이 수신되어 처리중
- 2XX(Successful): 요청 정상 처리
- 3XX(Redirection): 요청을 완료하려면 추가 행동이 필요
- 4XX(Client Error): 클라이언트 오류. 잘못된 문법 등으로 서버 요청 처리에 실패
- 5XX(Server Error): 서버 오류, 서버가 요청 처리에 실패
- 클라이언트가 인식못하는 상태코드가 서버로부터 반환되어도 클라이언트에서는 상위 상태코드로 해석하여 처리한다.
- 273 코드 -> 클라이언트 인식 못함 -> 2XX로 정상 처리
# 2XX
- 200: 요청 성공
- 201: Created, 요청 성공하여 새로운 리소스가 생성
- 202: Accepted, 요청 접수되었지만 처리가 완료되지 않음. 스케줄된 배치처리에서 사용
- 204: No Content, 서버가 요청을 잘 수행했지만 응답 페이로드 본문에 보낼 데이터가 없음
# 3XX
- 3XX: 요청 완료를 위한 유저 에이전트의 추가 조치 필요
- 웹 브라우저는 3XX 응답결과에 Location 헤더가 있으면 Location 위치로 자동 이동함.
// 서버 응답
HTTP/1.1 301 Moved Permanently
Location: https://www.new-url.com/search
- 301 Moved Permanently 상태코드와 Location 정보를 클라이언트에 보내면 자동으로 리다이렉트를 한다.
- 리다이렉션에는 여러 타입이 있다.
- 영구 리다이렉션: 특정 리소스 URI가 영구적으로 이동 (/members -> /users)
- 일시 리다이렉션: 일시적인 변경
- 주문 완료 후 리다이렉트 시나리오에 자주 사용
- POST -> 302 + Location -> 주문 완료 페이지 GET
- 위와 같은 패턴을 PRG 패턴이라고 한다. (POST - REDIRECT - GET)
- 영구 리다이렉션
- 리소스 URI가 영구적으로 이동
- 301 Moved Permanently: 리다이렉트시 요청 메서드가 GET으로 변경, 본문이 제거될 수 있음 (사실은 해도되고 안해도됨)
- 308 Permanently Redirect: 301과 같은 기능, 리다이렉트시 요청 메서드 및 본문 유지 (POST -> 리다이렉트 -> POST)
- 일시 리다이렉션
- 302 Found: 리다이렉트시 GET으로 메서드가 변경되고 본문이 제거될 수 있음 (사실은 해도되고 안해도됨)
- 307 Temporary Redirect: 302와 같은 기능, 요청 메서드와 본문 유지
- 303 See Other: 리다이렉트시 GET으로 메서드 변경 (명시적으로 해야됨.)
- 기타 리다이렉션
- 300, 안씀
- 304: Not Modified
- 캐시 목적으로 사용
- 클라이언트에 리소스가 수정되지 않았음을 알림. 응답을 받고 클라이언트가 로컬에 저장된 캐시를 재사용
- 304는 캐시 사용을 위해 메시지 바디를 포함하면 안됨
- 조건부 GET, HEAD 요청시 사용
- Last-Modified와 같은 값과 함께 GET요청을 날리는 것이 조건부 GET
- If-Modified-Since, If-None-Match(Etag)와 같은 헤더도 존재
# 4XX
- 클라이언트 요청에 잘못된 문법 등으로 서버 요청이 실패
- 오류원인이 클라이언트에 있기 때문에 똑같은 재시도가 실패함
- 400 Bad Request
- 클라이언트가 잘못된 요청을 해서 서버가 요청을 처리할 수 없음
- 요청 구문, 메시지 등 오류
- 요청 파라미터 형식 / API 스펙이 맞지 않을때
- 401 Unauthorized
- 인증되지 않음
- WWW-Authenticate 헤더와 함께 인증방법 설명
- 인증 (Authentication): 본인이 누구인지 확인
- 인가 (Authorization): 권한 부여
- 403 Forbidden
- 인증 자격은 있지만 접근 권한이 불충분
- 어드민 관리
- 404 Not Found
- 요청 리소스가 서버에 없음
- 클라이언트에 권한이 없어 요청을 숨기고 싶을때
# 5XX
- 서버 문제로 오류 발생
- 재시도시 성공할 수도 있음
- 500 Internal Server Error
- 서버 내부 문제로 오류 발생
- 503 Service Unavailable - 서버 일시 과부하 / 예정된 작업으로 요청 처리가 불가능 - Retry-After 헤더로 얼마 뒤에 복구되는지 보낼 수 있음
# HTTP 헤더
- HTTP 헤더는 key-value 구조로 이루어진다.
field-name: field-value
- HTTP 전송에 필요한 모든 부가 정보를 담는다.
- 메시지 바디 크기, 내용, 압축, 인증, 서버 정보, 캐시 정보 등
- 필요 시 임의의 헤더를 key-value 형태로 직접 정의할 수 있다.
- RFC7230 이후 HTTP 바디 관련 표준이 변경되었다.
- 메시지 바디(페이로드)를 통해 표현 데이터를 전달
- 표현: 요청/응답에서 실제 전달할 데이터 (HTML, JSON 등)
- 표현 헤더: 표현 데이터를 해석하는 데 필요한 정보 제공 (데이터 유형, 길이, 압축 방식 등)
- 표현 헤더 구성
Content-Type: 표현 데이터의 형식 (예:text/html,application/json)Content-Encoding: 표현 데이터의 압축 방식 (예:gzip), 읽는 쪽에서 인코딩 정보로 압축을 해제Content-Language: 표현 데이터의 자연 언어 (예:ko,en)Content-Length: 표현 데이터의 길이 (바이트 단위)
- 협상 (Content Negotiation): 클라이언트가 선호하는 표현을 서버에 요청하는 헤더
- Accept: 클라이언트가 선호하는 미디어 타입 (text/html, application/json)
- Accept-Charset: 클라이언트가 선호하는 문자 인코딩 (utf-8)
- Accept-Encoding: 클라이언트가 선호하는 압축 인코딩 (gzip, deflate)
- Accept-Language: 클라이언트가 선호하는 자연 언어
- q값(0~1)으로 우선순위 지정 가능, 생략 시 1(최우선)
- 예:
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8
- 예:
- 구체적으로 명시할수록 우선순위가 높다.
text/*,text/plain;format=flowed,*/*text/plain우선순위가 가장 높음
- q값이 명시되면 q값이 우선이다.
- 전송 방식에 따른
- 단순 전송: Content-Length 사용하여 컨텐츠 크기 명시
- 압축 전송: Content-Encoding으로 인코딩 정보 표시, Content-Length로 컨텐츠 크기 명시
- 분할 전송:
Transfer-Encoding, 데이터를 전송할 때 적용하는 인코딩 방식을 명시- 전체 크기를 모를 때 chunked로 분할 전송하는 용도가 주된 사용 사례, 전체 크기를 모르기 때문에 Content-Length를 사용하면 안됨
- HTTP/2에서는 사용되지 않음 (프레임 단위로 자체 처리)
- gzip, deflate 등으로 압축 후 분할전송을 하도록 설정할수도 있다.
- 범위 전송: Range, Content-Range
- 파일 다운로드가 끊겼을때 이어받기 하는 경우 사용한다.
- 요청:
Range: bytes=1001-2000 - 응답:
Content-Range: bytes 1001-2000/5000
- 요청:
- 파일 다운로드가 끊겼을때 이어받기 하는 경우 사용한다.
HTTP/2에 분할전송이 필요없는 이유
- HTTP/1.1
- 텍스트 기반 프로토콜이라 데이터 경계를 명시적으로 표시해줘야 함 → Transfer-Encoding: chunked로 해결
- HTTP/2
- 바이너리 프레임 기반 프로토콜 모든 데이터가 이미 프레임 단위로 분할됨 → chunked가 필요 없음
# 일반 정보
- From: 유저 에이전트 이메일 정보 (잘 사용 안됨)
- Referer: 이전 웹 페이지 주소
- A -> B로 이동할때 B 요청시 Referer: A를 포함하여 요청
- 유입경로 분석 / 요청에서 사용
- User-Agent: 유저 에이전트 애플리케이션 정보
- 웹브라우저 정보 등 포함
- 통계, 브라우저 장애 파악
- Server: 요청을 처리하는 오리진 서버 소프트웨어 정보
- 요청을 처리하는 origin 서버 소프트웨어 정보
- 응답에서 사용
- Date: 메시지가 생성된 날짜
- 메시지 발생 시각 및 날짜, 응답에서 사용
# 특별 정보
Host: 요청한 호스트의 도메인 정보 (요청 헤더 / 필수)- 하나의 서버(IP)에 여러 도메인이 운영되는 경우, 서버가 어느 도메인으로 온 요청인지 구분하기 위해 사용
- 예: 같은 IP에
aaa.com,bbb.com이 운영 중일 때 Host 헤더로 어느 도메인의 요청인지 식별
Location: 리다이렉션 대상 URL- 3XX 응답 시 이동할 URL 명시
- 201(Created) 응답 시 생성된 리소스 URL을 나타내기도 함
Allow: 해당 리소스에서 허용되는 HTTP 메서드 목록- 405(Method Not Allowed) 응답 시 반드시 포함해야 함
- 예:
Allow: GET, HEAD, PUT
Retry-After: 클라이언트가 다음 요청까지 대기해야 하는 시간- 503(Service Unavailable) 응답 시 서비스 복구 시점을 알려줄 수 있음
- 날짜 표기:
Retry-After: Fri, 31 Dec 2026 23:59:59 GMT - 초 단위 표기:
Retry-After: 120
# 인증
- Authorization: 클라이언트 인증 정보를 서버에 전달
- Basic XXXX...
- Bearer XXXX....
- WWW-Authenticate: 리소스 접근시 필요한 인증 방법 정의
- 401 Unauthorized 응답과 사용
# 쿠키
Set-Cookie: 서버 → 클라이언트 쿠키 전달 (응답 헤더)Cookie: 클라이언트 → 서버 쿠키 전달 (요청 헤더)- HTTP는 무상태 프로토콜이므로 쿠키를 활용해 클라이언트를 식별할 수 있다.
- 쿠키 동작 흐름
- 서버가
Set-Cookie로 쿠키를 응답에 포함 - 브라우저가 쿠키 저장소에 저장
- 이후 요청 시 해당 도메인/경로에 맞는 쿠키를 자동으로
Cookie헤더에 포함하여 전송
- 서버가
- 주요 속성
expires/max-age: 쿠키 만료 시간 (없으면 브라우저 종료 시 삭제)domain: 쿠키를 전송할 도메인 범위, 생략시 현재 문서 기준 도메인만 적용 (example.org에 적용되는 경우dev.example.org는 적용 안됨)path: 쿠키를 전송할 경로 범위, 지정한 경로 포함 하위 경로 페이지만 쿠키 접근secure: HTTPS 연결에서만 전송- 쿠키는 기본적으로 HTTP / HTTPS를 구분하지 않고 전송함.
- Secure 적용시 HTTPS인 경우에만 전송
httpOnly: JS에서 접근 불가 (XSS 방어)document.cookie로 접근 불가
SameSite: Cross-Site Request Forgery(XSRF) 공격 방지.- 요청 도메인과 쿠키에 설정된 도메인이 같은 경우에만 쿠키 전송
- 세션 쿠키: 만료날짜 생략시 브라우저 종료까지만 유지
- 영속 쿠키: 만료날짜 입력시 해당 날짜까지 유지
# HTTP 캐시와 조건부 요청
- 캐시가 없을때 데이터가 변경되지 않아도 매 요청마다 네트워크를 통해 새로 데이터를 다운로드 받아야 한다.
- 브라우저 로딩 속도가 느려지고, 느린 사용자 경험은 서비스에 악영향을 끼친다.
- 캐시 설정 관련 헤더를 알아보자.
cache-control: max-age=60- 첫 요청시 응답결과를 캐시에 저장
- 다음 요청시 캐시에서 데이터를 조회
- 만약 시간 초과시 클라이언트에서 무효한 값으로 판단하고 새로 서버에 요청
- 캐시 만료 후에도 서버에서 데이터를 변경하지 않은 상황도 존재한다.
- 이 경우 서버의 데이터 전체를 다시 보내는것이 아니라 클라이언트 캐시를 다시 사용하는 것이 좋다. 이를 위한 방법들이 존재한다.
Last-Modified값을 서버에서 응답으로 보내준다. 클라이언트 캐시 만료 시 Last Modified값을 GET 헤더에 추가하여, 서버와 비교하여 동일한 경우 클라이언트 캐시를 그대로 사용한다.- 이때 서버는 304 Not Modified를 응답으로 보내준다.
- 1초 미만 단위로 캐시 조정은 불가능하다.
- 날짜 기반 로직을 사용해야 한다.
- 데이터 수정 결과가 자잘해서 캐시를 유지하고 싶거나 수정 결과가 동일한 경우에도 여전히 캐시가 갱신되게 된다.
- 위의 한계를 해소하기 위해
Etag방법을 제공한다.- 캐시용 데이터에 임의의 버전 이름을 달아둔다.
- 해당 태그값을 보내서 같으면 유지하고, 다르면 다시 받는다.
- 캐시 제어로직을 서버에서 자체적으로 관리할수있다.
- 캐시 제어 헤더
- Cache-Control: 캐시 제어
Cache-Control: max-age: 캐시 유효시간, 초단위Cache-Control: no-cache: 데이터는 캐시해도 되지만 요청시마다 서버에 검증 후 사용Cache-Control: no-store: 캐싱 금지
- Pragma: 캐시 제어 (하위호환)
Pragma:no-cache, HTTP 1.0 하위호환
- Expires: 캐시 유효 기간 (하위호환)
expires: 날짜캐시 만료일을 정확한 날짜로 지정- 현대는 Cache-Control 사용
- Cache-Control: 캐시 제어
- 검증헤더
- ETag: 값
- Last-Modified: 날짜
- 조건부 요청 헤더
- If-Match, If-None-Match: Etag
- If-Modified-Since, If-Unmodified-Since: 마지막 수정날짜
- Cache-Control 캐시 관련 기타 지시어
Cache-Control: public: 응답이 퍼블릭 캐시에 저장 가능Cache-Control: private: 프라이빗 캐시에만 저장해야 함. (기본값) 사용자만을 위한 것- HTTPS 기반 통신이면 프록시에서 데이터를 채가도 복호화 불가
Cache-Control: s-maxage: 프록시 캐시에만 적용되는 max-age- 프록시들은 자신이 공유 캐시임을 인지하고 있음.
Age: 60: HTTP 헤더로, 오리진 서버에서 응답 후 프록시 캐시 내에 머문 시간을 의미 (프록시 -> 클라이언트 방향)
- 캐시 무효화:
Cache-Control: no-cache, no-store, must-revalidateno-cache: 데이터는 캐시해도 되지만 원 서버에 검증하고 사용no-store: 저장하면 안됨must-revalidate: 캐시 만료후 최초 조회시 원서버에 검증 필요- 원서버 접근 실패시 504 에러가 발생해야함
- 만료된 캐시는 검증없이 쓰지 못하도록 강제
- no-cache의 경우 서버 불능 상태에서 만료된 캐시를 그대로 쓰는 경우가 있음.