스프링부트 블로그 만들기 – 6강 로그인, 로그아웃
로그인
1.로그인 페이지에서 데이터를 넘겨줄 수 있게 만들어준다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="../layout/header.jsp"%> <div class="container p-4 w-25 bg-light rounded shadow"> <h5 style="font-family: 'IBM Plex Sans KR', sans-serif; margin-bottom: 30px;">로그인</h5> <form action="/login" method="post"> <div class="form-group"> <input type="text" name="username" class="form-control" placeholder="Enter username" required="required"> </div> <div class="form-group"> <input type="password" class="form-control" name="password" placeholder="Enter password" id="pwd" > </div> <button type="submit" class="btn btn-primary col-md-4" style="margin-top: 30px;">로그인</button> </form> </div> <%@ include file="../layout/footer.jsp"%>
2.로그인 DTO(Data Transfer Oject) 를 만들어준다.
DTO(Data Transfer Object)란 통신을 위한 오브젝트로 변수를 적는 대신 함수로 만들어 재사용할 수 있게 만든다. jsp 파일의 form 태그에서 name으로 데이터를 받아올 때 일반 변수로 받으면 MINE Type으로 데이터를 받아오는데 이렇게 만들면 validation 타입으로 받을 수 없어지기 때문에 Dto를 사용하여 타입에 상관없이 받을 수 있게 만드는 것이다.
방법1. 개인 프로젝트
package com.cos.blogapp.web.dto; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class LoginReqDto { @Size(min = 2, max = 20) @NotBlank private String username; @Size(min = 4, max = 20) @NotBlank private String password; }
join 메서드를 실행할 때 DTO에서 toEntity라는 함수로 만들어 사용하면 호출시 코드가 깔끔해지고 재사용에 용이해집니다.
방법2. 회사 실무
package com.cos.blogapp2.web.dto; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @AllArgsConstructor @Setter @Getter public class LoginReqDto { private String username; private String password; }
3.UserRepository에서 아이디/비밀번호를 셀렉트하기 위한 네이티브 쿼리를 만들어준다.
package com.cos.blogapp.domain.user; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; public interface UserRepository extends JpaRepository<User, Integer>{ @Query(value = "SELECT * FROM user WHERE username = :username AND password = :password", nativeQuery = true) User mLogin(String username, String password); }
기본적으로 가지고 있는 함수 crud 중에 save(), findById(), findAll(), deleteById()가 있는데 로그인과 패스워드로 select하는 함수는 없으므로 네이티브쿼리를 따로 만들어줄 수 있습니다. 내가 만든 쿼리 이름에는 ‘m’을 붙여 구분해주세요.
interface는 메모리에 뜨지 않기 때문에 @Repository를 붙여줘야하지만 JpaRepository 내부에 이미 포함되어 있기때문에 이를 상속받는 UserRepository는 따로 적어주지 않아도 됩니다.
4.UserController.java에서 로그인 메서드를 만들어준다.
private final HttpSession session; @PostMapping("/login") public String login(LoginReqDto dto) { String encPassword = SHA.encrypt(dto.getPassword()); User principal = userRepository.mLogin(dto.getUsername(), encPassword); if(principal == null) { return "redirect:/loginForm"; }else { session.setAttribute("principal", principal); return "redirect:/"; } }
방법2를 선택한 경우 다음과 같은 오류가 뜬다.
원인 : 디폴트 생성자의 부재
해결 방법
User.java 파일에서 디폴트 생성자를 만들어주는
@NoArgsConstructor 어노테이션 추가
5.로그인 완료 후 헤더 메뉴가 바뀌도록 만들어준다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html> <html lang="en"> <head> <title>Blog</title> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.css" rel="stylesheet"> <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote.min.js"></script> </head> <body> <!-- 네브바 시작 --> <nav class="navbar navbar-expand-md bg-dark navbar-dark"> <a class="navbar-brand" href="/">블로그</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="collapsibleNavbar"> <ul class="navbar-nav"> <c:choose> <c:when test="${empty sessionScope.principal}"> <li class="nav-item"><a class="nav-link" href="/loginForm">로그인</a> </li> <li class="nav-item"><a class="nav-link" href="/joinForm">회원가입</a> </li> </c:when> <c:otherwise> <li class="nav-item"><a class="nav-link" href="/board/saveForm">글쓰기</a> </li> <li class="nav-item"><a class="nav-link" href="/user/${sessionScope.principal.id}">회원정보</a> </li> <li class="nav-item"><a class="nav-link" href="/logout">로그아웃</a> </li> </c:otherwise> </c:choose> </ul> </div> </nav> <br> <!-- 네브바 끝 -->
로그아웃
세션 값을 무효화하고 메인페이지로 이동
@GetMapping("/logout") public String logout() { session.invalidate(); return "redirect:/"; }
로그인 부가기능
스프링부트 블로그 만들기 - 유효성 검사프로그램을 만들 때 핵심기능을 만들기 전후로 부가기능을 추가해줘야 한다. 핵심기능을 부가기능과 분리시켜 함수로 만들어 재사용하면 아주 편하게 코딩을 할 수 있다. 하지만 함수로 만들기 위한 공통 로직을 찾는 것은 결코 쉬운 일이 아니다....
스프링부트 블로그 만들기 - 사용자 경험프로그램을 만들 때 핵심기능을 만들기 전후로 부가기능을 추가해줘야 한다. 핵심기능을 부가기능과 분리시켜 함수로 만들어 재사용하면 아주 편하게 코딩을 할 수 있다. 하지만 함수로 만들기 위한 공통 로직을 찾는 것은 결코 쉬운 일이 아니다....