Spring/강의

[📙 숙련 Spring] 2-2. Cookie, Session

가지코딩 2025. 5. 16. 21:40

📙 목차

  1. Cookie
  2. Cookie 사용 예제
  3. Session
  4. Session 사용 예제 1 - Servlet
  5. Session 사용 예제 2 - Spring

1. Cookie

Cookie (쿠키)

  • 웹 서버가 사용자의 웹 브라우저에 저장하는 작은 데이터 조각
  • 클라이언트와 서버 간의 상태 정보를 유지하거나 추적하기 위해 사용된다.

 

Cookie 특징

  • 사용자의 상태 정보(예: 로그인 정보, 설정 값 등)를 클라이언트 측에 저장한다.
  • HTTP가 기본적으로 상태 비저장(stateless)이기 때문에, 상태 유지를 위해 사용한다.
  • 서버가 응답 헤더에 Set-Cookie를 보내면, 브라우저가 이를 저장하고 이후 요청 시 Cookie 헤더로 함께 전송한다.

 

Cookie 기본 구조 (HTTP Header 기준)

Set-Cookie: <name>=<value>; <attribute1>; <attribute2>; ...
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; Max-Age=3600; SameSite=Lax

 

 

Cookie 구성 요소

구성 요소 필수 설명
Name ✔️ 쿠키의 이름이다. 예: sessionId
Value ✔️ 쿠키에 저장할 값이다. 예: abc123
Path 이 경로 이하의 요청에서만 쿠키가 전송된다.
ex: /, /user기본값은 현재 경로이다.
Domain 쿠키를 전송할 도메인을 지정한다.
ex: .example.com으로 설정하면 서브도메인에도 전송된다.
기본값은 현재 요청의 호스트이다.
Expires 쿠키의 만료 날짜를 명시적으로 설정한다.
형식: Wdy, DD Mon YYYY HH:MM:SS GMT
ex: Wed, 21 Oct 2025 07:28:00 GMT
Max-Age 쿠키의 유효 시간을 초 단위로 설정한다.
0이면 즉시 삭제된다.
Expires보다 우선 적용된다.
Secure 이 속성이 있으면 HTTPS에서만 쿠키가 전송된다.
HttpOnly JavaScript에서 document.cookie로 접근할 수 없도록 막는다.
(보안 강화: XSS 방지)
SameSite 쿠키가 cross-site 요청 시 전송되는지를 제어한다.
- Strict: 완전 차단
- Lax: 일부 허용 (GET 링크 등)
- None: 모두 허용 (단, Secure 필수)

 

 

* Cookie의 문제점

  • 쿠키 값은 클라이언트가 임의로 변경할 수 있다.
    • 사용자가 브라우저 개발자 도구(F12) → Application → Cookies에서 직접 값 수정 가능
    • 예: userId 값을 해커가 다른 유저로 변경 가능
  • 쿠키에 저장된 데이터는 네트워크 구간에서 탈취되기 쉽다.
    • userId 같은 민감 정보 저장 위험
    • HTTPS 미사용 시 탈취 위험 극대화
    • 탈취된 쿠키는 만료되지 않는 한 지속적으로 악용 가능

 

* 보안 대처방법

  • 쿠키에 중요한 정보를 직접 저장하지 않는다.
  • 암호화된 토큰(Token)을 저장한다.
    • 서버에서 토큰과 사용자 정보를 매핑하여 인증 처리
    • 토큰 값은 유추하거나 임의로 생성 불가능하게 만든다.
  • 토큰 위조 방지
    • 해커가 임의 토큰을 넣어도 동작하지 않도록 검증 로직 구현
  • 토큰 만료시간을 짧게 설정하여 탈취 피해 최소화
  • 탈취 의심 시 토큰 강제 만료 처리
    • IP, 접속 기기 정보 등으로 의심 활동 감지 후 차단 가능

2. Cookie 사용 예제

쿠키 생성 (Set-Cookie)

@GetMapping("/set-cookie")
public ResponseEntity<String> setCookie(HttpServletResponse response) {
    Cookie cookie = new Cookie("username", "alice");
    cookie.setPath("/");
    cookie.setHttpOnly(true);
    cookie.setMaxAge(60 * 60); // 1시간

    response.addCookie(cookie);
    return ResponseEntity.ok("쿠키가 설정되었습니다.");
}

 

 

쿠키 읽기 (Cookie 헤더에서)

@GetMapping("/get-cookie")
public ResponseEntity<String> getCookie(@CookieValue(name = "username", defaultValue = "guest") String username) {
    return ResponseEntity.ok("저장된 사용자 이름: " + username);
}

 

 

쿠키 삭제 (MaxAge = 0)

@GetMapping("/delete-cookie")
public ResponseEntity<String> deleteCookie(HttpServletResponse response) {
    Cookie cookie = new Cookie("username", null);
    cookie.setPath("/");
    cookie.setMaxAge(0); // 삭제
    response.addCookie(cookie);

    return ResponseEntity.ok("쿠키가 삭제되었습니다.");
}

 

 

실전 예제

@RestController
public class LoginController {

    // 로그인: 쿠키 생성
    @GetMapping("/login")
    public ResponseEntity<String> login(@RequestParam String userId, HttpServletResponse response) {
        Cookie cookie = new Cookie("userId", userId);
        cookie.setPath("/");
        cookie.setMaxAge(60 * 60); // 1시간 유지
        cookie.setHttpOnly(true);  // JS에서 접근 못하게 (보안)
        response.addCookie(cookie);

        return ResponseEntity.ok(userId + "님 로그인 성공");
    }

    // 마이페이지: 쿠키에서 로그인 여부 확인
    @GetMapping("/mypage")
    public ResponseEntity<String> mypage(@CookieValue(name = "userId", required = false) String userId) {
        if (userId == null) {
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("로그인이 필요합니다.");
        }
        return ResponseEntity.ok(userId + "님의 마이페이지입니다.");
    }

    // 로그아웃: 쿠키 삭제
    @GetMapping("/logout")
    public ResponseEntity<String> logout(HttpServletResponse response) {
        Cookie cookie = new Cookie("userId", null);
        cookie.setPath("/");
        cookie.setMaxAge(0); // 삭제
        response.addCookie(cookie);

        return ResponseEntity.ok("로그아웃 되었습니다.");
    }
}

3. Session

Session (세션)

  • 웹에서 사용자의 상태 정보를 서버에 저장하여 관리하는 기술
  • 서버가 특정 클라이언트를 구분하고 그 상태를 기억하도록 해준다.
  • 서버는 클라이언트에게 고유한 세션 ID(Session ID)를 발급하고, 이를 통해 사용자의 상태 데이터를 서버에 저장한다.
  • 클라이언트는 보통 이 세션 ID를 쿠키(cookie) 형태로 저장하고 요청 시 서버에 전달한다.

 

Session 특징

  • 중요한 사용자 정보는 서버에 저장되므로 보안에 유리하다.
  • 클라이언트에는 세션 ID만 저장되어 데이터 위변조 위험이 적다.
  • 서버 자원을 사용하기 때문에 대규모 서비스 시에는 세션 관리와 확장성 고려가 필요하다.

 

Session 구성 요소

구성요소 역할 및 설명 특징 및 주의점
Session ID 클라이언트를 고유하게 식별하는 문자열로, 서버가 생성하여 클라이언트에 전달한다. 랜덤하고 예측 불가능한 값이어야 하며,
쿠키에 저장되어 요청 시 서버에 전달된다.
보안을 위해 HTTPS, HttpOnly, Secure 옵션 적용 권장.
Session 객체 사용자별 상태 정보를 키-값 쌍 형태로 저장하는 서버 내 데이터 컨테이너이다. 로그인 정보, 권한, 장바구니 등 사용자 상태 정보를 저장한다.
서버 내 API로 접근 및 수정 가능하다.
Session 저장소 세션 객체를 저장하는 공간으로 메모리, DB, 분산 캐시 등 다양한 방식이 있다. 메모리: 빠르나 서버 재시작 시 데이터 소실 DB/분산 캐시: 영속성 및 확장성 우수, 대규모 서비스에 적합
만료 시간
(Timeout)
세션이 활성 상태로 유지되는 최대 시간을 제한하며, 일정 시간 무요청 시 세션을 만료시킨다. 보안을 위해 적절한 시간 설정이 중요하다.
만료 후에는 세션 데이터 삭제 또는 무효화되어 새로운 세션이 생성된다.
세션 관리기 세션 생성, 조회, 검증, 만료 처리를 담당하는 서버 내부 모듈이다. 세션 ID 중복 방지, 유효성 검사, 만료 관리 등을 수행한다.
세션 저장소와 연동하여 세션 상태를 관리한다.

 

 

Session TimeOut

  • Session의 문제점
    • HTTP는 Connectionless 특성을 가지고 있어서 서버가 브라우저 종료 여부를 판별하지 못한다.
    • 따라서 세션을 언제 삭제해야 할지 서버 입장에서는 판단하기 어렵다.
    • JSESSIONID가 탈취되면 악의적인 요청에 악용될 수 있다.
    • 세션은 서버 메모리를 사용하므로 많은 사용자가 동시에 접속하면 서버 자원이 급격히 소모된다.
  • Session 생명주기
    • 세션은 기본적으로 30분 동안 유지되며, 그 이후에는 자동 삭제된다.
    • 사용자가 로그인 후 아무 활동이 없으면 30분이 지나 세션이 만료되고 재로그인이 필요할 수 있다.
  • HttpSession의 생명주기 관리
    • HttpSession은 세션 생성 시점 기준이 아니라, 가장 최근 요청 시간(LastAccessedTime) 을 기준으로 30분을 유지한다.
    • 사용자가 활동할 때마다 세션 유효 시간이 갱신되며, 마지막 접근 시점으로부터 30분이 지나야 세션이 자동 만료된다.

 

Session의 한계

  • 서버는 요청마다 DB나 메모리에 저장된 세션 정보를 조회하므로 오버헤드가 발생한다.
  • 서버가 사용자의 상태를 유지해야 하므로 동시 접속자가 많을수록 부담이 커진다.
  • 세션은 주로 웹 브라우저 기반이기 때문에, 모바일 앱 등 다양한 클라이언트에서 인증 처리에 제한이 있다.
  • 서버를 여러 대로 확장하는 수평 확장(Scale Out) 환경에서는 세션 정보를 서버 간에 공유하기 어렵다.

 

* 오버헤드

  • 어떤 작업을 수행하기 위해 추가로 소모되는 간접적인 시간, 자원(메모리, CPU 등), 처리 비용을 의미한다.
  • 본래 목적을 달성하기 위한 핵심 작업 외에 부수적으로 필요한 처리나 자원 사용을 말한다.
  • 오버헤드가 많으면 시스템 성능이 저하되고 효율이 떨어진다.
  • ex. 서버가 사용자 세션 정보를 매번 조회하는 것

4. Session 사용 예제 1 - Servlet

Servlet의 HttpSession

  • HttpSession은 서블릿에서 클라이언트별 상태를 서버에 저장하고 관리하기 위한 인터페이스이다.
  • HttpSession을 사용해 사용자의 상태를 서버에 저장하고 여러 요청에 걸쳐 유지할 수 있다.
  • 특징
    • 서버가 클라이언트에게 고유한 세션 ID를 발급하여, 이를 쿠키(JSESSIONID)로 클라이언트에 전달한다.
    • 클라이언트는 이후 요청 시 이 세션 ID를 서버로 보내고, 서버는 해당 ID로 세션 객체를 찾아 사용자 상태를 관리한다.
    • HttpSession 객체를 통해 세션에 데이터를 저장(setAttribute)하거나 조회(getAttribute)할 수 있다.
    • 세션은 일정 시간 무활동 시 자동 만료된다.
// 로그인 처리 서블릿
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String id = req.getParameter("id");
        String pw = req.getParameter("pw");

        if ("user".equals(id) && "1234".equals(pw)) {
            HttpSession session = req.getSession();
            session.setAttribute("user", id);  // 로그인 성공 시 세션에 저장
            resp.getWriter().println("로그인 성공");
        } else {
            resp.getWriter().println("로그인 실패");
        }
    }
}

// 로그인 상태 확인 서블릿
@WebServlet("/profile")
public class ProfileServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        HttpSession session = req.getSession(false);
        resp.setContentType("text/html;charset=UTF-8");

        if (session != null && session.getAttribute("user") != null) {
            String user = (String) session.getAttribute("user");
            resp.getWriter().println(user + "님, 환영합니다!");
        } else {
            resp.getWriter().println("로그인 필요");
        }
    }
}

5. Session 사용 예제 2 - Spring

Spring 에서 Session 사용 방법

  • HttpSession
  • @SessionAttribute

 

HttpSession

@Controller
public class SessionController {

    // 로그인 처리 및 세션 저장
    @GetMapping("/login")
    public String login(HttpSession session) {
        session.setAttribute("username", "user123");
        return "redirect:/welcome";
    }

    // 세션에서 직접 값 꺼내 사용
    @GetMapping("/welcome")
    public String welcome(HttpSession session) {
        String username = (String) session.getAttribute("username");
        System.out.println("환영합니다, " + username + "님!");
        return "welcome";
    }
}

 

 * HttpSession을 통해 확인할 수 있는 세션 정보

메서드 설명
getId() 세션의 고유 ID (JSESSIONID)를 반환한다.
getMaxInactiveInterval() 세션의 유효 시간(초)을 반환한다.기본값은 1800초 (30분)이다.
getCreationTime() 세션이 최초로 생성된 시간을 반환한다.예: Sat Dec 9 15:40:23 KST 2024
getLastAccessedTime() 클라이언트가 마지막으로 세션에 접근한 시간을 반환한다.예: Sat Dec 9 15:40:23 KST 2024
isNew() 해당 세션이 새롭게 생성된 것인지 여부를 boolean 값으로 반환한다.

 

 

@SessionAttribute

@Controller
public class SessionAttrController {

    // 로그인 처리 및 세션 저장
    @GetMapping("/login")
    public String login(HttpSession session) {
        session.setAttribute("username", "user123");
        return "redirect:/welcome";
    }

    // 세션에서 값을 바로 인자로 주입받음
    @GetMapping("/welcome")
    public String welcome(@SessionAttribute("username") String username) {
        System.out.println("환영합니다, " + username + "님!");
        return "welcome";
    }
}