ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [CS] Http란 무엇이고 어떤 특징이 있을까?
    CS 2025. 12. 5. 00:29

    우리가 매일 사용하는 웹 브라우저는 인터넷이라는 거대한 네트워크 위에서 동작합니다. 주소창에 URL을 입력하고 엔터를 치는 순간, 클라이언트는 서버와 통신을 시작합니다. 이때 가장 핵심이 되는 통신 규약이 바로 HTTP(HyperText Transfer Protocol)입니다.

    따라서 개발자라면 HTTP에 대해 잘 알고 있어야 합니다. 단순히 데이터를 주고받는 것을 넘어, 네트워크의 효율성, 보안, 그리고 아키텍처 설계를 이해하는 기반이 되기 때문입니다. 이번 글에서는 HTTP의 정의부터 시작해 그 구조와 특징을 깊이 있게 다뤄보겠습니다.

    1. HTTP란?

    • HTTP(HyperText Transfer Protocol)는 인터넷상에서 데이터를 주고받기 위한 애플리케이션 계층(Application Layer)의 프로토콜입니다.
    • HyperText: 문서와 문서가 링크로 연결되도록 하는 HTML(HyperText Markup Language)을 전송하는 것에서 시작되었습니다.
    • Transfer: 클라이언트와 서버 간에 데이터를 전송(Transfer)하는 규약을 의미합니다.
    • Protocol: 상호 간에 정의된 규칙(Protocol)을 따릅니다.

    초기에는 HTML 문서를 전송하기 위해 설계되었으나, 현재는 웹 브라우저와 웹 서버 간의 통신뿐만 아니라, 모바일 앱과 서버, 서버와 서버 간의 통신 등 인터넷상의 거의 모든 데이터 교환의 기반이 되는 프로토콜로 자리 잡았습니다. 주로 TCP/IP 프로토콜 위에서 동작하며(HTTP/3는 UDP 기반의 QUIC 사용), 80번 포트(HTTP) 또는 443번 포트(HTTPS)를 사용합니다.

    2. 모든 것이 HTTP

    현대의 웹 생태계에서 HTTP가 전송하는 데이터는 단순히 HTML 문서에 국한되지 않습니다. 말 그대로 '모든 것'을 HTTP에 담아 전송합니다.

    • HTML, TEXT: 기본적인 웹 페이지 구조와 텍스트 데이터.
    • Image, Audio, Video: 각종 멀티미디어 리소스 파일.
    • API (JSON, XML): 클라이언트(앱/웹)와 서버 간의 데이터 통신, 또는 마이크로서비스(MSA) 환경에서 서버 간 통신에 사용되는 데이터 포맷.
    • Binary Data: 파일 다운로드/업로드 시 사용되는 이진 데이터.

    과거에는 소켓 통신을 직접 구현하거나 다른 프로토콜을 사용하는 경우가 많았으나, 현재는 범용성확장성 덕분에 사실상 HTTP가 인터넷 통신의 표준으로 자리 잡았습니다. 따라서 개발자는 HTTP 메시지의 구조와 동작 방식을 명확히 이해해야만 효율적인 API 설계와 트러블슈팅이 가능합니다.

    3. HTTP 역사 (History)

    HTTP는 웹의 폭발적인 성장과 함께, 대용량 트래픽을 효율적으로 처리하고 지연 시간(Latency)을 줄이는 방향으로 발전해 왔습니다. 각 버전의 등장은 이전 버전의 기술적 한계를 극복한 결과물입니다.

    HTTP/0.9 (1991년): The One-Line Protocol

    • 초기 버전으로, 매우 단순한 구조를 가지고 있습니다.
    • 특징:
      • GET 메서드만 지원했습니다.
      • HTTP 헤더(Header)가 없었습니다.
      • 응답은 오직 HTML 파일만 전송 가능했습니다.
    • 한계: 확장성이 부족하여 다양한 데이터를 표현하기 어려웠습니다.

    HTTP/1.0 (1996년): 확장성 부여

    • 헤더(Header) 개념이 도입되면서 프로토콜의 유연성이 확보되었습니다.
    • 특징:
      • HTTP 헤더를 통해 버전 정보와 상태 코드(Status Code)를 전송하게 되었습니다.
      • Content-Type 헤더의 도입으로 HTML 외에 이미지, 비디오 등 다양한 포맷 전송이 가능해졌습니다.
    • 한계: 연결 수립 및 종료 비용이 높아 성능상 비효율적이었습니다.

    HTTP/1.1 (1997년): 표준의 정립과 성능 개선

    • 가장 오랫동안 사용된 표준이며, 현재도 많은 시스템의 기본값입니다. (RFC 2068 -> RFC 2616 -> RFC 7230~7235)
    • 주요 개선점:
      • Persistent Connections (Keep-Alive): 지정된 시간 동안 TCP 연결을 유지하여 핸드셰이크 비용을 줄였습니다.
      • Pipelining: 요청에 대한 응답을 기다리지 않고 연속적인 요청을 보낼 수 있게 되었습니다.
    • 한계: HOL Blocking (Head of Line Blocking) 문제가 발생합니다. 앞선 요청의 처리가 늦어지면 후속 요청들이 모두 대기해야 하는 문제입니다.

    HTTP/2 (2015년): 성능 최적화와 이진 전송

    • 구글의 SPDY 프로토콜을 기반으로 표준화되었으며, 성능 향상에 초점을 맞췄습니다.
    • 주요 기술:
      • Binary Framing: 텍스트 기반이 아닌 바이너리(이진) 프레임 단위로 데이터를 전송하여 파싱 속도를 높이고 오류 발생 가능성을 줄였습니다.
      • Multiplexing: 하나의 TCP 연결 내에서 여러 개의 스트림(Stream)을 사용하여, 요청과 응답을 병렬로 처리함으로써 HOL Blocking(애플리케이션 레벨)을 해결했습니다.
      • Header Compression (HPACK): 중복되는 헤더 데이터를 압축하여 전송량을 줄였습니다.

    HTTP/3 (진행 중): TCP의 한계 극복 (UDP 기반)

    • HTTP/1.1과 HTTP/2는 모두 TCP 위에서 동작하기 때문에, TCP 자체의 패킷 손실로 인한 지연 문제(TCP 레벨의 HOL Blocking)는 해결할 수 없었습니다.
    • 이를 해결하기 위해 UDP 기반의 QUIC (Quick UDP Internet Connections) 프로토콜을 도입했습니다.
    • 특징:
      • TCP의 3-way Handshake 과정을 최적화하여 연결 설정 시간을 단축했습니다 (0-RTT 지원).
      • 패킷 손실이 발생해도 해당 스트림에만 영향을 주고, 다른 스트림은 정상 통신이 가능합니다.

    4. Http 특징

    4-1. 클라이언트 - 서버 구조 (Client-Server Architecture)

    HTTP는 요청(Request)과 응답(Response) 구조로 동작하는 대표적인 클라이언트-서버 구조입니다.

    과거의 모놀리식(Monolithic) 하거나 단순한 구조에서는 데이터 처리와 화면 출력이 합쳐져 있는 경우가 많았으나, HTTP 기반의 웹 애플리케이션은 클라이언트(Client)와 서버(Server)라는 두 개의 독립적인 주체로 명확히 분리되어 동작합니다.

    구조적 특징

    • Request & Response: 클라이언트가 먼저 서버에 요청(Request)을 보내면, 서버는 그에 맞는 결과(Response)를 만들어서 응답합니다. 서버가 먼저 클라이언트에게 데이터를 보내는 경우는 없습니다(HTTP/2의 Server Push 같은 최적화 기술 제외).
    • User Agent: 클라이언트는 웹 브라우저, 모바일 애플리케이션 등 사용자를 대신해 요청을 보내는 주체(User Agent)가 됩니다.
    • Origin Server: 서버는 리소스를 보관하고 비즈니스 로직을 수행하는 주체입니다.

    핵심 가치: 관심사의 분리

    이 구조의 가장 큰 의의는 양쪽의 역할이 명확히 분리된다는 점입니다.

    1. 클라이언트(Client):
      • UI/UX 집중: 복잡한 데이터를 어떻게 시각화하고 사용자 경험을 최적화할지에 집중합니다.
      • 사용성: 사용자의 입력을 받고, 서버의 응답을 파싱(Parsing) 하여 렌더링 합니다.
    2. 서버(Server):
      • 비즈니스 로직(Business Logic): 데이터의 생성, 조회, 수정, 삭제(CRUD)와 같은 핵심 로직 처리에 집중합니다.
      • 데이터 관리: 데이터베이스 트랜잭션 관리 및 보안 등을 담당합니다.

    개발 관점에서의 장점

    이러한 구조적 분리는 클라이언트, 서버를 각각 개발하고 개선하는 것을 가능하게 합니다.

    • 클라이언트 개발자는 서버의 내부 로직(DB가 무엇인지, Java 인지 Node.js 인지 등)을 몰라도 API 명세만 알면 개발이 가능합니다.
    • 서버 개발자는 클라이언트 단말이 PC인지 아이폰인지 신경 쓰지 않고, 데이터 처리 효율성과 아키텍처 확장에 집중할 수 있습니다.
    • 트래픽이 폭주할 때, 클라이언트는 그대로 두고 서버만 스케일 아웃(Scale-out) 하여 대응하는 등 유연한 인프라 운용이 가능해집니다.

    4-2. Stateless (무상태성)와 Stateful (상태 유지)

    HTTP는 기본적으로 Stateless(무상태성)을 지향합니다. 하지만 웹 서비스가 복잡해지면서 상태를 유지해야 하는 경우(Stateful)가 생겨났고, 이를 어떻게 처리하느냐가 백엔드 아키텍처의 핵심 과제가 되었습니다.

    Stateful (상태 유지)

    서버가 클라이언트의 이전 요청 상태(Context)를 보존하는 구조입니다.

    • 동작 방식: 클라이언트 A가 데이터를 요청하면, 서버는 그 상태를 메모리나 세션 등에 저장해 둡니다. 이후 클라이언트 A가 다음 요청을 보낼 때, 서버는 저장된 이전 상태를 참조하여 응답합니다.
    • 대표적인 예: TCP 통신(3-way handshake로 연결 상태 유지), 전통적인 세션 기반 로그인.
    • 한계:
      • 확장성(Scalability) 저하: 클라이언트의 상태를 특정 서버가 들고 있기 때문에, 해당 클라이언트의 다음 요청은 반드시 같은 서버가 처리해야 합니다. 이를 위해 로드 밸런서에서 Sticky Session 같은 기술을 강제해야 합니다.
      • 장애 대응(Failover) 취약: 만약 상태를 들고 있던 서버가 다운(Down) 되면, 그 서버에 저장된 클라이언트의 진행 정보(로그인 정보, 결제 진행 중 상태 등)는 모두 증발하며 처음부터 다시 시작해야 합니다.

    Stateless (무상태성)

    서버가 클라이언트의 상태를 전혀 보존하지 않는 구조입니다. HTTP의 기본 원칙입니다.

    • 동작 방식: 서버는 단순히 들어온 요청에 대해 응답만 하고 연결을 끊거나 상태를 잊어버립니다. 따라서 클라이언트는 요청을 보낼 때마다 자신을 식별할 수 있는 모든 정보(Context)를 담아서 보내야 합니다.
    • 장점 :
      • 무한한 서버 확장(Scale-out): 서버가 상태를 저장하지 않으므로, 어떤 서버가 요청을 처리해도 상관없습니다. 트래픽이 폭주할 때 단순히 서버 인스턴스(Replica)를 늘리는 것만으로 대응이 가능합니다.
      • 서버 교체 용이: 갑자기 서버 하나가 죽어도, 다른 서버가 아무런 문제 없이 요청을 이어받아 처리할 수 있습니다.
    • 단점:
      • 데이터 전송량 증가: 클라이언트가 매 요청마다 인증 정보(토큰 등)나 상태 데이터를 포함해서 보내야 하므로 헤더나 바디의 데이터 양이 늘어날 수 있습니다.

    어떻게 설계해야 하는가?

    현대의 백엔드 아키텍처는 최대한 Stateless 하게 설계하는 것을 지향합니다.

    • 어쩔 수 없는 상태 유지: 로그인과 같은 상태 유지가 필수적인 기능은 브라우저의 쿠키(Cookie), 서버의 세션(Session)을 이용하거나, 최근에는 JWT(JSON Web Token) 등을 사용하여 상태 정보를 클라이언트에게 넘기는 방식을 주로 사용합니다.
    • 데이터베이스 활용: 서버 인스턴스끼리 상태를 공유해야 한다면, 웹 서버 메모리가 아닌 Redis 같은 별도의 외부 저장소를 통해 상태를 관리하여 웹 서버 자체의 Stateless 성질을 유지합니다.

    4-3. 비 연결성 (Connectionless)

    HTTP는 기본적으로 비 연결성(Connectionless)을 지향하는 프로토콜입니다.

    클라이언트와 서버가 연결을 맺고 요청을 보낸 후, 응답이 도착하면 즉시 연결을 끊어버리는(Disconnect) 방식을 의미합니다. 이는 채팅이나 게임 서버와 같이 클라이언트와 서버가 연결을 지속적으로 유지(Persistent)하는 방식과 대비됩니다.

    동작 메커니즘

    1. Connect: 클라이언트가 서버에 TCP 연결을 요청합니다 (3-way Handshake).
    2. Request: 연결이 수립되면 클라이언트가 HTTP 요청 메시지를 보냅니다.
    3. Response: 서버는 요청을 처리하고 응답 메시지를 보냅니다.
    4. Close: 응답이 완료되면 서버는 즉시 TCP 연결을 종료합니다.

    비 연결성을 지향하는 이유: 서버 자원 효율성

    HTTP가 처음 설계될 당시, 그리고 지금도 유효한 가장 큰 이유는 최소한의 자원으로 최대한 많은 사용자를 처리하기 위함입니다.

    • 서버 자원 절약: 서버가 클라이언트와의 연결을 계속 유지하고 있다면, 그만큼 메모리나 소켓 리소스를 점유하게 됩니다. 사용자가 아무런 행동을 하지 않고 화면만 보고 있는 시간에도 리소스가 낭비됩니다.
    • 빠른 회전율: 작업을 마친 연결을 즉시 끊어버림으로써, 서버는 가용 리소스를 확보하여 새로운 다른 클라이언트의 요청을 받을 준비를 할 수 있습니다. 덕분에 수천, 수만 명의 동시 접속자가 있는 서비스라도 실제 동시에 처리하는 요청은 그보다 적기 때문에 서비스 운영이 가능합니다.

    한계: TCP/IP 연결 비용 (Overhead)

    하지만 현대의 복잡한 웹 환경에서 비 연결성은 치명적인 단점을 가집니다.

    • 3-way Handshake 비용: TCP/IP 연결을 새로 맺을 때마다 3-way Handshake 과정을 거쳐야 합니다. 이는 물리적인 시간(RTT: Round Trip Time)을 소모하므로 응답 속도(Latency) 저하의 주범이 됩니다.
    • 수많은 리소스: 현대의 웹 페이지는 HTML 하나로 끝나지 않습니다. 자바스크립트(JS), CSS, 이미지 등 수십, 수백 개의 파일을 다운로드해야 합니다.
    • 비효율의 극대화: 만약 비 연결성 원칙을 그대로 따른다면, 수십 개의 파일을 받을 때마다 [연결 -> 요청/응답 -> 종료] 과정을 수십 번 반복해야 합니다. 이는 엄청난 네트워크 오버헤드를 발생시킵니다.

    극복: HTTP/1.1의 등장

    이러한 비 연결성의 비효율을 해결하기 위해 HTTP/1.0의 확장과 HTTP/1.1에서는 Persistent Connections (Keep-Alive) 기술이 표준으로 도입되었습니다. 이는 다음 챕터에서 자세히 다룹니다.

    4-4. 일반적인 연결과 Persistent Connections

    앞서 언급한 비 연결성(Connectionless)의 구조적 한계를 극복하기 위해, HTTP는 연결을 관리하는 방식을 발전시켰습니다. 크게 단기 연결(Short-lived Connections)과 지속 연결(Persistent Connections)로 나뉩니다.

    1) 일반적인 연결 (Short-lived Connections)

    HTTP/1.0의 기본 동작 방식이며, 1개의 요청 당 1개의 TCP 연결을 맺는 구조입니다.

    • 동작 프로세스:
      1. [SYN - SYN/ACK - ACK] (3-way Handshake) -> 연결 수립
      2. Request (HTML 요청) -> Response (HTML 응답)
      3. [FIN - ACK - ...] (4-way Handshake) -> 연결 종료
    • 문제점 (Overhead):
      • 웹 페이지 하나를 렌더링 하기 위해 HTML, CSS, JS, 이미지 등 수십 개의 리소스가 필요합니다.
      • 단기 연결 방식은 리소스 개수만큼 TCP 연결 설정(Handshake)과 해제 과정을 반복해야 합니다.
      • 이로 인해 불필요한 RTT(Round Trip Time)가 증가하고, 서버의 CPU 및 메모리 리소스가 낭비됩니다.

    2) Persistent Connections (지속 연결, Keep-Alive)

    HTTP/1.1부터 표준으로 채택된 방식으로, 하나의 TCP 연결을 통해 여러 개의 요청과 응답을 처리하는 구조입니다.

    • 동작 프로세스:
      1. [3-way Handshake] -> 연결 수립
      2. Request 1 -> Response 1
      3. Request 2 -> Response 2 (연결 끊지 않고 유지)
      4. ... (설정된 시간 또는 횟수만큼 반복)
      5. [4-way Handshake] -> 연결 종료
    • 구현 방법 (Header):
      • HTTP/1.0: 기본이 아니므로 Connection: keep-alive 헤더를 명시해야 합니다.
      • HTTP/1.1: 기본값이므로 별도 설정 없이 동작하며, 연결을 끊고 싶을 때만 Connection: close를 명시합니다.

    3) Persistent Connections의 이점

    백엔드 개발자 입장에서 이 기술이 주는 이점은 단순히 '빠르다' 이상입니다.

    1. 네트워크 지연(Latency) 감소: 핸드셰이크 과정이 생략되므로 초기 연결 비용 외에는 추가적인 RTT가 발생하지 않습니다.
    2. CPU 및 메모리 절약: 서버는 소켓(Socket)을 열고 닫는 시스템 콜(System Call)을 줄일 수 있어 부하가 감소합니다.
    3. TCP 혼잡 제어(Congestion Control) 회피: TCP는 연결 초기에는 패킷을 천천히 보내며 대역폭을 탐색하는 Slow Start 메커니즘을 가집니다. 지속 연결을 사용하면 이미 워밍업 된 연결을 재사용하므로, 후속 요청부터는 최대 속도로 데이터를 전송할 수 있습니다.

    4) HTTP Pipelining (파이프라이닝)

    Persistent Connections의 기능을 극대화하기 위해 등장한 기법입니다.

    • 개념: 클라이언트가 응답을 기다리지 않고(Non-blocking) 연속적으로 여러 요청을 서버로 보내는 기술입니다.
    • 한계: 하지만 서버는 요청이 들어온 순서대로 응답을 처리해야 합니다(FIFO). 만약 첫 번째 요청 처리가 오래 걸리면, 후속 요청들이 모두 대기해야 하는 HOL Blocking(Head of Line Blocking) 문제가 발생합니다. 이 문제는 결국 HTTP/2의 Multiplexing 기술이 등장하는 계기가 되었습니다.

    4-5. HTTP 메시지 구조 (Message Structure)

    개발자가 브라우저 개발자 도구(F12)의 Network 탭이나 Wireshark 같은 패킷 분석 도구를 통해 보게 되는 데이터는 결국 HTTP 메시지입니다.

    HTTP/1.1을 기준으로 HTTP 메시지는 사람이 읽을 수 있는 텍스트(ASCII) 기반으로 구성되어 있습니다. 요청(Request)과 응답(Response)은 모두 시작 라인(Start Line), 헤더(Headers), 공백 라인(Empty Line), 바디(Message Body)라는 4단계의 공통된 구조를 가집니다.

    1) 시작 라인 (Start Line)

    메시지의 가장 첫 번째 줄로, 이 메시지가 요청인지 응답인지에 따라 구성이 달라집니다.

    • 요청 메시지 (Request-Line): 서버에게 무엇을 해야 하는지 알려줍니다.
      • method SP request-target SP HTTP-version CRLF
      • 예시: GET /search?q=hello HTTP/1.1
        • Method: GET, POST 등 서버가 수행해야 할 동작.
        • Path: /search?q=hello (요청하는 리소스의 위치).
        • Version: HTTP/1.1.
    • 응답 메시지 (Status-Line): 서버의 처리 결과를 나타냅니다.
      • HTTP-version SP status-code SP reason-phrase CRLF
      • 예시: HTTP/1.1 200 OK
        • Version: HTTP/1.1.
        • Status Code: 200, 404, 500 등 처리 상태를 나타내는 3자리 숫자.
        • Reason Phrase: OK, Not Found 등 상태 코드를 사람이 이해할 수 있게 짧게 글로 쓴 것.

    2) 헤더 (Headers)

    HTTP 전송에 필요한 모든 메타데이터(Metadata)가 담겨 있습니다.

    필드명: 필드값 형태의 Key-Value 쌍으로 이루어져 있습니다.

    • General Header: 요청과 응답 모두에 적용되는 정보 (예: Date, Connection).
    • Request Header: 요청 시 클라이언트의 정보 (예: Host, User-Agent, Authorization).
      • 주의: HTTP/1.1에서 Host 헤더는 필수입니다. 하나의 서버 IP에 여러 도메인이 연결된 가상 호스팅(Virtual Hosting) 환경을 식별하기 위함입니다.
    • Response Header: 응답 시 서버의 정보 (예: Server, Allow).
    • Representation Header: 바디(Body) 데이터의 타입과 크기 정보 (예: Content-Type, Content-Length).

    3) 공백 라인 (Empty Line / CRLF)

    헤더의 끝을 알리는 매우 중요한 라인입니다.

    헤더와 바디를 구분하기 위해 반드시 한 줄의 공백(CRLF - Carriage Return Line Feed)이 있어야 합니다. 파서(Parser)는 이 공백을 보고 Body를 인식합니다.

    4) 메시지 바디 (Message Body / Payload)

    실제 전송하고자 하는 데이터(Payload)가 들어가는 부분입니다.

    • 요청 시에는 전송할 데이터(JSON, Form Data 등)가 들어갑니다. (GET 요청은 보통 바디가 없습니다.)
    • 응답 시에는 클라이언트가 요청한 리소스(HTML, JSON, 이미지 바이너리 등)가 들어갑니다.
    • 바디의 데이터 형식을 파싱 하기 위해서는 헤더의 Content-Type을 반드시 확인해야 합니다.

    실제 메시지 예시

    [HTTP Request]

    POST /create-user HTTP/1.1
    Host: www.example.com
    Content-Type: application/json
    Content-Length: 35
    Authorization: Bearer xxxxx.yyyyy.zzzzz
    
    {"username": "developer", "age": 28}
    

    [HTTP Response]

    HTTP/1.1 201 Created
    Date: Mon, 27 Jul 2024 12:28:53 GMT
    Server: Apache/2.4.41 (Ubuntu)
    Content-Type: application/json
    Content-Length: 50
    
    {"status": "success", "id": 1024, "message": "created"}
    
Designed by MSJ.