ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] 2-1. 세션을 사용하는 회원 로그인, 로그아웃
    프로젝트/도서 관리 시스템 2023. 6. 19. 10:47

     

     

     

     

    세션은 사용자(브라우저)로부터 들어온 데이터를 서버에 연결해 여러 페이지에서 일정하게 유지하는 기술이다. 즉) 사용자와 서버 간의 상태를 유지시켜 주는 방법이다. 그러나 현업에서는 session을 거의 사용하지 않는다. 일단 세션을 이용해 구현하는 방법을 알아보고 다음 포스팅에서는 Spring Security를 사용해서 회원 기능을 구현해보려고 한다. 

     

     

    회원 기능을 위한 요구사항은 다음과 같다. 

     


     

    📑 로그인 구현 

     

     

    <MemberMapper.xml>

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.wish.library.member.mapper.MemberMapper">
    
    
        <select id="read" resultType="MemberVO">
            select *
            from member
            where email = #{email}
            or nickname= #{nickname}
    
        </select>
    
    
       
    </mapper>

     

    mapper.xml에서 설정한 id를 mapper interface의 메서드 명으로 만들면 된다. 여기서 where 절에 email or nickname으로 한 이유는 로직상 email과 nickname이 unique한 속성을 가진다. 그렇기에 중복이 되면 안되는데 중복 검사를 위해 해당 이메일이나 닉네임이 있는 경우 회원가입을 할 수 없게 만들어야된다.

     

    그럼 파라미터로 email을 받고 해당 이메일과 동일한 이메일이 있다면 false, 없다면 true 이런식으로 비즈니스단에서  값을 전달해서 사용하려고 한다. 

     

    그런데 파라미터로 email과 nickname을 전달받아 값을 반환하는 로직이 똑같다. 그저. 이메일인가 닉네임인가 파라미터만 다를뿐, 그래서 최소한 중복되는 코드를 줄이기 위해 메서드들을 하나로 합치다보니 mapper.xml에서 or을 사용하는 일이 발생했다. 어떤게 나은 코드인지는 좀 더 생각을 해봐야겠지만 일단 회원 기능에선 이렇게 구현하였다. 

     

     

     

    <MemberMapper.java>

    package com.wish.library.member.mapper;
    
    
    import com.wish.library.member.domain.MemberVO;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface MemberMapper {
    
        //회원 정보 조회
        public MemberVO read(String email);
    
     
            
    }

     

     

    <MemberServiceImpl.java>

     

    MemberService 인터페이스를 구현하고 인터페이스를 구현하는 MeberServiceImpl을 생성한다. 인터페이스는 따로 코드를 올리지 않겠다. 짜피 재정의해서 사용하기 때문에 .

     

    package com.wish.library.member.service;
    
    import com.wish.library.member.domain.MemberVO;
    import com.wish.library.member.mapper.MemberMapper;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    import java.lang.reflect.Member;
    
    @Service
    @RequiredArgsConstructor
    @Slf4j
    public class MemberServiceImpl implements MemberService{
    
        private final MemberMapper mapper;
    
    //로그인
    	@Override
        public MemberVO login(String email, String password){
            log.info("============= member login service");
            MemberVO readMember = mapper.read(email);
            //해당 아이디로 조회된 회원이 있을 때
            if(readMember !=null){
                //회원이 입력한 비밀번호랑 조회한 값의 비밀번호가 같을 때 로그인 성공.
                if(readMember.getPassword().equals(password)){
                    return readMember;
                }
            }
            return null;
        }
    
    //중복 검사
        @Override
        public MemberVO get(String param) {
            log.info("============= member get service");
            MemberVO readMember = mapper.read(param);
            if(readMember !=null){
                return readMember;
            }
            return null;
        }
    
    }

     

    위의 서비스단에서 살펴보면 login과 get 메서드는 mapper에 read라는 같은 쿼리를 호출하게 된다. 그러나 각각 메서드는 반환하는 로직이 다르므로 따로 구현한다.

     

    login 메서드는 파라미터로 email과 password를 받는다. 사용자의 입력을 받는다는 의미인데 만약 사용자가 입력한 email을 쿼리에서 조회해와서 값이 있다면, 즉 회원이라면 사용자가 입력한 비밀번호와 아이디를 조회했을 때 조회된 비밀번호가 일치하면 로그인한 회원 정보를 컨트롤러로 반환해준다. 

     

    get 메서드는 회원가입에서 설명!~!

     

     

    <MemberController.java>

     

    package com.wish.library.member.comtroller;
    
    import com.wish.library.member.domain.MemberVO;
    import com.wish.library.member.service.MemberService;
    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 org.springframework.web.servlet.mvc.support.RedirectAttributes;
    
    import javax.servlet.http.HttpSession;
    
    @Controller
    @Slf4j
    //생성자 주입
    @RequiredArgsConstructor
    public class MemberController {
    
        private final MemberService service;
    
        @GetMapping("/login")
        public String loginForm(){
            return "/member/login";
        }
    
         @PostMapping("/login")
        public String login(@RequestParam("email") String email, String password , HttpSession session){
    
            MemberVO loginMember = service.login(email, password);
            log.info("==========loginMember={}", loginMember);
            if(loginMember == null){
                return "redirect:/login";
            }
            session.setAttribute("email", loginMember.getEmail());
            session.setAttribute("nickname", loginMember.getNickname());
            //redirect는 경로로 설정
            return "redirect:/";
        }
    
        @PostMapping("/logout")
        public String logout(HttpSession session){
            session.invalidate();
            log.info("로그아웃");
            return "redirect:/";
        }
    
    
    }

     

     

    GetMapping, PostMapping을 나눠서 화면을 조회해오는 작업은 Get, 값을 넘겨서 변화가 일어난다면 Post 어노테이션을 사용하였다. 사용자가 입력한 값을 받아서(email, password) service단에 넘겨주고, 만약 해당 값으로 반환받은 결과가 있다면 (loginMember가 null이 아니라면) session에 email과 nickname을 저장한 후 메인 화면 페이지로 redirect한다. 

     

    로그아웃은 비즈니스로직에서 처리 없이 컨트롤러에서만 작업을 처리한다. session의 값을 없애는 작업. 

     

     


     

    첫 페이지가 나오고 우측 상단에 Login 버튼을 클릭하면 컨트롤러에 @GetMapping된 login 메서드가 호출되며 로그인 페이지를 보여준다. 

     

     

     

    이메일과 비밀번호를 클릭 후 Sign in 누르면 로그인! 

     

     

    <login.jsp>

    <%@ page language="java" contentType="text/html; charset=UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    
    
    <!doctype html>
    <html lang="en" data-bs-theme="auto">
    <head>
    
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="">
        <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
        <meta name="generator" content="Hugo 0.111.3">
        <title>로그인</title>
    
        <link rel="canonical" href="https://getbootstrap.com/docs/5.3/examples/sign-in/">
        <link href="assets/dist/css/sign-in.css" rel="stylesheet">
        <jsp:include page="../includes/common_includes.jsp"></jsp:include>
    
    
    
        <style>
            @media (min-width: 768px) {
                .bd-placeholder-img-lg {
                    font-size: 3.5rem;
                }
            }
    
            .nav-scroller .nav {
                display: flex;
                flex-wrap: nowrap;
                padding-bottom: 1rem;
                margin-top: -1px;
                overflow-x: auto;
                text-align: center;
                white-space: nowrap;
                -webkit-overflow-scrolling: touch;
            }
    
    
        </style>
    
        <script>
            $(document).ready(function (){
                const result = '<c:out value="${result}" />';
                checkAlert(result);
    
                //회원
                function checkAlert(result){
                    console.log(result);
                    if(result === ''){
                        //해당 메서드를 아예 끝내버려라.
                        return;
                    }
    
                    alert(result);
    
                }
            })
        </script>
    </head>
    <body class="text-center">
    
    
    <main class="form-signin w-100 m-auto">
        <form action="/login" method="post">
            <img class="mb-4" src="assets/brand/bootstrap-logo.svg" alt="" width="72" height="57"
                 onclick="location.href='/'">
            <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
    
            <div class="form-floating">
                <input type="email" class="form-control" name="email" id="email" placeholder="name@example.com">
                <label for="email">Email address</label>
            </div>
            <div class="form-floating">
                <input type="password" class="form-control" name="password" id="password" placeholder="Password" autoComplete="off">
                <label for="password">Password</label>
            </div>
    
            <div class="checkbox mb-3">
                <label>
                    <input type="checkbox" value="remember-me"> Remember me
                </label>
            </div>
            <button class="w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
        </form>
    </main>
    
    
    
    </body>
    </html>

     

    form의 action값은 해당 데이터들을 담아서 다음의 url을 호출한다. 즉) 컨트롤러에서 action값에 담긴 uri를 호출한다. 

     

     

    로그인하면 다음과 같은 화면이 나오는 걸 확인 가능하다. 

     

     

    반응형

    댓글

Designed by Tistory.