로그인 기능을 위해 아래 코드들을 생성 및 수정하였다.
- LoginService 생성
- MemberController 수정
- SessionConst 생성
- loginMemberForm.html 생성
LoginService
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class LoginService {
private final MemberRepository memberRepository;
public Member login(String loginId, String password) {
Member findMember = memberRepository.findByLoginId(loginId);
if(findMember!=null) {
if(findMember.getPassword().equals(password)) return findMember;
}
return null;
}
}
- @Transactional(readOnly=true): 데이터 베이스에서 값을 읽기만 할 것이므로 읽기 전용으로 선언하였다.
- login()
- 매개변수로 loginId와 password를 받아 검증 성공 시 해당 객체, 실패 시 null을 반환한다.
SessionConst
public class SessionConst {
public static final String LOGIN_MEMBER="loginMember";
}
- 로그인 시 세션에 값을 설정할 때 쓰이는 세션 이름 값
MemberController 수정
@Controller
@RequestMapping("/member")
@RequiredArgsConstructor
public class MemberController {
private final MemberService memberService;
private final LoginService loginService;
@GetMapping("/login")
public String loginForm (@ModelAttribute Member member) {
return "member/loginMemberForm";
}
@PostMapping("/login")
public String login (@Validated @ModelAttribute Member member, BindingResult bindingResult,
HttpServletRequest request) {
if(bindingResult.hasErrors()) { //예외 발생, 1
return "member/loginMemberForm";
}
Member loginMember = loginService.login(member.getLoginId(), member.getPassword());
if(loginMember==null) { //2
bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다.");
return "member/loginMemberForm";
}
//3
HttpSession session = request.getSession();
session.setAttribute(SessionConst.LOGIN_MEMBER,loginMember);
return "redirect:/"; //일단 홈으로 이동하게 설정
}
...
- 필드로 LoginService 생성
- loginForm()
- 로그인 폼으로 이동
- login()
- 로그인 검증하여 실패 시 에러 메시지 생성하거나 성공 시 세션 설정
- //1 : 필드 자체에 문제가 있을 경우 동작, Bean Validation의 어노테이션으로 예외 설정하여서 에러 메세지 자동 생성
- //2 : 아이디 또는 비밀번호 검증 실패 시 동작, 이는 GlobalError에 속한다. 에러 메시지를 임의로 생성해야 한다.
- //3 : 로그인 성공, 세션 설정
loginMemberForm.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LoginForm</title>
<link rel="canonical" href="https://getbootstrap.com/docs/5.1/examples/sign-in/">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<!-- Bootstrap core CSS -->
<link href="../../static/css/bootstrap.min.css"
th:href="@{/css/bootstrap.min.css}" rel="stylesheet"/>
<link href="../../static/css/signin.css"
th:href="@{/css/signin.css}" rel="stylesheet"/>
<link href="../../static/css/headers.css"
th:href="@{/css/headers.css}" rel="stylesheet"/>
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
.field-error {
border-color: #dc3545;
color: #dc3545;
}
</style>
</head>
<body class="text-center">
<main class="form-signin">
<form th:action th:object="${member}" method="post">
<h1 class="h3 mb-3 fw-normal">Login</h1>
<div th:if="${#fields.hasGlobalErrors()}">
<p class="field-error" th:each="err : ${#fields.globalErrors()}"
th:text="${err}">전체 오류 메시지</p>
</div>
<div class="form-floating">
<input type="text" class="form-control" id="loginId" th:field="*{loginId}" placeholder="Id"
th:errorclass="field-error">
<label for="loginId">Id</label>
<div class="field-error" th:errors="*{loginId}" />
</div>
<div class="form-floating">
<input type="password" class="form-control" id="password" th:field="*{password}" placeholder="Password"
th:errorclass="field-error">
<label for="password">Password</label>
<div class="field-error" th:errors="*{password}" />
</div>
<button class="w-100 btn btn-lg btn-primary" type="submit">Login</button>
</form>
</main>
</body>
</html>
- th:errors="*{필드 이름}"을 설정하면 태그 안에 있는 문자열을 필드 이름에 관련된 에러 메시지로 치환할 수 있다.
- 입력 검사에서 오류가 발견됐을 경우에는 th:errorclass속성 값이 class속성에 설정된다.
- th:if="${#fields.hasGlobalErrors()}": 글로벌 에러가 있을 때, 해당 태그가 읽힌다.
- th:each="err : ${#fields.globalErrors()}": 글로벌 에러 하나씩 읽기
- th:field : th:object 속성을 이용하면, th:field를 이용해서 HTML 태그에 멤버 변수를 매핑 할 수 있다.
- th:field을 이용한 사용자 입력 필드(input, textarea 등)는 id, name, value 속성 값이 자동으로 매핑된다.
- th:field="*{loginId}" => id="loginId" name="loginId" value="${member.loginId}"
- th:object와 th:field는 컨트롤러에서 특정 클래스의 객체를 전달 받은 경우에만 사용 가능하다.
- th:field을 이용한 사용자 입력 필드(input, textarea 등)는 id, name, value 속성 값이 자동으로 매핑된다.
추후 인터셉터를 사용하여 웹 페이지를 이동할 때마다 현재 로그인이 되어있는지 확인해야 한다.
'토이 프로젝트 > 게시판 프로젝트' 카테고리의 다른 글
6. 게시판 프로젝트 - 인터셉터 등록, 회원가입 폼 중복 ID 검증 (0) | 2022.08.10 |
---|---|
5. 게시판 만들기 - 회원가입 필드 오류 검증, PRG 패턴 (0) | 2022.08.09 |
3. 스프링 게시판 만들기 - 컨트롤러 생성(Member), 회원가입 기능, 뷰 생성 (0) | 2022.08.09 |
2. 스프링 게시판 만들기 - 도메인 생성, Member 테스트(repository,service) (0) | 2022.08.08 |
1. 스프링 게시판 만들기 - 초기 설정 (0) | 2022.08.08 |