개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › 네트워크 › HTTP

쿠키

기억하지 못하는 서버에게 매번 이름표를 내민다

HTTP는 요청과 요청을 이어 주지 않습니다. 서버는 앞서 온 요청이 같은 브라우저에서 왔는지 알 수 없습니다. 그래서 로그인에 성공하면 서버가 응답에 작은 값(쿠키)을 실어 보내고, 브라우저는 그것을 보관했다가 같은 사이트로 가는 요청마다 자동으로 붙여 보냅니다.

일부만 실행으로 확인 · 브라우저와 시험 서버로 확인(브라우저 종료 시 삭제는 미확인)

이 실습에 나오는 개념

쿠키
서버가 응답에 실어 보내면 브라우저가 저장해 두었다가 같은 사이트에 요청할 때마다 자동으로 함께 보내는 작은 데이터다. HTTP는 요청 사이의 상태를 기억하지 않으므로, 로그인 상태처럼 이어져야 하는 정보를 유지하는 데 쓰인다. 사용자의 브라우저에 저장되므로 내용을 보거나 바꿀 수 있다는 점을 전제로 사용해야 한다.
HTTP와 무상태(Stateless)
HTTP는 클라이언트가 요청을 보내면 서버가 응답하는 방식의 통신 규약이다. 무상태라는 것은 서버가 이전 요청의 내용을 기억하지 않아 각 요청이 그 자체로 처리에 필요한 정보를 모두 담고 있어야 한다는 뜻이다. 서버가 상태를 기억하지 않으므로 어느 서버가 요청을 받아도 되어 서버를 늘리기 쉽다. 로그인 상태처럼 이어져야 하는 정보는 쿠키, 세션, 토큰으로 유지한다.
HttpOnly, Secure, SameSite
쿠키를 보호하는 속성들이다. HttpOnly는 자바스크립트가 쿠키를 읽지 못하게 해 스크립트 삽입 공격으로 쿠키가 탈취되는 것을 막는다. Secure는 HTTPS 연결에서만 쿠키를 전송하게 한다. SameSite는 다른 사이트에서 시작된 요청에 쿠키가 함께 전송되는 것을 제한해 CSRF 공격을 줄인다.
세션
사용자의 상태를 서버 쪽에 저장하고, 클라이언트에는 그 저장소를 가리키는 식별자(세션 ID)만 쿠키로 전달하는 방식이다. 실제 정보가 서버에 있으므로 클라이언트가 내용을 고칠 수 없다(세션 ID를 빼앗기는 것은 다른 문제다). 서버가 여러 대일 때는 어느 서버로 요청이 가도 같은 세션을 찾을 수 있도록 Redis 같은 공용 저장소에 세션을 두는 구성이 일반적이다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. 로그인 요청입니다. 첫 요청이라 브라우저에는 이 사이트의 쿠키가 없습니다. 서버는 본문의 아이디와 비밀번호를 확인합니다.
  2. 서버가 응답에 Set-Cookie: sid=abc123; Path=/; HttpOnly 헤더를 실어 보냅니다. 브라우저는 이 값을 shop.example의 쿠키로 보관합니다. 서버는 "abc123은 회원 7"이라고 자기 쪽에 적어 둡니다.
  3. shop.example/orders 요청입니다. 브라우저가 Cookie 헤더에 sid를 자동으로 붙였습니다. 코드에서 따로 넣어 주지 않아도 붙습니다. 서버는 abc123을 찾아 회원 7의 요청임을 압니다.
  4. shop.example/shop/cart 요청입니다. 이 경로는 Path=/에 해당하므로 쿠키가 붙습니다.
  5. 다른 사이트 other.example로 가는 요청입니다. shop.example이 준 쿠키는 붙지 않습니다. 쿠키는 그것을 내려 준 호스트(shop.example)로 가는 요청에만 붙습니다(Domain 속성을 쓰면 범위가 넓어집니다).
  6. shop.example/shop/cart 페이지에서 자바스크립트가 document.cookie를 읽습니다. HttpOnly 쿠키는 스크립트에 보이지 않습니다. 그래도 요청에는 계속 자동으로 붙습니다.

실행 결과

코드

@PostMapping("/login")
ResponseEntity<Void> login(@RequestBody LoginRequest req) {
    Member m = memberService.check(req.id(), req.password());
    String sid = sessionStore.create(m.getId());        // "abc123" → 회원 7

    ResponseCookie cookie = ResponseCookie.from("sid", sid)
        .path("/")
        .httpOnly(true)
        .build();
    return ResponseEntity.ok()
        .header(HttpHeaders.SET_COOKIE, cookie.toString())
        .build();
}
// 응답 헤더: Set-Cookie: sid=abc123; Path=/; HttpOnly

이 모형의 전제

Path에 따라 붙는 경로, HttpOnly 쿠키가 document.cookie에 보이지 않는 것, Max-Age가 지난 쿠키가 사라지는 것, 호스트가 다른 주소로는 쿠키가 가지 않는 것은 실제 브라우저와 시험용 서버로 확인한 결과와 같습니다. 브라우저를 닫을 때 세션 쿠키가 지워지는 것은 표준 동작을 따른 것이며 직접 실행해 확인하지는 않았습니다. "이전 탭 복원" 기능을 켠 브라우저는 세션 쿠키를 되살리기도 합니다. Secure와 SameSite, Domain 속성은 이 실습에서 다루지 않았습니다.

앞 실습: HTTP/1.1, 2, 3 · 다음 실습: HTTP 캐시와 ETag

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요