Skip to content

HTTP (HyperText Transfer Protocol)

🧩 Reference

  • 프로토콜: 컴퓨터끼리 통신할 때 지켜야 하는 규칙 + 형식

  • TLS Handshake: 클라이언트와 서버가 비대칭키(공개키/개인키)를 이용해, 이후 통신에 사용할 대칭키(세션 키)를 안전하게 합의하는 과정

  • MDN - HTTP 메시지

🧩 아래 정리를 하고 깨달은 점!

  • 왜 refreshToken을 쿠키에 넣는지
    → JS로 접근 못 하는 httpOnly 쿠키로 관리해서, 탈취 위험을 줄이고 자동 전송만 맡기려고.
  • 왜 accessToken은 Authorization 헤더에 넣는지
    → 매 요청마다 내가 명시적으로 어떤 사용자 권한인지 전달하기 위해, 헤더에 Bearer 토큰 형태로 붙인다.
  • 왜 httpOnly 쿠키가 보안적으로 안전한지
    → JS에서 읽을 수 없어서, XSS로 스크립트가 떠도 쿠키 내용을 훔쳐갈 수 없기 때문.
  • 왜 state가 없는 HTTP에서 우리가 토큰을 계속 보내야 하는지
    → 서버는 이전 요청을 기억하지 않으니, “나 누구인지 + 내 권한” 정보를 토큰으로 매번 증명해야 하기 때문.
  • 왜 HTTPS가 없으면 토큰/쿠키가 털리는지
    → 암호화가 없으면 네트워크 중간에서 패킷을 훔쳐볼 수 있어서, Authorization 헤더/쿠키가 그대로 노출되기 때문.

HTTP란?

HTTP는 클라이언트가 항상 먼저 요청을 보내는 요청/응답 모델이다.
서버는 클라이언트에게 먼저 말을 걸 수 없기 때문에, 클라이언트가 매번 요청을 보내야만 응답을 받을 수 있다.

클라이언트와 서버가 데이터를 주고받을 때 사용하는 통신 규약(프로토콜)이며,
HTTP는 상태가 없는(stateless) 프로토콜이다.
즉 서버는 요청 간의 상태를 기억하지 않기 때문에, 인증 정보(토큰 등)를 매 요청마다 포함해서 보내야 한다.

plaintext
Client → (요청) → Server
Client ← (응답) ← Server
  • 브라우저는 요청(Request)을 보내고, 서버는 그에 대한 응답(Response)을 돌려준다.

💡 HTTP는 왜 Stateless일까?

  • HTTP는 요청 1개 = 처리 1번 형식의 단순한 통신 규약이다.
  • 서버는 이전 요청의 상태를 메모리에 저장하지 않는다.
  • 즉, 서버는 "너 누구였지..?" 기억하지 못한다.

그래서 클라이언트는 매번 자신의 신원을 증명해야 하고,
이를 위해 요청마다 아래 정보가 반복해서 포함된다:

  • Authorization: Bearer <accessToken>
  • 또는 브라우저가 자동으로 보내는 Cookie (refreshToken)

💡 결론
HTTP의 stateless 특성 때문에, 로그인한 사용자는 매 요청마다 스스로를 증명해야 한다.
JWT·쿠키·세션 개념이 여기에서 자연스럽게 필요해진다!


HTTP/1.1 vs HTTP/2

HTTP/1.1

HTTP/1.1에서는 보통

  • 요청 1 → 응답 1 → 연결 종료
  • 여러 리소스(HTML, JS, CSS, 이미지 등)를 받아와야 할 때 같은 서버에 대해 여러 연결을 열어야 해서 병목이 발생할 수 있다. → 이미지/JS/CSS가 많을수록 느려짐

이 구조는 정적인 웹에는 충분했지만, 현대 웹(채팅, 알림, 실시간 데이터, 대량 리소스 로딩)에는 한계가 있다.
이런 성능 문제와 한계를 해결하기 위해 HTTP/2가 등장했다.


HTTP/2

HTTP/2는 실시간 통신기술이라기보다는,
동시에 많은 리소스를 더 효율적으로 받아오기 위한 네트워크 성능 최적화 기술이다.

  • 하나의 TCP 연결 내에서 여러 요청을 동시에 처리(멀티플렉싱) → 브라우저 로딩 속도가 크게 빨라짐
  • 한 번의 TCP 연결 안에서 여러 요청을 병렬로 보냄
  • 각 요청은 스트림(stream)이라는 단위로 처리됨
  • 헤더 압축 등 추가적인 최적화 기법 제공

결과적으로, 같은 페이지라도 더 빠르게 로딩된다.


HTTP 실시간 통신의 진화

기본 HTTP는 요청이 있어야 응답하는 1회성 통신이라 실시간에 약하다.
이 한계를 보완하기 위해 등장한 패턴들이 Polling → Long Polling → SSE → WebSocket이다.

Polling

클라이언트가 일정 간격으로 계속 서버에 물어보는 방식이다.

plaintext
Client → "알림 있어?" → Server
Client → "알림 있어?" → Server
Client → "알림 있어?" → Server
...
  • 장점: 구현이(setInterval + fetch 정도로 가능) 쉽다.
  • 단점:
    • 실시간이 아님 (요청 주기만큼 딜레이)
    • 매번 요청으로 인해 트래픽 낭비
    • 서버 부하가 커짐 ("없음" 응답도 계속 보내야 함)

Long Polling

일반 Polling을 개선한 방식으로, 서버가 새로운 데이터가 생길 때까지 응답을 늦게 주는 방식이다.

plaintext
Client → 요청 보냄
Server → 새 이벤트가 생길 때까지 기다림
Server → 이벤트 발생! 응답 전송
Client → 바로 다시 요청
  • 장점:
    • 일반 폴링보다 실시간에 가깝다.
    • 거의 모든 브라우저/서버 환경에서 동작한다.
  • 단점:
    • 연결을 계속 열었다 닫았다 반복
    • 서버 부하가 여전히 크고
    • 채팅/게임처럼 이벤트가 많은 서비스에는 비효율적이다.

SSE (Server-Sent Events)

Polling이 아니라, 서버 → 클라이언트 방향으로 일방적으로 계속 푸시 가능한 방식이다.
클라이언트가 한 번 연결을 열면, 서버가 스트림 형태로 데이터를 계속 push 해준다.

plaintext
Client → 연결 열기 (GET /streams)
Server → "알림 발생!"
Server → "새 글 등록!"
Server → "주식 가격 변경!"
...
  • 장점:

    • 진짜 실시간에 가깝다. (딜레이 거의 없음)
    • 서버 → 클라이언트 방향 push 가능
    • 브라우저 기본 지원 (EventSource API)
    • HTTP 기반이라 방화벽/프록시 통과가 수월하다.
  • 단점:

    • 단방향: 서버 → 클라이언트만 가능하고,
      클라이언트 → 서버는 여전히 fetch / AJAX / fetch API 필요하다.
    • 양방향 채팅에는 부적합하다.
    • 오래된 브라우저(IE)는 미지원

WebSocket (양방향 실시간)

HTTP 한계를 완전히 넘어서, 서버 ↔ 클라이언트 양방향 실시간 통신을 가능하게 하는 기술이다.
초기에는 HTTP 요청으로 핸드셰이크를 하고, 이후에는 WebSocket 프로토콜로 업그레이드되어 동작한다.

plaintext
Client ⇄ Server
(양쪽 모두 먼저 메시지를 보낼 수 있음)
  • 장점:

    • 서버 ↔ 클라이언트 양방향 실시간 통신
    • 채팅, 게임, 공동 문서 편집 등 실시간성이 중요한 서비스에 적합하다.
  • 단점:

    • SSE보다 구현 난이도가 높다.
    • 서버 인프라에 대한 고려가 필요하다.
    • 연결을 계속 유지하므로, 접속자 수가 많으면 서버 리소스 관리가 중요해진다.
  • 사용 예시:

    • 카카오톡, 디스코드, 게임, 협업툴 등 실제 실시간 서비스에서 사용

HTTP 요청(Request)의 구조

구성 요소설명예시
Request Line어떤 메서드로 어떤 URL로 요청하는지GET /users/1 HTTP/1.1
Method서버에게 어떤 동작을 요청할지GET, POST, PUT, DELETE
URL자원의 위치https://api.example.com/users
Header요청에 대한 추가 정보 (토큰, 타입 등)Authorization, Cookie, Content-Type
Body실제 전송하는 데이터JSON, FormData 등

Authorization Header

Bearer Token 방식:

http
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

다른 인증 방식들:

http
Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l  // Base64 인코딩
Authorization: Digest username="user", realm="example.com"
Authorization: API-Key your-api-key-here

왜 Bearer를 쓸까?

  • RFC 6750에서 정의된 표준
  • 이 토큰을 가진 사람(Bearer)에게 권한 부여라는 의미
  • OAuth 2.0에서 표준으로 사용

요청 헤더에서 반드시 이해해야 하는 3대장

    1. Authorization - accessToken을 담는 곳
    http
    Authorization: Bearer eyJhbGciOiJIUzI1Ni...
    1. Cookie - refreshToken이 자동으로 저장되고 자동으로 실리는 곳
    http
    Cookie: refresh_token=abcdefg12345;
    1. Content-Type - 서버에게 Body 해석 방법을 알려줌
    http
    Content-Type: application/json

HTTP 응답(Response)의 구조

구성 요소설명
Status Line성공/실패 코드 (200, 400, 401, 500 등)
Headers서버가 브라우저에게 전달하는 정보
Body실제 응답 데이터

Representation Headers란?

Representation Headers는 응답 Body(콘텐츠) 자체에 대한 정보를 제공한다.
(Content-Type, Content-Length, Last-Modified, ETag 등)

즉, “이 Body는 어떤 형식의 데이터인지, 언제 만들어졌고, 얼마나 크고, 캐싱은 어떻게 해야 하는지” 같은 정보를 담고 있다.


plaintext
Set-Cookie: refresh_token=abcd1234; HttpOnly; Secure; SameSite=Strict
  • 브라우저는 Set-Cookie 헤더를 받는 순간 자동으로 쿠키를 저장하고,
    같은 도메인/경로 규칙에 맞는 요청을 보낼 때 Cookie 헤더에 자동으로 실어 보낸다.
  • 프론트 코드에서 따로 저장 로직을 작성할 필요가 없다.

프론트에서 따로 저장 로직을 작성해야하는 경우는?

  1. accessToken을 localStorage에 저장하고 싶을 때
    → JS 코드로 localStorage.setItem()해야 한다. (Zustand persist가 이 과정을 자동화해 주는 것뿐이다.)

  2. 브라우저에서 접근 가능한 쿠키(non-httpOnly)를 쓰는 경우
    document.cookie = "key=value" 로 저장 가능
    (하지만 보안 문제 때문에 실제 서비스에서는 거의 사용하지 않는다.)

  3. 서버가 쿠키를 주지 않고, 프론트가 직접 상태를 저장해야 할 때
    (예: 로그인 되어 있음 플래그를 단순히 localStorage에 넣는 경우 등)


💡 결론:

  • refreshToken은 서버가 httpOnly 쿠키로 내려주고, 브라우저가 자동 관리한다.
  • 프론트는 refreshToken을 localStorage에 직접 저장하지 않는다.
  • accessToken은 JS 코드(또는 Zustand persist 등)를 통해 메모리/스토리지에 저장하는 경우가 많다.

Set-Cookie가 자동 저장되는 조건

  1. 서버가 Set-Cookie 헤더로 쿠키를 내려줄 것

    http
     Set-Cookie: refresh_token=abc123; HttpOnly; Secure
  2. 도메인/경로 규칙이 올바르게 설정되어 있을 것

    http
    Set-Cookie: refresh_token=abc; Domain=example.com; Path=/
  3. 추가 속성들

    • Secure: HTTPS 연결에서만 쿠키를 전송
    • SameSite: 크로스 사이트 요청 시 쿠키 전송을 얼마나 제한할지 제어

이 조건들을 만족하면, 브라우저는 쿠키를 자동으로 저장하고,
해당 도메인/경로로 요청을 보낼 때 자동으로 Cookie 헤더에 실어서 보낸다.


자주 쓰는 HTTP 메서드

메서드설명사용 예시
GET데이터 조회게시글 목록 보기
POST데이터 생성새 글 작성
PUT전체 수정프로필 전체 수정
PATCH일부 수정닉네임만 수정
DELETE데이터 삭제글 삭제

HTTP Status Code

인증/인가 관련해서 자주 만나는 상태 코드들:

코드의미인증 상황에서
200성공로그인 성공, API 요청 성공
401UnauthorizedAccessToken 만료/없음 → 토큰 재발급 필요
403Forbidden권한 없음 → 다른 사용자 데이터 접근 시도
422Unprocessable Entity로그인 정보 틀림 (이메일/비밀번호 오류)
500Server Error서버 오류

401 vs 403 차이점

  • 401: 누구인지 모르겠어 → 인증 필요
  • 403: 누군지는 알겠는데 권한 없어 → 접근 금지


HTTPS (Hyper-Text Transfer Protocol Secure)

HTTPS가 필요한 이유

  1. 내가 사이트에 보내는 모든 데이터(토큰, 쿠키, 비밀번호 등)가 암호화된다.

    • 대칭키 암호화 + 비대칭키 암호화를 조합해서 사용한다.
    • 중간에서 패킷을 훔쳐보더라도 내용을 알아볼 수 없다.
  2. 접속한 서버가 진짜 서버인지 인증된다. (MITM 공격 방지)

    • CA(공인 인증기관)가 발급한 인증서로 브라우저가 서버의 신원을 검증한다.
    • 사용자가 접속한 곳이 “진짜 우리 서버인지” 확인할 수 있다.
    • 가짜 서버로 토큰/쿠키가 털리는 것을 방지한다.

두 보안 기능이 동작하는 원리

  1. 대칭키 암호화

    • 암호화/복호화에 같은 키를 사용
    • 속도가 빠르지만, 키가 털리면 끝!
  2. 비대칭키 암호화 (공개키/개인키)

    • 비대칭키로 안전한 통신 채널을 만들고 이후 대칭키로 빠르게 암호화하며 통신한다.
      (공개키로 암호화 → 개인키로만 복호화 가능)
    • 키 교환/인증에 강하다.

실제 HTTPS에서는 처음 연결할 때 TLS Handshake 과정에서 비대칭키(공개키/개인키)를 사용해 안전하게 세션 키(대칭키)를 교환하고, 이후 모든 실제 데이터는 이 대칭키로 빠르고 안전하게 암호화해서 통신한다.


정리

💡

  • HTTP는 요청이 있어야 응답하는 stateless 모델이다.
  • 인증 정보(토큰)는 매 요청마다 포함해서 보내야 한다.
  • refreshToken은 보통 httpOnly 쿠키로 관리되고,
    accessToken은 메모리 또는 스토리지(localStorage 등)에 저장해 Authorization 헤더로 보낸다.
  • Set-Cookie는 브라우저가 자동 저장 + 자동 전송하며,
    도메인/경로/보안 속성(Secure, SameSite 등)에 따라 동작이 달라진다.
  • HTTP/1.1의 한계를 보완하기 위해 HTTP/2, SSE/ WebSocket 같은 기술들이 등장했다.
  • HTTPS는 암호화 + 서버 인증을 제공해 토큰/쿠키/비밀번호를 안전하게 지켜준다.

이제 HTTP가 어떻게 동작하는지 이해했으니,
인증에 필요한 쿠키, 스토리지, 세션이 어떻게 작동하는지 살펴보자!