Search

[스프링 게시판] 로그인 기능 구현

@3/17/2023

프론트단에서 해싱을 해야할까?

결론은 https에서는 비밀번호가 평문전송되지 않으니 굳이 해싱할 필요가 없다는 것

ERD 수정

password와 loginId 필드 추가
엔티티 다이어그램
DB 테이블 ERD
그 외 엔티티, DTO 등 필드 추가 및 코드 변경

로그인 화면 띄우기

타임리프
<form action="/users/login" method="post" class="container login" th:object="${loginForm}"> <div class="row d-flex flex-column justify-content-between align-items-center gap-4"> <h4 class="h1 text-center">로그인</h4> <th:block th:if="${#fields.hasGlobalErrors()}"> <div th:each="err : ${#fields.globalErrors()}" th:text="${err}" class="alert alert-danger mb-0" role="alert"> 에러 메시지 </div> </th:block> <div class="input-group input-group-lg p-0"> <span class="input-group-text w-25 fs-6">아이디</span> <input type="text" class="form-control" aria-label="Sizing example input" th:field="*{loginId}"> </div> <div class="input-group input-group-lg p-0"> <span class="input-group-text w-25 fs-6">비밀번호</span> <input type="password" class="form-control" aria-label="Sizing example input" th:field="*{password}"> </div> <button type="submit" class="btn btn-primary btn-lg">로그인</button> <button type="button" class="btn btn-outline-secondary btn-lg">회원가입</button> </div> </form>
JavaScript
복사
User 컨트롤러
타임리프에서 th:object를 사용했기 때문에 폼을 건네주는 컨트롤러에도 ModelAttribute를 받아서 뷰에 넘겨줘야 함
package dev.gyuray.forum.controller; ... @Controller @RequestMapping("/users") @RequiredArgsConstructor @Slf4j public class UserController { private final UserService userService; @GetMapping("/login") public String loginForm( @ModelAttribute LoginForm loginForm, Model model ) { model.addAttribute("hideLoginButtons", "true"); return "users/loginForm"; } @PostMapping("/login") public String login( @ModelAttribute LoginForm loginForm, BindingResult bindingResult, Model model ) { User loginUser = userService.login(loginForm.getLoginId(), loginForm.getPassword()); if (loginUser == null) { bindingResult.reject("loginFail", "아이디 또는 비밀번호가 맞지 않습니다."); model.addAttribute("hideLoginButtons", "true"); return "users/loginForm"; } return "redirect:/posts"; } }
Java
복사
UserService
로그인 서비스를 따로 만들지 않고 일단 유저 서비스에 만듦
public User login(String loginId, String password) { return userRepository.findByLoginId(loginId).stream() .filter((user) -> user.getPassword().equals(password)) .findAny().orElse(null); }
Java
복사
UserRepository
public List<User> findByLoginId(String loginId) { return em.createQuery("select u from User u where u.loginId = :loginId", User.class) .setParameter("loginId", loginId) .getResultList(); }
Java
복사
김영한님은 UserRepository의 findByLoginId를 아래처럼 구현함
public Optional<Member> findByLoginId(String loginId) { return findAll().stream() .filter(m -> m.getLoginId().equals(loginId)) .findAny(); }
Java
복사
하지만 나는 findBy~ 의 반환형을 List<User>로 통일하기 위해 Repository단에서 Optional로 변환하지 않음(일관성 중시)

결과 화면

실패 시 아이디 데이터가 유지되면서 경고차 발생. 성공 시 홈화면으로 이동