DevLog ๐ถ
[Spring] ์ํ ๊ด๋ฆฌ ์์คํ ์ ํ์๊ฐ์ / ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ๋ก์ง ์ถ๊ฐํ๊ธฐ, ์ฟ ํค๋ฅผ ํตํ ์๋ณ ์ฒ๋ฆฌ ๋ณธ๋ฌธ
[Spring] ์ํ ๊ด๋ฆฌ ์์คํ ์ ํ์๊ฐ์ / ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ๋ก์ง ์ถ๊ฐํ๊ธฐ, ์ฟ ํค๋ฅผ ํตํ ์๋ณ ์ฒ๋ฆฌ
dolmeng2 2022. 8. 23. 17:13๊น์ํ ๋์ '์คํ๋ง MVC 2ํธ - ๋ฐฑ์๋ ์น ๊ฐ๋ฐ ํ์ฉ ๊ธฐ์ '์ ๋ณด๊ณ ์ ๋ฆฌํ ๊ธ์ ๋๋ค ๐
- ์ง๋ ํฌ์คํ ๊ณผ ์ด์ด์ง๋๋ค :D
| ํ์๊ฐ์ , ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ์งํํ๊ธฐ
- ์ง๋ ํฌ์คํ ๋ถํฐ ๊ณ์ํด์ ์ ์ํด์๋ ์ํ ๊ด๋ฆฌ ์์คํ ์ ํ์ ๊ฐ์ ์์คํ ์ ์ถ๊ฐํด๋ณด์.
โ ๊ฐ๋จํ ํ ํ๋ฉด ์ ์ํ๊ธฐ
[LHomeController.java]
@Controller
@RequiredArgsConstructor
@RequestMapping("/login")
public class LHomeController {
@GetMapping("/")
public String home() {
return "login/home";
}
}
- ๊ฐ๋จํ ๋ฉ์ธ ํ ํ๋ฉด์ด๋ค.
[home.html] - /resources/login
<!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="mt-3 py-2 text-center">
<h1 class="font-monospace fw-bold mb-3"
th:text="#{page.home}"> ITEM SERVICE HOME :D</h1>
<hr class="my-4">
</div>
<div class="row">
<div class="text-center float-end mb-2 fw-bold" th:text="#{lang}">์ธ์ด ์ ํ</div>
<select id="lang" name="lang" class="form-select mb-4"
onchange="if(this.value) location.href=(this.value);">
<option value="" th:text="#{select.lang.default}">== SELECT LANGUAGE TYPE ==</option>
<option value="?lang=en" th:text="#{select.lang.en}">์์ด</option>
<option value="?lang=ja" th:text="#{select.lang.ja}">์ผ๋ณธ์ด</option>
</select>
<hr class="my-4">
<img class="my-2 mb-5"
src="https://cdn.discordapp.com/attachments/805265715179159623/1011307630284783697/jerryHello.gif"
alt="hello">
<div class="col">
<button class="font-monospace w-100 btn btn-secondary btn-lg" type="button"
th:text="#{label.login.join}"
th:onclick="|location.href='@{/login/members/add}'|">
JOIN
</button>
</div>
<div class="col">
<button class="font-monospace w-100 btn btn-dark btn-lg"
onclick="location.href='items.html'"
th:text="#{label.login.login}"
th:onclick="|location.href='@{/login/members/login}'|" type="button">
LOGIN
</button>
</div>
</div>
<hr class="my-4">
</div>
</body>
</html>
- ๊ธฐ์กด์ items.html์ ์์๋ ์ธ์ด ์ ํ ๋ก์ง์ ํ ํ๋ฉด์ผ๋ก ์ฎ๊ฒผ๋ค.
- ๋ํ, ์ ๋ฐ์ ์ผ๋ก ๋ฉ์์ง๋ฅผ ํ์ฉํ ๊ตญ์ ํ๋ฅผ ์ ์ฉํ์๋ค.
- ์ด๋ฏธ์ง๋ ๊ทธ๋ฅ ์ถ๊ฐํด์ฃผ์๋ค... ๋๋ฌด ํ์ ํด๋ณด์ฌ์...
โ ๋ฉค๋ฒ ๋๋ฉ์ธ ์ ์ํ๊ธฐ
[LMember.java]
@Getter
public class LMember {
private Long id;
private String loginId;
private String name;
private String password;
public void setId(Long id) {
this.id = id;
}
@Builder(builderMethodName = "createMember")
public LMember(String loginId, String name, String password) {
this.loginId = loginId;
this.name = name;
this.password = password;
}
}
- ํ๋์ ํ๋ก์ ํธ์ ํ๋ค ๋ณด๋ ์ด๋ฆ์ ์ด๋ ๊ฒ ์ค์ ํ์๋ค...!
- @Setter๋ฅผ ์ด์ง ์๊ณ ๋กฌ๋ณต์ Builder ํจํด๊ณผ ํ์ํ ํ๋๋ง setter๋ฅผ ์ถ๊ฐํ๋๋ก ์งํํ์๋ค.
[LMemberService.java]
@Service
@RequiredArgsConstructor
public class LMemberService {
private final LMemberRepository repository;
public LMember save(LMemberSaveRequest saveRequest) {
LMember member = LMember.createMember()
.loginId(saveRequest.getLoginId())
.name(saveRequest.getName())
.password(saveRequest.getPassword())
.build();
return repository.save(member);
}
}
- ํ์ ์ ์ฅ ๋ก์ง์ด๋ค. ์๋น์ค๋จ์์ DTO->์ค์ entity๋ก ๋ณ๊ฒฝํ๋ ์์ ์ ์งํํ๋ค.
[LMemberRepository.java]
@Repository
public class LMemberRepository {
private static Map<Long, LMember> store = new ConcurrentHashMap<>();
private static AtomicLong sequence = new AtomicLong(0L);
public LMember save(LMember member) {
member.setId(sequence.incrementAndGet());
store.put(member.getId(), member);
return member;
}
public Optional<LMember> findByLoginId (String loginId) {
return findAll().stream()
.filter(m -> m.getLoginId().equals(loginId))
.findFirst();
}
public LMember findById(Long id) {
return store.get(id);
}
public List<LMember> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore() {
store.clear();
}
}
- ๊ธฐ์กด์ ์ํ ์ ์ฅ ์์คํ ๊ณผ ๊ฑฐ์ ์ ์ฌํ๊ฒ ์ฝ๋๋ฅผ ์งฐ๋ค.
โ ํ์๊ฐ์ ์์คํ ์ ์ํ๊ธฐ
[LMemeberSaveRequest.java]
@Data
public class LMemberSaveRequest {
private Long id;
@NotEmpty
private String loginId;
@NotEmpty
private String password;
@NotEmpty
private String name;
public LMemberSaveRequest(String loginId, String password, String name) {
this.loginId = loginId;
this.password = password;
this.name = name;
}
}
- ํ์ ์ ์ฅ ์ ๋ชจ๋ ์ ๋ณด๊ฐ ํ์๋ก ๋ค์ด์์ผ ํ๊ธฐ ๋๋ฌธ์ @NotEmpty๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค.
- ํ ์คํธ์ฉ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํด์ฃผ๊ธฐ ์ํด์ ์์ฑ์ ํ๋๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค.
[LMemberController.java]
@Controller
@RequiredArgsConstructor
@RequestMapping("/login/members")
public class LMemberController {
private final LMemberService service;
// ํ
์คํธ์ฉ ๋ฐ์ดํฐ ์ถ๊ฐ
@PostConstruct
public void addMember() {
service.save(new LMemberSaveRequest("test@naver.com", "test1234", "Spring"));
}
@GetMapping("/add")
public String addForm(@ModelAttribute("member") LMember member) {
// ํ์๋ฆฌํ์์ th:object์์ member๋ฅผ ๋ฃ์ด์ฃผ๊ธฐ ๋๋ฌธ์
// GET, POST ๋ฐฉ์ ๋ชจ๋ addMemberForm์ ์ฌ์ฉํ๊ธฐ ์ํด @ModelAttribute ์ฌ์ฉ
// ์ด๋ ๊ฒ ์ฌ์ฉํ์ง ์์๋ ์ด์ฐจํผ ์ฌ์ฉ์์ ํ๋ผ๋ฏธํฐ๊ฐ ๋์ด์ค์ง๋ ์๊ธฐ ๋๋ฌธ์
// ModelAttribute๊ฐ ๋น ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ฒ ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์
// model.addAttribute("member", new LMember())๋ก ์จ๋ ๋ฌด๋ฐฉํ๋ค.
return "login/members/addMemberForm";
}
@PostMapping("/add")
public String save(@Valid @ModelAttribute("member") LMemberSaveRequest saveRequest,
BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
return "login/members/addMemberForm";
}
service.save(saveRequest);
return "redirect:/login/";
}
}
- ํ ์คํธ์ฉ ๋ฉค๋ฒ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํด์ฃผ์๋ค. (์ด์ฐจํผ ์์ดํ ์ SItemController์์ ์ถ๊ฐํด์ฃผ๋๊น)
๐ฉ addForm ๋ถ๋ถ์์ ๊ตณ์ด @ModelAttribute๋ฅผ ์จ์ผ ํ๋? ๋ผ๋ ์๊ฐ์ ํ์๋๋ฐ,
์ด์ฐจํผ ์ฌ์ฉ์์ ๊ฐ์ด ๋์ด์ค์ง ์๊ธฐ ๋๋ฌธ์ @ModelAttribute๊ฐ LMember ๊ฐ์ฒด๋ฅผ ๋ง๋ค ๋ ํ๋ผ๋ฏธํฐ ์์ด new LMember()๋ก ๋ง๋ ๋ค.
- ๊ทธ๋์ ๊ทธ๋ฅ Model์ ์ ์ธํ ๋ค์ model.addAttribute("member", new LMember())๋ก ์จ๋ ๋ฌด๋ฐฉํ๋ค!
[addMemberForm.html]
<!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">
<style>
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="mt-5 mb-2 text-center">
<h2 class="font-monospace fw-bold" th:text="#{label.login.join}">JOIN</h2>
</div>
<hr class="my-4">
<form action="" th:action th:object="${member}" method="post">
<div th:if="${#fields.hasGlobalErrors()}">
<p class="field-error text-center fw-bold"
th:each="err: ${#fields.globalErrors()}"
th:text="${err}">
์ ์ฒด ์ค๋ฅ ๋ฉ์์ง</p>
</div>
<div class="row fw-bold font-monospace mb-2">
<div class="col text-center">
<label for="loginId">ID</label>
<input type="text"
id="loginId"
th:field="*{loginId}"
class="form-control text-center"
th:errorclass="field-error"
placeholder="Please Enter the Login ID.">
<div class="field-error"
th:errors="*{loginId}">
</div>
</div>
</div>
<div class="row fw-bold font-monospace mb-2">
<div class="col text-center">
<label for="password" th:text="#{label.login.password}">PASSWORD</label>
<input type="text"
id="password"
th:field="*{password}"
class="form-control text-center"
th:errorclass="field-error"
placeholder="Please Enter the Password.">
<div class="field-error"
th:errors="*{password}">
</div>
</div>
</div>
<div class="row fw-bold font-monospace mb-2">
<div class="col text-center">
<label for="name" th:text="#{label.login.name}">NAME</label>
<input type="text"
id="name"
th:field="*{name}"
class="form-control text-center"
th:errorclass="field-error"
placeholder="Please Enter the Name.">
<div class="field-error"
th:errors="*{name}">
</div>
</div>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-warning btn-lg"
type="submit">
โญ
</button>
</div>
<div class="col">
<button class="w-100 btn btn-danger btn-lg"
th:onclick="|location.href='@{/login/}'|"
type="button">
โ
</button>
</div>
</div>
</form>
</div>
</body>
</html>
- ํ์ ๊ฐ์ ํผ์ด๋ค. ๋ฐ๋ก ๋์์ธํ๊ธฐ ๊ท์ฐฎ์์ ๊ทธ๋ฅ ์ํ ๋ฑ๋ก ํผ์ด๋ ์ ์ฌํ๊ฒ ๋ง๋ค์๋ค.
โ ๋ก๊ทธ์ธ ์์คํ ์ ์ํ๊ธฐ
[LMemberLoginRequest.java]
@Data
public class LMemberLoginRequest {
@NotEmpty
private String loginId;
@NotEmpty
private String password;
}
[LMemberService.java] - ์ถ๊ฐ
public LMember login(LMemberLoginRequest loginRequest) {
return repository.findByLoginId(loginRequest.getLoginId())
.filter(m -> m.getPassword().equals(loginRequest.getPassword()))
.orElse(null);
}
- ์ ์ฅ๋ id, pwd๊ฐ ๋ค๋ฅผ ๊ฒฝ์ฐ Member ์ํฐํฐ๋ฅผ null๋ก ๋ฆฌํดํ๋ค.
[LMemberController.java] - ์ถ๊ฐ
@GetMapping("/login")
public String loginForm(@ModelAttribute("loginForm") LMemberLoginRequest loginRequest) {
return "login/members/loginForm";
}
@PostMapping("/login")
public String login(@Valid @ModelAttribute("loginForm") LMemberLoginRequest loginRequest,
BindingResult bindingResult) {
if(bindingResult.hasErrors()) {
return "login/members/loginForm";
}
LMember loginMember = service.login(loginRequest);
if(loginMember == null) {
bindingResult.reject("loginFail", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ต๋๋ค.");
return "login/members/loginForm";
}
return "redirect:/login/";
}
- 1์ฐจ์ ์ผ๋ก ๊ธ๋ก๋ฒ ์ค๋ฅ (@NotEmpty)๋ฅผ ์ฒดํฌํ๊ณ , ๋ง์ฝ ์๋น์ค์์ member๊ฐ null๋ก ๋ฆฌํด๋๋ค๋ฉด bindingResult์ ํด๋น ๊ฒฐ๊ณผ๋ฅผ ์ถ๊ฐํ๋ค.
[loginForm.html]
<!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">
<style>
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body>
<div class="container" style="max-width: 600px">
<div class="mt-5 mb-2 text-center">
<h2 class="font-monospace fw-bold" th:text="#{label.login.login}">LOGIN</h2>
</div>
<hr class="my-4">
<form action="" th:action th:object="${loginForm}" method="post">
<div th:if="${#fields.hasGlobalErrors()}">
<p class="field-error text-center fw-bold"
th:each="err: ${#fields.globalErrors()}"
th:text="${err}">
์ ์ฒด ์ค๋ฅ ๋ฉ์์ง</p>
</div>
<div class="row fw-bold font-monospace mb-2">
<div class="col text-center">
<label for="loginId">ID</label>
<input type="text"
id="loginId"
th:field="*{loginId}"
class="form-control text-center"
th:errorclass="field-error"
placeholder="Please Enter the Login ID.">
<div class="field-error"
th:errors="*{loginId}">
</div>
</div>
</div>
<div class="row fw-bold font-monospace mb-2">
<div class="col text-center">
<label for="password" th:text="#{label.login.password}">PASSWORD</label>
<input type="password"
id="password"
th:field="*{password}"
class="form-control text-center"
th:errorclass="field-error"
placeholder="Please Enter the Password.">
<div class="field-error"
th:errors="*{password}">
</div>
</div>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-warning btn-lg"
type="submit">
โญ
</button>
</div>
<div class="col">
<button class="w-100 btn btn-danger btn-lg"
th:onclick="|location.href='@{/login/}'|"
type="button">
โ
</button>
</div>
</div>
</form>
</div>
</body>
</html>
- ๋ก๊ทธ์ธ ํ์ด์ง์ด๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก ํ์๊ฐ์ ์ด๋ ๊ฑฐ์ ๋๊ฐ์ด ์ ์ํ์๋ค.
- ๊ธ๋ก๋ฒ ์ค๋ฅ ๋ฉ์์ง ๋ฐ ID/PWD ๊ฒ์ฆ๋ ์ฑ๊ณต์ ์ผ๋ก ์คํ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
| ์ฟ ํค๋ฅผ ์ด์ฉํ์ฌ ๋ก๊ทธ์ธ ์ฒ๋ฆฌํ๊ธฐ
- ๊ธฐ๋ณธ์ ์ธ ํ์๊ฐ์ , ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ๋ก์ง์ ๊ตฌํํ์๋ค.
- ๊ทธ๋ฌ๋, ๋จ์ํ ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ณ์ํด์ ์๋ชจํ๋ฉด์ ๋ณด๋ด๋ ๊ฒ์ ๋ฒ๊ฑฐ๋กญ๊ธฐ ๋๋ฌธ์ ์ฐ๋ฆฌ๋ HTTP ์๋ต์ ์ฟ ํค๋ฅผ ๋ด์ ์์ ์ด๋ค.
- โญ ๋ธ๋ผ์ฐ์ ๋ ํด๋น ์ฟ ํค ๊ฐ์ ๋ฐ์์ ์ฟ ํค ์ ์ฅ์์ ๋ฃ์ด๋๊ณ , ์์ผ๋ก ์์ฒญ์ ๋ณด๋ผ ๋๋ง๋ค ์ฟ ํค ๊ฐ์ ํจ๊ป ๋ณด๋ด์ค ๊ฒ์ด๋ค.
- ๊ทธ๋ผ ์๋ฒ๋ ์์ฒญ๊ณผ ํจ๊ป ์จ ์ฟ ํค ๊ฐ์ ํตํด ํ์์ ์๋ณํ๊ณ , ๊ทธ์ ๋ง๋ ํ๋์ ์ฒ๋ฆฌํ ์ ์๋ค!
- ๋ธ๋ผ์ฐ์ ๊ฐ ์ข ๋ฃ๋๋ฉด ๋ก๊ทธ์์์ด ๋๋๋ก ์ธ์ ์ฟ ํค๋ฅผ ๊ตฌํํ์.
[LMemberController.java] - ์์
@PostMapping("/login")
public String login(@Valid @ModelAttribute("loginForm") LMemberLoginRequest loginRequest,
BindingResult bindingResult, HttpServletResponse response) {
if(bindingResult.hasErrors()) {
return "login/members/loginForm";
}
LMember loginMember = service.login(loginRequest);
if(loginMember == null) {
bindingResult.reject("loginFail", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์ต๋๋ค.");
return "login/members/loginForm";
}
Cookie cookie = new Cookie("memberId", String.valueOf(loginMember.getId()));
cookie.setPath("/");
response.addCookie(cookie);
return "redirect:/login/";
}
- ๋ก๊ทธ์ธ ์ฑ๊ณต ์ ์ฟ ํค๋ฅผ ์์ฑํ๊ณ HttpServletResponse์ ๋ด์์ค๋ค.
- ์ด๋, ์ฟ ํค ์ด๋ฆ์ memberId๋ก ์ค์ ํ์ฌ ํ์ Id๋ฅผ ๋ด์๋๋ค.
- ์ฐธ๊ณ ๋ก, cookie์ path๋ฅผ ์ง์ ํด์ค์ผ ํ๋ค.
๐ฉ๋ณด๋๊น path ๊ฐ์ ๋ฐ๋ก ์ค์ ํ์ง ์์ผ๋ฉด ์ฟ ํค๋ฅผ ์์ฑํ๋ ํ์ด์ง์ ๊ฒฝ๋ก๋ก๋ง ์ ์ก๋๋ค๊ณ ํ๋ค.
๋ชจ๋ ๊ฒฝ๋ก๊ฐ ํธ์ถ๋ ๋ cookie ๊ฐ์ ์ ๋ฌํ๋๋ก ํ๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ด ์ค์ ํด์ฃผ์๋ค.
[LHomeController.java] - ๋ก๊ทธ์ธ ์ ์ฉ ํ์ด์ง
@GetMapping("/")
public String home(@CookieValue(name = "memberId", required = false) Long memberId,
Model model) {
if(memberId == null) {
return "login/home";
}
LMember member = service.findById(memberId);
if(member == null) {
return "login/home";
}
model.addAttribute("member", member);
return "login/loginHome";
}
- ๋ค์๊ณผ ๊ฐ์ด @CookieValue๋ฅผ ์ฌ์ฉํ์ฌ ์ฟ ํค์ ์๋ ๊ฐ์ ์ฝ๊ฒ ์ ๊ทผํ๋๋ก ์ ์ํ์๋ค.
- ์ด๋, ๋ก๊ทธ์ธํ์ง ์์ ์ฌ์ฉ์๋ ํ ํ๋ฉด์ ์ ๊ทผ์ด ๊ฐ๋ฅํ๊ธฐ ๋๋ฌธ์ required=false ์กฐ๊ฑด์ ๊ฑธ์ด์ฃผ์๋ค.
[loginHome.html]
<!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="mt-5 py-2 text-center">
<h1 class="font-monospace fw-bold mb-3"
th:text="#{start.message(${member.name})}">๐๐ป Hello</h1>
<hr class="my-4">
</div>
<div class="row">
<div class="text-center float-end mb-2 fw-bold" th:text="#{lang}">์ธ์ด ์ ํ</div>
<select id="lang" name="lang" class="form-select mb-4"
onchange="if(this.value) location.href=(this.value);">
<option value="" th:text="#{select.lang.default}">== SELECT LANGUAGE TYPE ==</option>
<option value="?lang=en" th:text="#{select.lang.en}">์์ด</option>
<option value="?lang=ja" th:text="#{select.lang.ja}">์ผ๋ณธ์ด</option>
</select>
<hr class="my-4">
<img class="my-2 mb-5"
src="https://cdn.discordapp.com/attachments/703627120320184320/1011530716741369927/img.gif"
alt="hello">
<div class="col">
<button class="font-monospace w-100 btn btn-secondary btn-lg" type="button"
th:onclick="|location.href='@{/basic/items}'|">
๐
</button>
</div>
<div class="col">
<form th:action="@{/login/members/logout}" method="post">
<button class="font-monospace w-100 btn btn-dark btn-lg"
th:onclick="|location.href='@{/login/}'|"
th:text="#{label.login.logout}"
type="submit">
LOGOUT
</button>
</form>
</div>
</div>
<hr class="my-4">
</div>
</body>
</html>
- ๋ก๊ทธ์ธํ ์ฌ์ฉ์ ์ ์ฉ ํ๋ฉด์ด๋ค. ์๋จ์ ์ฌ์ฉ์์ ์ด๋ฆ์ด ๋ฌ๋ค.
- ๋ก๊ทธ์ธํ์ง ์์ ์ฌ์ฉ์์๊ฒ๋ ์ ๋ฆฌ๋ฅผ, ๋ก๊ทธ์ธํ ์ฌ์ฉ์์๊ฒ๋ ํฐ์ ๋์์ฃผ์๋ค... ใ ใ ใ
โ ๋ก๊ทธ์์ ์ฒ๋ฆฌํ๊ธฐ
- ์ธ์ ์ฟ ํค์ด๊ธฐ ๋๋ฌธ์ ์น ๋ธ๋ผ์ฐ์ ์ข ๋ฃ ์ ๋ก๊ทธ์์ ์ฒ๋ฆฌ๊ฐ ๋๋ค.
- ํน์, ์๋ฒ์์ ์์๋ก ์ฟ ํค์ ์ข ๋ฃ ๋ ์ง๋ฅผ 0์ผ๋ก ์ค์ ํ ์ ์๋ค.
[LMemberController.java]
@PostMapping("/logout")
public String logout(HttpServletResponse response) {
Cookie cookie = new Cookie("memberId", null);
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
return "redirect:/login/";
}
- ์ฌ๊ธฐ์ ์ฝ๊ฐ ์๋ฌธ์ธ ์ ์ด, ๊ธฐ์กด์ ์กด์ฌํ๋ ์ฟ ํค๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ ์๋๋ผ ๊ตณ์ด Cookie๋ฅผ new๋ก ๋ง๋ค์ด์ผ ํ๋? ๋ผ๋ ๊ฒ์ด์๋๋ฐ
โญ ์ฐพ์๋ณด๋๊น cookie์ name ์์ฑ์ด ๋์ผํ ๊ฒฝ์ฐ ๊ฐ์ฅ ๋ง์ง๋ง์ ์ถ๊ฐ๋ ์ฟ ํค๋ง ์ ์ฅ๋๋ค๊ณ ํ๋ค!
- ๊ทธ๋์ ์ด์ฐจํผ ์ฟ ํค 1๊ฐ๋๊น ๊ทธ๋ฅ ์๋กญ๊ฒ ์์ฑํด์ "memberId"์ธ ์ฟ ํค์ ์ข ๋ฃ ๋ ์ง๋ฅผ 0์ผ๋ก ์ค์ ํ์๋ค.
| ์ฟ ํค์ ๋ณด์ ์ด์
- ๊ธฐ๋ณธ์ ์ผ๋ก ์ฟ ํค์ ๊ฐ์ ์์๋ก ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ๋ค.
- ๊ฐ๋ฐ์ ๋ชจ๋ -> Application -> Cookie์์ ํ์ธ
- ์ฟ ํค์ ๋ณด๊ด๋ ์ ๋ณด๋ ํ์ทจ๊ฐ ๊ฐ๋ฅํ๋ค.
- ํ์ทจ๋ ์ฟ ํค๋ ํด์ปค๊ฐ ์ ์์ ์ธ ์์ฒญ์ ํ ์๋ ์๋ค.
- ๊ทธ๋์ ์ฟ ํค์ ๊ฐ์ ์ค์ํ ๊ฐ์ ๋ ธ์ถํ์ง ์๊ณ , ์ฌ์ฉ์๋ณ๋ก ์์ธก์ด ์ ๋๋ ๋๋ค๊ฐ์ ๋ ธ์ถํด์ผ ํ๋ค.
- ์๋ฒ์์ ํ ํฐ์ ๊ด๋ฆฌํด์ ๋๋ค๊ฐ์ ํตํด ๋งคํ๋ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ์ป์ด์ค๋ ๊ฒ ๋ฐ๋์งํ๋ค.
- ํด์ปค๊ฐ ์ฟ ํค์ ๊ฐ์ ํ์ณ๊ฐ๋๋ผ๋ ์ง์ ์๊ฐ์ ์งง๊ฒ ํ๋ค.
- ํน์, ํดํน์ด ์์ฌ๋๋ค๋ฉด ํด๋น ํ ํฐ์ ๊ฐ์ ๋ก ์ ๊ฑฐ์ํจ๋ค.
- ๋ค์ ํฌ์คํ ์์๋ ์ฟ ํค ๋์ ์ธ์ ์ ํตํด ๋ก๊ทธ์ธ ์ฒ๋ฆฌ๋ฅผ ๋ณ๊ฒฝํด๋ณด์.