쇼핑몰 웹사이트 만들어보기 - 세션을 통한 로그인 상태 유지

Shiba·2024년 5월 17일
0

프로젝트 및 일기

목록 보기
6/29
post-thumbnail

이번 시간에는 로그인 메소드에 세션을 적용하여 로그인 상태를 유지할 수 있도록 해보자!

구현에는 해당 벨로그 글을 참고했다.

먼저 세션과 쿠키에 대해 알 필요가 있어보여 알아보았다.

쿠키(Cookie)는 웹 브라우저에 저장되는 작은 텍스트 파일로, 저장해둔 데이터를 나중에 필요하다면 다시 읽어와서 사용할 수 있도록 한다고 한다. 하지만 로그인 정보와 같은 중요한 개인정보를 쿠키로 저장하게되면 해커들은 쿠키만 얻을 수 있다면 해당 id를 언제든지 뚫어낼 수 있기 때문에 주의해야할 것이다.

세션(Session)이란 서버 측에서 사용자별로 상태를 유지하기 위해 사용하는 기술이라고 한다. 사용자별로 고유한 세션 ID를 부여하고, 이 세션 ID를 통해 각 사용자의 상태정보를 관리한다고 한다.
세션은 서버가 관리하므로 서버에 저장하는 방식이라 데이터 크기에 제한이 없다. 다만 세션 ID는 클라이언트의 쿠키에 저장된다.
세션은 쿠키보다는 안전하지만 세션 하이재킹과 같은 공격에 대비해야한다고 한다.

세션과 쿠키는 하는 일은 비슷하지만, 보안과 데이터 저장측면에서는 세션이 유리해보인다. 따라서, 우리는 세션을 통해 로그인을 구현해보도록 할 것이다.

위에 올려둔 참고글에서는 두 가지 방법을 제시하는데, 여기서는 두번째 방법인 Servlet이 제공하는 Session을 사용해보도록 하자.

먼저, HttpServletRequest를 통해 session을 추가해주자. 그런 후, SessionConst인터페이스타입에 sessionId변수에 id를 저장하는 방식으로 session에 id를 저장하도록 하자.

//SessionConst.java//
public interface SessionConst {
    String sessionId ="LOGIN_MEMBER";
}

//Controller 수정//
 //로그인 처리
    @PostMapping(value = "/user/login")
    public String handleLogin(@RequestBody LoginRequest loginRequest, HttpServletRequest request){
        User user = userService.Login(loginRequest);
        if(user != null){
            HttpSession session = request.getSession();
            session.setAttribute(SessionConst.sessionId, user.getId());
            return "success";
        }
        else return "fail";
    }

이제 세션에 id가 저장되어있고, 세션이 종료되기 전까지(로그아웃을 하거나 서버를 닫기 전까지)는 로그인 상태가 유지될 것이다. 해당 세션이 진짜로 유효한지 확인하기 위해, 상태 페이지를 만들어서 기존의 로그인 버튼으로 사용되던 부분에 login이 되어있을 시, 상태 페이지로 넘어가도록 하여 세션이 유효한지 확인해보자.

let isLogin = false;

if(localStorage.getItem("id") != null) isLogin = true; //로그인 시, id를 localStorage에 저장해두기로 해놓았으므로, 존재한다면 로그인이 된걸로 취급.
function updateUserMenu() {
        userMenu.innerHTML = "";
        const loginButton = document.createElement("button");
        loginButton.classList.add("login_button");
        loginButton.innerHTML = "<img alt='user' src='/images/icons/login.png'>";
        if(!isLogin)
            loginButton.addEventListener("click", redirectToLoginPage);
        else{
            loginButton.addEventListener("click", function () {
                window.location.href = "/user/status";
            });
        }
        userMenu.appendChild(loginButton);
    }

이제 상태페이지와 로그아웃 시스템을 매핑해주고 실험해보도록 하자.

컨트롤러 매핑

 @GetMapping(value = "/user/status")
    public String gotoStatusPage(HttpServletRequest request, Model model){
        HttpSession session = request.getSession(false);
        if(session == null){
            return "user/login";
        }
        String id = (String)session.getAttribute(SessionConst.sessionId);
        Optional<User> findUserOptional = Optional.ofNullable(userService.isIdExists(id));
        User user = findUserOptional.orElse(null);
        if(user == null)
            return "user/login";
        model.addAttribute("user", user);
        return "user/status";
    }

    @PostMapping("logout")
    public String logout(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if(session == null) {
            return "redirect:/";
        }
        session.invalidate(); //세션종료
        return "redirect:/";
    }

상태페이지

<!DOCTYPE html>
<html lang="ko">
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>회원정보</title>
</head>
<body>
<h1>회원정보</h1>
<div th:text="|${user.id}님 환영합니다.|"></div>
<form th:action="@{/logout}" method="post">
    <button type="submit">로그아웃</button>
</form>
</body>
</html>

실험을 위해 아이디를 두 개 준비했다. 로그아웃을 눌렀을 때, 세션이 종료되면서, 정상적으로 작동이 되는지 확인하기 위해서이다.


다음 gif에서 볼 수 있듯이 정상작동하는 것을 확인할 수 있다! 다음시간에는 회원정보 창에서 회원가입 때 작성한 회원정보를 볼 수 있도록 만들고, 본격적인 쇼핑몰 만들기에 들어가보도록 하자!!

++20240524) 간단하게 회원정보 창을 만들어보았다. 타임리프를 통해 user값을 받아와서 js를 통해 해당 텍스트를 가공하여 표시할 수 있도록 했다. 작업은 앞서 로그인,회원정보등의 페이지를 만들면서 어느정도 익숙해진거같다.

<!DOCTYPE html>
<html lang="ko">
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>회원정보</title>
    <script src="/js/status.js"></script>
    <link href="/css/status.css" rel="stylesheet" />
</head>
<body>
<div class="header">
    <a href="/" id="home_logo">
        <img src="/images/icons/logo.png" />
    </a>
    <h1>회원정보</h1>
    <form th:action="@{/logout}" method="post">
        <button id="logout" type="submit">로그아웃</button>
    </form>
</div>
<div class="container">
    <div class="form-group">
        <label>아이디 : </label>
        <div class="box" id="id" th:text="|${user.id}|"></div>
        <hr>
        <label>이름 : </label>
        <div class="box" id="name" th:text="|${user.name}|"></div>
        <hr>
        <label>생년월일 : </label>
        <div class="box" id="birth" th:text="|${user.birth}|"></div>
        <hr>
        <label>휴대폰 번호 : </label>
        <div class="box" id="phone" th:text="|${user.phone}|"></div>
        <hr>
        <label>이메일 : </label>
        <div class="box" id="email" th:text="|${user.email}|"></div>
        <hr>
        <label>주소 : </label>
        <div class="box" id="place" th:text="|${user.place}|"></div>
    </div>
</div> <!-- /container -->
<form th:action="@{/modify}" method="get">
    <button id="mod" type="submit">회원정보 수정</button>
</form>
</body>
</html>

추후에는 modify에 대한 컨트롤러도 만들어서 회원정보를 수정하는 기능도 만들어볼 것이다.

회원정보를 회원가입을 통해 다음과 같이 저장해두었다.

위의 정보중, 생년월일과 휴대폰 번호는 바로 출력하기에는 가독성이 떨어진다고 생각했다.
그래서 js를 통해 페이지가 로드되면, 타임리프로 해당 사용자의 생년월일과 휴대폰 번호를 받아내서 text값으로 가지게 됐다면, 이 text를 가공하여 나타내도록 하였다.

document.addEventListener("DOMContentLoaded", function() {
    var b = document.getElementById("birth").innerText;
    document.getElementById("birth").innerText
        = b.substring(0,4) + "년 " + b.substring(4,6) + "월 " + b.substring(6,8) + "일";

    var p = document.getElementById("phone").innerText;
    document.getElementById("phone").innerText
        = p.substring(0,3) + "-" + p.substring(3,7) + "-" + p.substring(7,11);


});

결과적으로는 다음과 같이 회원정보가 나타나게된다.

여기서 살짝 놀라웠던 점이라면 개발자도구로 html을 확인하면 th:text를 통해 받아온 생년월일과 휴대폰번호는 가공된 텍스트가 아닌 th:text로 받아낸 원래 텍스트라는 점이다. 즉, 잘만 이용한다면 html에서조차도 데이터를 약간 숨겨낸 후, js를 통해 복호화하여 나타내면 html파일이 드러나더라도 개인정보를 보호시켜줄 수도 있겠다는 생각을 했다. (이게 효과가 있을지는 잘 모르겠다..)

아무튼 회원정보창까지 만들어냈으니 다음시간에는 본격적으로 상품을 데이터베이스에 추가해두고, 이를 장바구니에 넣을 수 있도록 만들어보자.

profile
모르는 것 정리하기

0개의 댓글