๐ก
Spring Boot์ JPA๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๊ทธ์ธ ๋ฐ ๋ก๊ทธ์์ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๋ค.
1. ๋ก๊ทธ์ธ ์ฟผ๋ฆฌ ๋ง๋ค๊ธฐ
๋จผ์ , UserRepository๋ฅผ ํตํด ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ก ์ฌ์ฉ์๋ฅผ ์กฐํํ๋ ๋ฉ์๋๋ฅผ ๋ง๋ญ๋๋ค. ์ด ๋ฉ์๋๋ JPQL์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ์ฌ์ฉ์๋ฅผ ๊ฒ์ํฉ๋๋ค.
package com.tenco.blog_v1.user;
import jakarta.persistence.EntityManager;
import jakarta.persistence.TypedQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@RequiredArgsConstructor
@Repository // IoC
public class UserRepository {
private final EntityManager em;
/**
* ์ฌ์ฉ์ ์ด๋ฆ๊ณผ ๋น๋ฐ๋ฒํธ๋ก ์ฌ์ฉ์ ์กฐํ
* @param username
* @param password
* @return ์กฐํ๋ User ์ํฐํฐ, null
*/
public User findByUsernameAndPassword(String username, String password) {
TypedQuery<User> jpql =
em.createQuery("SELECT u FROM User u WHERE u.username = :username AND u.password = :password", User.class);
jpql.setParameter("username", username);
jpql.setParameter("password", password);
return jpql.getSingleResult();
}
}
LoginDTO ๋ง๋ค๊ธฐ
๋ก๊ทธ์ธ ์์ฒญ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด Data Transfer Object (DTO)๋ฅผ ๋ง๋ญ๋๋ค. ์ด๋ ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ์ ๋ฌ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ฒ ๊ด๋ฆฌํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
package com.tenco.blog_v1.user;
import lombok.Data;
@Data
public class UserDTO {
// ์ ์ ๋ด๋ถ ํด๋๋ก ๋ชจ์ฐ์
@Data
public static class LoginDTO {
private String username;
private String password;
}
// ์ ์ ๋ด๋ถ ํด๋๋ก ๋ชจ์ฐ์
@Data
public static class JoinDTO {
private String username;
private String password;
private String email;
}
}
UserController ๊ตฌํ
UserController๋ฅผ ๊ตฌํํ์ฌ ๋ก๊ทธ์ธ ๋ฐ ๋ก๊ทธ์์ ์์ฒญ์ ์ฒ๋ฆฌํฉ๋๋ค. ์ด ์ปจํธ๋กค๋ฌ๋ ์ฌ์ฉ์์ ๋ก๊ทธ์ธ ์ ๋ณด๋ฅผ ๊ฒ์ฆํ๊ณ , ์ธ์ ์ ๊ด๋ฆฌํฉ๋๋ค.
๋ก๊ทธ์ธ ๋ก๊ทธ์์ ๊ธฐ๋ฅ ์ถ๊ฐ
package com.tenco.blog_v1.user;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
@RequiredArgsConstructor
@Slf4j
@Controller
public class UserController {
// DI ์ฒ๋ฆฌ
private final UserRepository userRepository;
private final HttpSession session;
/**
* ์์์ ์์ฒญ์ GET ๋ฐฉ์์ด์ง๋ง ๋ณด์์ ์ด์ ๋ก ์์ธ !
* ๋ก๊ทธ์ธ ์ฒ๋ฆฌ ๋ฉ์๋
* ์์ฒญ ์ฃผ์ POST : http://localhost:8080/login
* @param reqDto
* @return
*/
@PostMapping("/login")
public String login(UserDTO.LoginDTO reqDto) {
try {
User sessionUser = userRepository.findByUsernameAndPassword(reqDto.getUsername(), reqDto.getPassword());
session.setAttribute("sessionUser", sessionUser);
return "redirect:/";
} catch (Exception e) {
// ๋ก๊ทธ์ธ ์คํจ
return "redirect:/login-form?error";
}
}
@GetMapping("/logout")
public String logout() {
session.invalidate(); // ์ธ์
์ ๋ฌดํจํ (๋ก๊ทธ์์)
return "redirect:/";
}
/**
* ํ์๊ฐ์
ํ์ด์ง ์์ฒญ
* ์ฃผ์์ค๊ณ : http://localhost:8080/join-form
*
* @param model
* @return ๋ฌธ์์ด
* ๋ฐํ๋๋ ๋ฌธ์์ด์ ๋ทฐ ๋ฆฌ์กธ๋ฒ๊ฐ ์ฒ๋ฆฌํ๋ฉฐ
* ๋จธ์คํ์น ํ
ํ๋ฆฟ ์์ง์ ํตํด์ ๋ทฐ ํ์ผ์ ๋ ๋๋ง ํฉ๋๋ค.
*/
@GetMapping("/join-form")
public String joinForm(Model model) {
log.info("ํ์๊ฐ์
ํ์ด์ง");
model.addAttribute("name", "ํ์๊ฐ์
ํ์ด์ง");
return "user/join-form"; // ํ
ํ๋ฆฟ ๊ฒฝ๋ก : user/join-form.mustache
}
/**
* ๋ก๊ทธ์ธ ํ์ด์ง ์์ฒญ
* ์ฃผ์์ค๊ณ : http://localhost:8080/login-form
*
* @param model
* @return ๋ฌธ์์ด
* ๋ฐํ๋๋ ๋ฌธ์์ด์ ๋ทฐ ๋ฆฌ์กธ๋ฒ๊ฐ ์ฒ๋ฆฌํ๋ฉฐ
* ๋จธ์คํ์น ํ
ํ๋ฆฟ ์์ง์ ํตํด์ ๋ทฐ ํ์ผ์ ๋ ๋๋ง ํฉ๋๋ค.
*/
@GetMapping("/login-form")
public String loginForm(Model model) {
log.info("๋ก๊ทธ์ธ ํ์ด์ง");
model.addAttribute("name", "๋ก๊ทธ์ธ ํ์ด์ง");
return "user/login-form"; // ํ
ํ๋ฆฟ ๊ฒฝ๋ก : user/join-form.mustache
}
/**
* ํ์ ์ ๋ณด ์์ ํ์ด์ง ์์ฒญ
* ์ฃผ์์ค๊ณ : http://localhost:8080/user/update-form
*
* @param model
* @return ๋ฌธ์์ด
* ๋ฐํ๋๋ ๋ฌธ์์ด์ ๋ทฐ ๋ฆฌ์กธ๋ฒ๊ฐ ์ฒ๋ฆฌํ๋ฉฐ
* ๋จธ์คํ์น ํ
ํ๋ฆฟ ์์ง์ ํตํด์ ๋ทฐ ํ์ผ์ ๋ ๋๋ง ํฉ๋๋ค.
*/
@GetMapping("/user/update-form")
public String updateForm(Model model) {
log.info("ํ์ ์์ ํ์ด์ง");
model.addAttribute("name", "ํ์ ์์ ํ์ด์ง");
return "user/update-form"; // ํ
ํ๋ฆฟ ๊ฒฝ๋ก : user/join-form.mustache
}
}
header.mustache ์ฝ๋ ์์
<!DOCTYPE html>
<html lang="en">
<head>
<title>Blog</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
</head>
<body>
<nav class="navbar navbar-expand-sm bg-dark navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="/">Metacoding</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#collapsibleNavbar">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="collapsibleNavbar">
<ul class="navbar-nav">
{{# sessionUser}}
<li class="nav-item">
<a class="nav-link" href="/board/save-form">๊ธ์ฐ๊ธฐ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/user/update-form">ํ์์ ๋ณด๋ณด๊ธฐ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/logout">๋ก๊ทธ์์</a>
</li>
{{/sessionUser}}
{{^sessionUser}}
<li class="nav-item">
<a class="nav-link" href="/join-form">ํ์๊ฐ์
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/login-form">๋ก๊ทธ์ธ</a>
</li>
{{/sessionUser}}
</ul>
</div>
</div>
</nav>
์ค์ ํ์ธ
spring:
mustache:
servlet:
expose-session-attributes: true # Mustache ????? ?? ??? ??? ? ??? ??
expose-request-attributes: true # Mustache ????? ?? ??? ??? ? ??? ??