안녕하세요, 오늘은 Cookie 와 Session 에 대해 정리해 보았습니다.
💡 웹 브라우저(크롬, 엣지, 파이어폭스 등)에서 사용자의 컴퓨터에 저장되는 정보. 이 정보는 서버에서 사용자의 브라우저로 전송되고, 브라우저는 해당 정보를 저장해두었다가 이후에 같은 서버에 요청을 보낼 때 마다 함께 전송한다. 쿠키는 사용자의 상태나 세션을 유지하거나 사용자 경험을 개선하기 위해 사용된다.Cookie
set-cookie:
sessionId=abcd;
expires=Sat, 11-Dec-2023 00:00:00 GMT;
path=/;
domain=notion.so;
Secure
각각의 의미는 아래에서 설명



쿠키가 아무 사이트에서나 생기고 동작하면 안된다!(필요없는 값 전송, 트래픽 문제 등)
path=/ 루트(전체)로 지정한다path=/apiSecure
HttpOnly
SameSite
userId=1과 같은 index 정보를 저장한다. → 이것 또한 보안문제가 있음요구사항에 맞추어 세션 Cookie를 사용할지 영속 Cookie를 사용할지 결정하면 된다.
@Entity
public class User {
private Long id;
private String userId; // 로그인 ID
private String password; // 로그인 비밀번호
private String name;
}
@Getter
public class LoginRequest { // DTO
@NotBlank
private String id; // 사용자가 입력한 아이디
@NotNull
private String pwd; // 사용자가 입력한 비밀번호
}
@Getter
public class UserDto { // Response
private Long id;
// 이외 응답에 필요한 데이터들을 필드로 구성하면 된다.
// 필요한 생성자
}
@Controller
@Requiredargsconstructor
public class UserController {
private final UserService userService;
@PostMapping("/login")
public String login(
@Valid @ModelAttribute LoginRequest request,
HttpServletResponse response // 쿠키값 세팅에 필요
) {
// 로그인 유저 조회
UserDto loginUser = userService.login(request.getId(), request.getPwd());
if (loginUser == null) {
// 로그인 실패 예외처리
return "login";
}
// 로그인 성공 처리
// 쿠키 생성, Value는 문자열로 변환하여야 한다.
Cookie cookie = new Cookie("userId", String.valueOf(loginMember.getId()));
// 쿠키에 값 세팅 (expire 시간을 주지 않으면 세션쿠키가 됨, 브라우저 종료시 로그아웃)
// Response Set-Cookie: userId=1 형태로 전달된다.
response.addCookie(cookie);
return "redirect:/"; // 메인페이지로 리다이렉트
}
}
@Service
@Requiredargsconstructor
public class UserService {
private final UserRepository userRepository;
public UserDto login(String inputId, String inputPwd) {
// 1. 유저 조회
// 2. 입력한 값과 유저의 비밀번호 일치여부 조회
// 3. 일치하지 않으면 예외처리
// 3-1. 일치하면 return Type인 UserDto에 맞추어 Controller로 전달
return new UserDto(user);
}
}
HttpServletResponse 객체에 에 담는다.Set-Cookie: userId=1 형태로 전달된다.userId 이고 회원 index 값을 담아둔다.userId 가 모든 Request Header Cookie에 담겨서 전달된다.home 페이지로 이동(메인(home) 페이지를 보려면 로그인 필수)login 페이지로 이동@Controller
@Requiredargsconstructor
public class HomeController {
private UserService userService;
@GetMapping("/")
public String home(
// @CookieValue(required = true) 로 필수값(default) 설정
// required = false 이면 필수값 아님.
@CookieValue(name = "userId", required = false) Long userId, // String->Long 자동 타입컨버팅
Model model
) {
// 쿠키에 값이 없으면 로그인 페이지로 이동 -> 로그인 X
if(userId == null) {
return "login";
}
// 실제 DB에 데이터 조회 후 결과가 없으면 로그인 페이지로 이동 -> 일치하는 회원정보 X
UserDto loginUser = userService.findById(userId);
if(loginUser == null) {
return "login";
}
// 정상적으로 로그인 된 사람이라면 View에서 사용할 데이터를 model 객체에 데이터 임시 저장
model.addAttribute("loginUser", loginUser);
// home 화면으로 이동
return "home";
}
}
View에서는 model 객체에 담겨있는 loginUser 를 활용하여 변수로 사용할 수 있다.

${loginUser.getName()}님 환영합니다. “ 과같은 코드가 있음로그아웃 기능
// HTML 예시
<html>
<body>
<form action="/logout" method="post">
<button type="submit">
로그아웃
</button>
</form>
<body>
</html>
@Controller
@Requiredargsconstructor
public class UserController {
private final UserService userService;
@PostMapping("/login")
public String login(
@Valid @ModelAttribute LoginRequest request,
HttpServletResponse response
) {
// 위 예시코드에 나와있음
}
@PostMapping("/logout")
public String logout(
HttpServletResponse response
) {
Cookie cookie = new Cookie("userId", null);
cookie.setMaxAge(0); // 0초로 쿠키를 세팅하여 사라지게 만듬
response.addCookie(cookie);
return "redirect:/"; // 메인페이지로 이동
}
}
userId = 내맘대로 수정가능
💡 Cookie를 사용한 방식은 여러가지 보안 문제가 있다. 결국 보안 문제를 해결하려면 중요한 정보는 모두 서버에서 저장해야한다. Client와 서버는 예측이 불가능한 임의의 값으로 연결해야 한다.Session


Set-Cookie: SessionId=임의생성값 을 전달한다.
HttpSession 을 생성하게되면SessionId 가 → JSESSIONID로 생성된다.ex) JSESSIONID = WLRMATLRKSQKA10TIDIRMSDLTKDDB
// 추상클래스 -> 됨
public abstract class Const {
public static final String LOGIN_USER = "loginUser";
}
// 인터페이스 -> 됨
public interface Const {
String LOGIN_USER = "loginUser";
}
// 클래스 -> 안됨
public class Const { // new Const();
public static final String LOGIN_USER = "loginUser";
}
// Const.LOGIN_USER; O / new Const(); X
@Getter
public class LoginRequestDto {
@NotBlank
private String id; // 사용자가 입력한 아이디
@NotNull
private String pwd; // 사용자가 입력한 비밀번호
}
@Controller
@Requiredargsconstructor
public class UserController {
private final UserService userService;
@PostMapping("/login")
public String login(
@Valid @ModelAttribute LoginRequestDto dto,
HttpServletRequest request
) {
User loginUser = loginService.login(dto.getId(), dto.getPwd());
// 실패시 예외처리
// 로그인 성공시 로직
// Session이 request에 존재하면 기존의 Session을 반환하고,
// Session이 request에 없을 경우에 새로 Session을 생성한다.
HttpSession session = request.getSession(); // default true => session 생성
// Session에 로그인 회원 정보를 저장한다.
session.setAttribute(Const.LOGIN_USER, loginUser);
// 로그인 성공시 리다이렉트
return "redirect:/";
}
}
request.getSession();
- default 값은 true 이다.


request.getSession(true);
request.getSession(false);
session.setAttribute(Const.LOGIN_USER, loginUser);
request.setAttribute(); 와 비슷함로그아웃
@Controller
@Requiredargsconstructor
public class UserController {
private final UserService userService;
@PostMapping("/logout")
public String logout(
HttpServletRequest request
) {
HttpSession session = request.getSession(false);
// 세션이 존재하면 -> 로그인이 된 경우
if(session != null) {
session.invalidate(); // 해당 세션(데이터)을 삭제한다.
}
return "redirect:/";
}
}
home 페이지로 이동(”home” 페이지를 보려면 로그인 필수)login 페이지로 이동@Controller
@Requiredargsconstructor
public class HomeController {
private UserService userService;
@GetMapping("/")
public String home(
HttpServletRequest request,
Model model
) {
// default인 true로 설정되면 로그인하지 않은 사람들도 값은 비어있지만 세션이 만들어진다.
// session을 생성할 의도가 없다.
// session은 Memory를 사용하기 때문에 막 사용하면 안된다! 리소스 낭비!
request.getSession(false);
// session이 없으면 로그인 페이지로 이동
if(session == null) {
return "login";
}
// session에 저장된 유저정보 조회
// Object loginUser = session.getAttribute(Const.LOGIN_USER);
// 반환타입이 Object여서 Type Casting이 필요하다.
User loginUser = (User)session.getAttribute(Const.LOGIN_USER);
// Session에 유저 정보가 없으면 login 페이지 이동
if (loginUser == null) {
return "login";
}
// Session이 정상적으로 조회되면 로그인된것으로 간주
model.addAttribute("loginUser", loginUser);
// home 화면으로 이동
return "home";
}
}
해당 어노테이션은 Session을 새로 생성하는 기능은 없다.
이미 로그인된 사용자를 찾는 경우 즉, 이미 Session이 있는 경우에 사용한다.
코드예시
로그인 후 사용할 API
home 페이지로 이동(”home” 페이지를 보려면 로그인 필수)login 페이지로 이동@Controller
@Requiredargsconstructor
public class HomeController {
private UserService userService;
@GetMapping("/")
public String home(
// Session이 필수값은 아니다. 로그인 했거나 안했거나 판별해야하니 required false
@SessionAttribute(name = Const.LOGIN_USER, required = false) User loginUser,
Model model
) {
// session에 loginUser가 없으면 Login 페이지로 이동
if (loginUser == null) {
return "login";
}
// Session이 정상적으로 조회되면 로그인된것으로 간주
model.addAttribute("loginUser", loginUser);
// home 화면으로 이동
return "home";
}
}
완전히 처음 로그인을 시도하는 경우 URL 뒤에 “JSSESSIONID=값”이 함께 전달된다.
ex) http://localhost:8080/jsessionId=임의로 생성된값서버의 입장에서는 클라이언트의 브라우저가 Cookie를 지원하는지 여부를 확인할 수 없기때문에
최초 로그인 시도시에는 Cookie값과 함께 URL에 값을 함께 전달되는것.
당연히 URL에 값을 담아주지 않도록 설정할 수 있다.
server.servlet.session.tracking-modes=cookie
server:
servlet:
session:
tracking-modes: cookie
항상 URL이 아닌 Cookie를 통해서만 Session을 유지하고자 할 때 위 설정을 사용하면 된다.
@Slf4j
@RestController
public class SessionController {
@GetMapping("/session")
public String session(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null) {
return "no session";
}
// session 정보 조회
}
}
session.getId(); : jsessionId 값을 조회할 수 있다.session.getMaxInactiveInterval(); : 세션의 유효시간session.getCreationTime(); : 세션 생성시간session.getLastAccessedTime(); : 해당 세션에 마지막으로 접근한 시간session.getIsNew(); : 새로 생성된 세션인지 여부ex) 로그인한 유저가 100만명 이라면..?기본적으로 30분을 기준으로 세션을 삭제한다.
하지만 이런 경우 실제 로그인 후 30분 이상의 시간동안 사용중인 사용자의 세션 또한 삭제될 것이다.
즉, 다시 로그인 해야하는 경우가 발생한다.
LastAccessedTime 을 기준으로 30분이 지나면쿠키 (Cookie)
세션 (Session)
오늘의 코드카타
class Solution {
public int solution(int num, int n) {
int answer = 0;
if (num % n == 0) {
answer = 1;
} else {
answer = 0;
}
return answer;
}
}
public class Main {
public static void main(String[] args) {
Solution solution = new Solution();
System.out.println(solution.solution(98, 2));
System.out.println(solution.solution(34, 3));
}
}
n의 배수