[Spring Boot] 8. Spring Boot Project (Store v2)_11.Authentication logic 추가한 최종 Code

김미숙's avatar
Mar 31, 2025
[Spring Boot] 8. Spring Boot Project (Store v2)_11.Authentication logic 추가한 최종 Code

User

  • User
package com.metacoding.storev2.user; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import java.sql.Timestamp; @NoArgsConstructor @Getter @Table(name = "user_tb") @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; @Column(unique = true, nullable = false, length = 12) private String username; @Column(nullable = false, length = 12) private String password; @Column(nullable = false) private String fullname; private Timestamp createdAt; }
  • UserController
package com.metacoding.storev2.user; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @RequiredArgsConstructor @Controller public class UserController { private final UserService userService; private final HttpSession session; @GetMapping("/") public String home() { return "home"; } @GetMapping("/login-form") public String loginForm() { return "user/login-form"; } @GetMapping("/logout") public String logout() { session.invalidate(); return "redirect:/"; } @PostMapping("/login") public String login(UserRequest.LoginDTO loginDTO) { User sessionUser = userService.로그인(loginDTO); session.setAttribute("sessionUser", sessionUser); return "redirect:/"; } @GetMapping("/join-form") public String joinForm() { return "user/join-form"; } @PostMapping("/join") public String join(UserRequest.JoinDTO joinDTO) { userService.회원가입(joinDTO); return "redirect:/login-form"; } }
  • UserService
package com.metacoding.storev2.user; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Service public class UserService { private final UserRepository userRepository; @Transactional public void 회원가입(UserRequest.JoinDTO joinDTO) { // 동일 회원 있는지 검사 User user = userRepository.findByUsername(joinDTO.getUsername()); if (user != null) { throw new RuntimeException("동일한 username이 존재합니다."); } // 회원가입 userRepository.save(joinDTO.getUsername(), joinDTO.getPassword(), joinDTO.getFullname()); } public User 로그인(UserRequest.LoginDTO loginDTO) { // User 존재 확인 User user = userRepository.findByUsername(loginDTO.getUsername()); if (user == null) { throw new RuntimeException("해당 username이 없습니다."); } if (!(user.getPassword().equals(loginDTO.getPassword()))) { throw new RuntimeException("해당 password가 틀렸습니다."); } // 로그인 return user; } }
  • UserRepository
package com.metacoding.storev2.user; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @RequiredArgsConstructor @Repository public class UserRepository { private final EntityManager em; public User findByUsername(String username) { Query query = em.createNativeQuery("select * from user_tb where username = ?", User.class); query.setParameter(1, username); try { return (User) query.getSingleResult(); } catch (RuntimeException e) { return null; } } public void save(String username, String password, String fullname) { Query query = em.createNativeQuery("insert into user_tb(username,password,fullname,created_at) values(?,?,?,now())"); query.setParameter(1, username); query.setParameter(2, password); query.setParameter(3, fullname); query.executeUpdate(); } }
  • UserRequest
package com.metacoding.storev2.user; import lombok.Data; public class UserRequest { @Data public static class JoinDTO { private String username; private String password; public String fullname; } @Data public static class LoginDTO { private String username; private String password; } }
 

Store

  • Store
package com.metacoding.storev2.store; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @NoArgsConstructor @Getter @Table(name = "store_tb") @Entity public class Store { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private String name; private Integer stock; private Integer price; public void 재고감소(int qty){ this.stock = this.stock - qty; } }
  • StoreController
package com.metacoding.storev2.store; import com.metacoding.storev2.user.User; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @RequiredArgsConstructor @Controller public class StoreController { private final StoreService storeService; private final HttpSession session; @GetMapping("/store/save-form") public String saveForm() { return "store/save-form"; } @PostMapping("/store/save") public String save(StoreRequest.SaveDTO saveDTO) { // 인증체크 (반복되는 공통부가로직) User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요"); System.out.println("name: " + saveDTO.getName()); System.out.println("stock: " + saveDTO.getStock()); System.out.println("price: " + saveDTO.getPrice()); storeService.상품등록(saveDTO); return "redirect:/store/list"; } @GetMapping("/store/list") public String list(HttpServletRequest request) { List<Store> storeList = storeService.상품목록(); request.setAttribute("models", storeList); return "store/list"; } @GetMapping("/store/{id}") public String detail(@PathVariable("id") int id, HttpServletRequest request) { Store store = storeService.상세보기(id); request.setAttribute("model", store); return "store/detail"; } @PostMapping("/store/{id}/delete") public String delete(@PathVariable("id") int id) { // 인증체크 (반복되는 공통부가로직) User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요"); storeService.상품삭제(id); return "redirect:/store/list"; } @GetMapping("/store/{id}/update-form") public String updateForm(@PathVariable("id") int id, HttpServletRequest request) { // 인증체크 (반복되는 공통부가로직) User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요"); Store store = storeService.상세보기(id); request.setAttribute("model", store); return "store/update-form"; } @PostMapping("/store/{id}/update") public String update(@PathVariable("id") int id, @RequestParam("name")String name,@RequestParam("stock") int stock,@RequestParam("price") int price) { // 인증체크 (반복되는 공통부가로직) User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요"); storeService.상품수정(id,name, stock, price); return "redirect:/store/" + id; } }
  • StoreService
package com.metacoding.storev2.store; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @RequiredArgsConstructor @Service public class StoreService { private final StoreRepository storeRepository; @Transactional public void 상품등록(StoreRequest.SaveDTO saveDTO) { storeRepository.save(saveDTO.getName(), saveDTO.getStock(), saveDTO.getPrice()); } public List<Store> 상품목록() { List<Store> storeList = storeRepository.findAll(); return storeList; } public Store 상세보기(int id) { Store store = storeRepository.findById(id); return store; } @Transactional public void 상품삭제(int id) { // 상품존재확인 Store store = storeRepository.findById(id); if (store == null) { throw new RuntimeException("삭제할 상품이 없습니다."); } storeRepository.deleteById(id); } @Transactional public void 상품수정(int id, String name, int stock, int price) { // 상품존재확인 Store store = storeRepository.findById(id); if (store == null) { throw new RuntimeException("수정할 상품이 없습니다."); } storeRepository.update(id,name, stock, price); } }
  • StorerRepository
package com.metacoding.storev2.store; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import org.springframework.web.bind.annotation.PathVariable; import java.util.List; @RequiredArgsConstructor @Repository public class StoreRepository { private final EntityManager em; public void save(String name, int stock, int price) { Query query = em.createNativeQuery("insert into store_tb (name, stock, price) values (?, ?, ?)"); query.setParameter(1, name); query.setParameter(2, stock); query.setParameter(3, price); query.executeUpdate(); } public List<Store> findAll() { Query query = em.createNativeQuery("select * from store_tb order by id desc", Store.class); return query.getResultList(); } public Store findById(int id) { Query query = em.createNativeQuery("select * from store_tb where id = ?", Store.class); query.setParameter(1, id); return (Store) query.getSingleResult(); } public void deleteById(int id) { Query query = em.createNativeQuery("delete from store_tb where id = ?"); query.setParameter(1, id); query.executeUpdate(); } public void update(@PathVariable("id") int id, String name, int stock, int price) { Query query = em.createNativeQuery("update store_tb set name = ?, stock = ?, price = ? where id = ?"); query.setParameter(1, name); query.setParameter(2, stock); query.setParameter(3, price); query.setParameter(4, id); query.executeUpdate(); } }
  • StoreRequest
package com.metacoding.storev2.store; import lombok.Data; public class StoreRequest { @Data public static class SaveDTO { private String name; private Integer stock; private Integer price; } @Data public static class UpdateDTO { private String name; private Integer stock; private Integer price; } }
 

Order

  • Order
package com.metacoding.storev2.order; import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; @NoArgsConstructor @Getter @Table(name = "order_tb") @Entity public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Integer id; private Integer storeId; private Integer userId; private Integer qty; private Integer totalPrice; }
  • OrderController
package com.metacoding.storev2.order; import com.metacoding.storev2.user.User; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpSession; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @RequiredArgsConstructor @Controller public class OrderController { private final OrderService orderService; private final HttpSession session; @GetMapping("/order/list") public String orderList(HttpServletRequest request) { // 인증체크 (반복되는 공통부가로직) User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요"); List<OrderResponse.OrderListPage> OrderListPage = orderService.구매목록(sessionUser.getId()); request.setAttribute("models", OrderListPage); return "order/list"; } @PostMapping("order/save") public String save(@RequestParam("storeId") int storeId, @RequestParam("qty") int qty) { // 인증체크 (반복되는 공통부가로직) User sessionUser = (User) session.getAttribute("sessionUser"); if (sessionUser == null) throw new RuntimeException("로그인 후 사용해주세요"); orderService.구매(storeId,qty,sessionUser.getId()); return "redirect:/order/list"; } }
  • OrderService
package com.metacoding.storev2.order; import com.metacoding.storev2.store.Store; import com.metacoding.storev2.store.StoreRepository; import com.metacoding.storev2.store.StoreRequest; import lombok.AllArgsConstructor; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; @RequiredArgsConstructor @Service public class OrderService { private final OrderRepository orderRepository; private final StoreRepository storeRepository; public List<OrderResponse.OrderListPage> 구매목록(Integer userId) { return orderRepository.findAllJoinStore(userId); } @Transactional public void 구매(int storeId, int qty, int userId) { // 재고조회 Store store = storeRepository.findById(storeId); // 업데이트 store.재고감소(qty); storeRepository.update(store.getId(),store.getName(),store.getStock(),store.getPrice()); // 구매기록 orderRepository.save(storeId,qty,qty*store.getPrice(),userId); } }
  • OrderRepository
package com.metacoding.storev2.order; import jakarta.persistence.EntityManager; import jakarta.persistence.Query; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Repository; import java.util.ArrayList; import java.util.List; @RequiredArgsConstructor @Repository public class OrderRepository { private final EntityManager em; public List<OrderResponse.OrderListPage> findAllJoinStore(Integer userId) { Query query = em.createNativeQuery("select ot.id, st.name, ot.qty, ot.total_price, ot.user_id from order_tb ot inner join store_tb st on ot.store_id=st.id where user_id= ?order by ot.id desc"); query.setParameter(1,userId); List<Object[]> objList = (List<Object[]>) query.getResultList(); List<OrderResponse.OrderListPage> orderListPages = new ArrayList<>(); for (Object[] obj : objList) { OrderResponse.OrderListPage olp = new OrderResponse.OrderListPage( (Integer) obj[0], (String) obj[1], (Integer) obj[2], (Integer) obj[3], (Integer) obj[4] ); orderListPages.add(olp); } return orderListPages; } public void save(int storeId, int qty, int totalPrice,int userId) { Query query = em.createNativeQuery("insert into order_tb(store_id,qty,total_price,user_id) values(?,?,?,?)"); query.setParameter(1, storeId); query.setParameter(2, qty); query.setParameter(3, totalPrice); query.setParameter(4, userId); query.executeUpdate(); } }
  • OrderRequest
package com.metacoding.storev2.order; import lombok.Data; public class OrderRequest { @Data public static class SaveDTO{ Integer storeId; Integer qty; } }
  • OrderResponse
package com.metacoding.storev2.order; import lombok.Data; public class OrderResponse { @Data public static class OrderListPage { private Integer id; private String name; private Integer qty; private Integer totalPrice; private Integer userId; public OrderListPage(Integer id, String name, Integer qty, Integer totalPrice, Integer userId) { this.id = id; this.name = name; this.qty = qty; this.totalPrice = totalPrice; this.userId = userId; } } }
 

Muchache

Layout

  • header
<!DOCTYPE html> <html lang="en"> <head> <title>Store</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script> </head> <body> <nav class="navbar navbar-expand-sm bg-dark navbar-dark"> <div class="container-fluid"> <a class="navbar-brand" href="/">Home</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="/join-form">회원가입</a> </li> <li class="nav-item"> <a class="nav-link" href="/login-form">로그인</a> </li> <li class="nav-item"> <a class="nav-link" href="/store/list">상품목록</a> </li> {{/sessionUser}} {{#sessionUser}} <li class="nav-item"> <li class="nav-item"> <a class="nav-link" href="/store/list">상품목록</a> </li> <a class="nav-link" href="/store/save-form">상품등록</a> </li> <li class="nav-item"> <a class="nav-link" href="/order/list">구매목록</a> </li> <li class="nav-item"> <a class="nav-link" href="/logout">로그아웃</a> </li> {{/sessionUser}} </ul> </div> </div> </nav> <!--네브바종료-->

Order

  • list
{{> layout/header}} <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <h1>구매목록 페이지</h1> <hr> <table class="mt-4 table table-hover"> <thead> <tr> <th>주문번호</th> <th>상품명</th> <th>구매개수</th> <th>총 가격</th> </tr> </thead> <tbody> {{#models}} <tr> <td>{{id}}</td> <td>{{name}}</td> <td>{{qty}}개</td> <td>{{totalPrice}}원</td> </tr> {{/models}} </tbody> </table> </div> </div> </body> </html>

Store

  • list
{{> layout/header}} <div class="container mt-2"> <table class="table table-hover"> <thead> <tr> <th>번호</th> <th>상품명</th> <th>상세보기</th> </tr> </thead> <tbody> {{#models}} <tr> <td>{{id}}</td> <td>{{name}}</td> <td><a href="/store/{{id}}">상세보기</a></td> </tr> {{/models}} </tbody> </table> </div> </body> </html>
  • detail
{{> layout/header}} <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <p>번호 : {{model.id}}</p> <p>상품명 : {{model.name}}</p> <p>상품가격 : {{model.price}}</p> <p>상품재고 : {{model.stock}}</p> </div> <div class="mt-3 mb-3"> <a href="/store/{{model.id}}/update-form" class="btn btn-outline-primary">수정</a> <form action="/store/{{model.id}}/delete" method="POST" class="d-inline"> <button type="submit" class="btn btn-outline-primary">삭제</button> </form> </div> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <form action="/order/save" method="post"> <input type="hidden" value="{{model.id}}" name="storeId"> <input type="text" placeholder="개수를 입력하세요" name="qty"> <button type="submit" class="btn btn-outline-primary">구매</button> </form> </div> </div> </body> </html>
  • save-form
{{> layout/header}} <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <h1>상품등록 페이지</h1> <form action="/store/save" method="post"> <div class="mb-3 mt-3"> <input type="text" class="form-control" placeholder="상품명을 입력하세요" name="name"> </div> <div class="mb-3"> <input type="text" class="form-control" placeholder="재고를 입력하세요" name="stock"> </div> <div class="mb-3 mt-3"> <input type="text" class="form-control" placeholder="가격을 입력하세요" name="price"> </div> <button type="submit" class="btn btn-primary">상품등록</button> </form> </div> </div> </body> </html>
  • update-form
{{> layout/header}} <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <h1>상품수정 페이지</h1> <form action="/store/{{model.id}}/update" method="post"> <div class="mb-3 mt-3"> <input type="text" class="form-control" name="name" value="딸기"> </div> <div class="mb-3"> <input type="text" class="form-control" name="stock" value=45> </div> <div class="mb-3 mt-3"> <input type="text" class="form-control" name="price" value=2000> </div> <button type="submit" class="btn btn-primary">상품수정</button> </form> </div> </div> </body> </html>
 

User

  • join-form
{{> layout/header}} <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <h1>회원가입 페이지</h1> <form action="/join" method="post"> <div class="mb-3 mt-3"> <input type="text" class="form-control" placeholder="Enter username" name="username"> </div> <div class="mb-3"> <input type="password" class="form-control" placeholder="Enter password" name="password"> </div> <div class="mb-3"> <input type="text" class="form-control" placeholder="Enter fullname" name="fullname"> </div> <button type="submit" class="btn btn-primary">회원가입</button> </form> </div> </div> </body> </html>
  • login-form
{{> layout/header}} <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <h1>로그인 페이지</h1> <form action="/login" method="post"> <div class="mb-3 mt-3"> <input type="text" class="form-control" placeholder="Enter username" name="username"> </div> <div class="mb-3"> <input type="password" class="form-control" placeholder="Enter password" name="password"> </div> <button type="submit" class="btn btn-primary">로그인</button> </form> </div> </div> </body> </html>
 

home

{{> layout/header}} <div class="container mt-2"> <div class="mt-4 p-5 bg-light text-dark rounded-4"> <h1>가게에 오신것을 환영합니다.</h1> <h3> {{#sessionUser}} {{sessionUser.fullname}}님 {{/sessionUser}} </h3> </div> </div> </body> </html>
Share article

parangdajavous