Spring boot 중고거래사이트 쇼핑몰 만들기 프로젝트 3 (로그인)

전승재·2023년 7월 27일

이제 로그인을 구현해볼것이다.

로그인 Form

로그인 폼은 아래와 같다.

package com.shopingmall.seungjae.controller.Member;

import lombok.Data;

@Data
public class LoginForm {
    private String loginId;
    private String password;

    private String loginError;
}

LoginService

로그인 로직이다.
입력된 아이디 비밀번호를 토대로 member를 return하는데, 만약 해당하는 member가 없다면 null을 반환한다.

package com.shopingmall.seungjae.service;

import com.shopingmall.seungjae.domain.Member;
import com.shopingmall.seungjae.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.ui.Model;

import java.util.Map;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class LoginService {
    private final MemberRepository memberRepository;

    public Member login(String loginId, String password) {
        Optional<Member> member = memberRepository.findByLoginId(loginId);
        return member.filter(m -> m.getPassword().equals(password)).orElse(null);
    }
}

ShoppingMallMemberLoginRepository

우선 GetMapping으로 들어올 경우 login/loginPage를 return해서 로그인폼 뷰를 사용자에게 보여준다.
@PostMapping으로 /member/login에 요청이 왔을 경우 검증하고 LoginForm 클래스에 바인딩한다.
form에 들어온 (사용자가 입력한 값)을 loginService.login으로 확인하고 loginMember를 생성한다.
loginMember가 null이면 아이디 비밀번호가 틀린것이므로 에러메시지와 함께 다시 login/loginPage를 랜더링한다.

로그인에 성공했다면 세션이 있으면 세션을 반환하고 없다면 신규 세션을 생성한다.
이 때 세션은 서블릿이 제공하는 HttpSession을 사용하여 생성한다.
이렇게 생성한 세션에 로그인 회원 정보를 보관한다.
return "redirect:" + redirectURL; 로 로그인 시도했던 Url로 사용자를 보낸다.

package com.shopingmall.seungjae.controller.Member;

import com.shopingmall.seungjae.domain.Member;
import com.shopingmall.seungjae.repository.MemberRepository;
import com.shopingmall.seungjae.repository.MemberRepositoryImpl;
import com.shopingmall.seungjae.service.LoginService;
import com.shopingmall.seungjae.service.SessionManager;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;

@Controller
@Slf4j
@RequestMapping("/member")
@RequiredArgsConstructor
public class ShoppingMallMemberLoginController {
    private final SessionManager sessionManager;
    MemberRepository memberRepository = new MemberRepositoryImpl();
    private final LoginService loginService;

    @GetMapping("/login")
    public String loginPage() {
        return "login/loginPage";
    }


    @PostMapping("/login")
    public String loginV4(@Valid @ModelAttribute LoginForm form, Model model, @RequestParam(defaultValue = "/") String redirectURL, HttpServletRequest request) {

        Member loginMember = loginService.login(form.getLoginId(),
                form.getPassword());
        log.info("login? {}", loginMember);
        if (loginMember == null) {
            model.addAttribute("loginError", "아이디, 비밀번호가 틀렸습니다. 다시 입력해주세요.");
            return "login/loginPage";
        }
        //로그인 성공 처리
        //세션이 있으면 있는 세션 반환, 없으면 신규 세션 생성
        HttpSession session = request.getSession();
        //세션에 로그인 회원 정보 보관
        session.setAttribute(SessionConst.LOGIN_MEMBER, loginMember);
        return "redirect:" + redirectURL;
    }

    @PostMapping("/logout")
    public String logoutV3(HttpServletRequest request) {
        //세션을 삭제한다.
        HttpSession session = request.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        return "redirect:/";
    }
 
 
//사용자 페이지 (사용자의 정보를 볼 수 있음)
    @GetMapping("/{loginId}")
    public String userPage(@PathVariable("loginId") String loginId, Model model) {
        Member member = memberRepository.findByLoginId(loginId).orElse(null);
        model.addAttribute("member", member);
        return "userPage";
    }
}

Thymeleaf loginPage

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>로그인</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
        }

        .container {
            width: 400px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f0f0f0;
        }

        .form-group {
            margin-bottom: 20px;
        }

        .form-group label {
            display: block;
            margin-bottom: 5px;
        }

        .form-group input {
            width: 100%;
            padding: 8px;
            border-radius: 4px;
            border: 1px solid #ccc;
        }

        button[type="submit"] {
            padding: 10px 20px;
            background-color: #4285f4;
            color: #fff;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .error{
            color : red;
        }
    </style>
</head>
<body>
<div class="container">
    <h2>로그인</h2>
    <form action="/member/login"   method="post">
        <div class="form-group">
            <label for="loginId">아이디</label>
            <input type="text" id="loginId" name="loginId" placeholder="아이디를 입력하세요">
        </div>
        <div class="form-group">
            <label for="password">비밀번호</label>
            <input type="password" id="password" name="password" placeholder="비밀번호를 입력하세요">
        </div>
        <div th:text="${loginError}" class="error">로그인 에러 메시지</div>
        <button type="submit">로그인</button>
        <button type="button" onclick="location.href='/';">홈으로 돌아가기</button>

    </form>
</div>

<script>
<!--    const form = document.querySelector('form');-->
<!--    form.addEventListener('submit', function (event) {-->
<!--        event.preventDefault();-->
<!--        const id = document.querySelector('#id').value;-->
<!--        const password = document.querySelector('#password').value;-->

<!--        // 여기에 로그인 처리를 위한 JavaScript 코드를 추가하세요-->
<!--        // 예시:-->
<!--        console.log('아이디:', id);-->
<!--        console.log('비밀번호:', password);-->

<!--        // 로그인 처리 로직을 구현하세요-->
<!--        // 서버로 요청을 보내고 응답을 처리하는 등의 작업이 필요합니다.-->
<!--    });-->
</script>
</body>
</html>

결과

로그인 실패

로그인 성공

로그내역

0개의 댓글