[Spring] 쿠키, 세션
Updated:
1. 개요
쿠키와 세션을 이용하면, 쿼리 파라미터를 계속 유지하는 번거로운 작업 없이도 로그인 상태를 유지할 수 있다. 이번에는 쿠키와 세션을 사용하는 방법에 대해 알아보도록 하자.
2. 개발 환경
-
Java 11
-
Spring Boot 2.7.5
3. 쿠키
서버에서 로그인에 성공했을 때, 응답 헤더에 Set-Cookie로 쿠키를 담아서 클라이언트에게 전달하면, 클라이언트는 앞으로 서버에게 요청할 때, 해당 쿠키를 헤더에 포함해서 요청한다. 쿠키는 아래와 같이 두 가지 유형이 있다.
1) 영속 쿠키 : 만료 날짜를 입력하면 해당 날짜까지 쿠키 유지
2) 세션 쿠키 : 만료 날짜를 생략하면 브라우저 종료할 때까지만 유지
3-1. 예제 코드
[LoginForm.java]
1
2
3
4
5
6
7
8
9
@Data
public class LoginForm {
@NotEmpty
private String loginId;
@NotEmpty
private String password;
}
[LoginService.java]
1
2
3
4
5
6
7
8
9
10
11
12
@Service
@RequiredArgsConstructor
public class LoginService {
private final MemberRepository memberRepository;
public Member login(String loginId, String password) {
return memberRepository.findByLoginId(loginId)
.filter(m -> m.getPassword().equals(password))
.orElse(null);
}
}
[LoginController.java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Slf4j
@Controller
@RequiredArgsConstructor
public class LoginController {
private final LoginService loginService;
@PostMapping("/login")
public String login(@Valid @ModelAttribute("loginForm") LoginForm form, BindingResult bindingResult, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
return "login/loginForm";
}
Member loginMember = loginService.login(form.getLoginId(), form.getPassword());
if (loginMember == null) {
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "login/loginForm";
}
// 로그인 성공 처리
// 쿠키에 시간 정보를 주지 않으면 세션 쿠키 (브라우저 종료 시 모두 종료)
Cookie idCookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
response.addCookie(idCookie);
return "redirect:/";
}
@PostMapping("/logout")
public String logout(HttpServletResponse response) {
expireCookie(response, "memberId");
return "redirect:/";
}
private void expireCookie(HttpServletResponse response, String cookieName) {
Cookie cookie = new Cookie(cookieName, null);
cookie.setMaxAge(0);
response.addCookie(cookie);
}
}
[HomeController.java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Slf4j
@Controller
@RequiredArgsConstructor
public class HomeController {
private final MemberRepository memberRepository;
@GetMapping("/")
public String homeLogin(@CookieValue(name = "memberId", required = false) Long memberId, Model model) {
if(memberId == null) {
return "home";
}
// 로그인
Member loginMember = memberRepository.findById(memberId);
if(loginMember == null) {
return "home";
}
model.addAttribute("member", loginMember);
return "loginHome";
}
Line 24 ~ 25 : 쿠키 생성 및 응답에 쿠키정보 추가
Line 37 ~ 39 : 쿠키를 만료 시킨 후 응답에 쿠키정보 추가
[templates/loginHome.html]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>홈 화면</h2> </div>
<h4 class="mb-3" th:text="|로그인: ${member.name}|">로그인 사용자 이름</h4>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-secondary btn-lg" type="button"
th:onclick="|location.href='@{/items}'|">
상품 관리
</button>
</div>
<div class="col">
<form th:action="@{/logout}" method="post">
<button class="w-100 btn btn-dark btn-lg" type="submit">
로그아웃
</button>
</form>
</div>
</div>
<hr class="my-4">
</div> <!-- /container -->
</body>
</html>
3-2. 문제점
-
쿠키 값 임의로 변경가능
-
쿠키에 보관된 정보 탈취가능
-
쿠키 값 획득 시 평생 사용가능
4. 세션
연결 관련 정보를 서버에 저장하고, 서버를 통해 연결을 유지하는 방법을 세션이라고 한다. 쿠키의 문제점을 세션을 통해 아래와 같이 해결할 수 있다.
4-1. 예제 코드
[SessionConst.java]
1
2
3
4
public class SessionConst {
public static final String LOGIN_MEMBER = "loginMember";
}
[HomeController.java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Slf4j
@Controller
@RequiredArgsConstructor
public class HomeController {
@GetMapping("/")
public String homeLoginV3Spring(@SessionAttribute(name = SessionConst.LOGIN_MEMBER, required = false) Member loginMember, Model model) {
// 세션에 회원 데이터가 없으면 home
if(loginMember == null) {
return "home";
}
// 세션이 유지되면 이동
model.addAttribute("member", loginMember);
return "loginHome";
}
}
Line 7 : 세션정보 loginMember에 바인딩
[templates/loginHome.html]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="py-5 text-center">
<h2>홈 화면</h2> </div>
<h4 class="mb-3" th:text="|로그인: ${member.name}|">로그인 사용자 이름</h4>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-secondary btn-lg" type="button"
th:onclick="|location.href='@{/items}'|">
상품 관리
</button>
</div>
<div class="col">
<form th:action="@{/logout}" method="post">
<button class="w-100 btn btn-dark btn-lg" type="submit">
로그아웃
</button>
</form>
</div>
</div>
<hr class="my-4">
</div> <!-- /container -->
</body>
</html>
Leave a comment