본문 바로가기
SpringBoot

Spring-Boot Blog Project 4. 로그인 화면 구현하기

by 김마리님 2020. 7. 20.

이전 글 

Spring-Boot Blog Project 3. 회원가입 화면 구현하기

 

Spring-Boot Blog Project 3. 회원가입 화면 구현하기

Spring-Boot Blog Project 2. nav, footer 제작 Spring-Boot Blog Project 2. nav, footer 제작 Spring-Boot Blog Project 1. 기본 세팅 Spring-Boot Blog Project 1. 기본 세팅 사용할 기본 Dependency STS에서..

itstudy-mary.tistory.com

 

로그인도 회원가입과 유사하다.

1. 로그인으로 이동하는 로직,

2. 로그인이 진행되는 프로세스

 

앞서 글의 indexController에서 로그인 이동 로직은 생성해 놓았다.

 

로그인 jsp 화면의 소스코드는 

https://itstudy-mary.tistory.com/119?category=919831

 

Servlet 과 JSP를 이용한(모델2 형식) 블로그 만들기(3) - 부트스트랩을 이용한 웹 폼 만들기

웹페이지를 만들다보면 CSS 덕분에 시간이 다 가는 경우가 있다. 우리는.. 일단 프론트엔드가 위주가 아니므로 편리한 부트스트랩을 이용하고자 한다. 부트스트랩은 트위터가 만든 프론트엔드 ��

itstudy-mary.tistory.com

지난 jsp 블로그를 생성하며 만들어둔 코드를 참조하여 만들었다.

 

더보기

LoginForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<%@ include file="../layout/header.jsp" %> 

<div class="container">
   <form class="was-validated" >
     
     <div class="form-group">
       <label for="username">Username:</label>
       <input type="text" id="username" class="form-control" placeholder="Enter username" required>
       <div class="valid-feedback">Valid.</div>
       <div class="invalid-feedback">Please fill out this field.</div>
     </div>
     
     <div class="form-group">
       <label for="pwd">Password:</label>
       <input type="password" id="password" class="form-control"  placeholder="Enter password" required>
       <div class="valid-feedback">Valid.</div>
       <div class="invalid-feedback">Please fill out this field.</div>
     </div>

     <button type="button" id="btn-login" class="btn btn-primary">로그인</button>
   </form>
</div>

<script src="/js/user.js"></script>
<%@ include file="../layout/footer.jsp" %>

 

동일하게 인증을 거치지 않는 유저로직이므로, 같은 js 파일에 로그인 자바스크립트도 구현한다.

 

user.js

let index={
		//리스너
		init : function(){
			$("#btn-save").on("click",()=>{ //this 바인딩 할 필요 없이 바로 부모의 this를 찾음.
				//콜백
				this.save();
			});
			
			$("#btn-login").on("click",()=>{ //this 바인딩 할 필요 없이 바로 부모의 this를 찾음.
				//콜백
				this.login();
			});
		}, //이벤트 리스닝 바인딩 함수
		
		save : function(){
			let data={
					username:$("#username").val(),
					password:$("#password").val(),
					email:$("#email").val()		
			};
			
			$.ajax({
				type: "POST",
				url: "/auth/joinProc",
				data: JSON.stringify(data),
				contentType : "application/json; charset=utf-8", //스프링의 데이터 형식 인식 -> 오브젝트 변환
				dataType : "json"	
			}).done(function(resp){
				console.log(resp);
				alert("회원가입이 완료되었습니다.");
				location.href="/";
			}).fail(function(error){
				console.log(error);
				alert("회원가입 실패");
			});
		}, //이벤트 리스닝 실제 실행 함수
		
		login : function(){
			let data={
					username:$("#username").val(),
					password:$("#password").val()	
			};
			
			$.ajax({
				type: "POST",
				url: "/auth/loginProc",
				data: JSON.stringify(data),
				contentType : "application/json; charset=utf-8", //스프링의 데이터 형식 인식 -> 오브젝트 변환
				dataType : "json"	
			}).done(function(resp){
				console.log(resp);
				if(resp.statusCode==1){
					alert("로그인 성공");
				location.href="/";
				}else{
					alert("아이디와 패스워드를 확인하세요.");
				}
			}).fail(function(error){
				console.log(error);
				alert("로그인 실패");
			});
		} //이벤트 리스닝 실제 실행 함수
}

index.init();

 

이제 실제 로그인 로직 함수를 처리해줄 java - MyBatis 코드를 생성하자.

이 코드는 컨트롤러로 이동한다.

 

UserController.java

package com.mary.blog.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import com.mary.blog.controller.dto.CommonRespDto;
import com.mary.blog.model.User;
import com.mary.blog.service.UserService;

@Controller
public class UserController {

	@Autowired
	private UserService userService;
	
	@PostMapping("auth/joinProc")
	public @ResponseBody CommonRespDto<?> joinProc(@RequestBody User user) { //key-value 데이터가 아님
		userService.회원가입(user);
		return new CommonRespDto<String>(1,"회원 가입 성공");
	}
	
	@PostMapping("auth/loginProc")
	public @ResponseBody CommonRespDto<?> loginProc(@RequestBody User user, HttpSession session){
		User persistUser=userService.로그인(user);
		if(ObjectUtils.isEmpty(persistUser)) {
			return new CommonRespDto<String>(-1,"로그인 실패");
		}else {
			//세션 등록
			session.setAttribute("principal", persistUser);
			return new CommonRespDto<String>(1,"로그인 성공");
		}
	}
}

 

userService에서 "로그인" 서비스를 생성하자.

 

userService.java

package com.mary.blog.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mary.blog.model.User;
import com.mary.blog.repository.UserRepository;


//Controller, Repository(MyBatis를 쓰는 경우 MapperScan하기 때문에 선택),Configuration,Service(트랜젝션 호출),Component
//RestController,Bean

@Service //IoC
public class UserService {
	
	@Autowired
	private UserRepository userRepository; //DI
	
	@Transactional 
	public int 회원가입(User user) {
		try {
			userRepository.save(user);
			return 1;
		} catch (Exception e) {
			e.getMessage();
			return -1;
		}	
	}
	
	@Transactional (readOnly = true) //데이터 변경을 허용하지 않음 -> 정합성을 위함
	public User 로그인(User user) {

			User selectUser= userRepository.login(user);
			return selectUser;

	}
	
}

이 때, select함수에는 readOnly를 붙여야 한다.

왜냐면, 기존 jsp같은 경우는 다같이 한 서버에서 이용하지 않았었다. 그러나 커넥션 풀 방식을 이용하게 되면서 데이터베이스를 한번에 이용할 수 있는 사람이 1명 이상이 되었기 때문이다. 다수가 이용할 때, 데이터의 변경이 생기면 데이터를 read하는 시점에 있는 사람들은 이용하는 시간에 따라 다른 데이터를 접하게 되고, 이렇게 되면 데이터의 정합성이 떨어진다. 따라서, 유저가 커넥션 풀에서 데이터를 select 하는 순간에는 데이터의 변경값을 절대 참조하지 않겠다는 뜻에서 readOnly를 걸어 정합성을 유지하게 된다.

 

이제 repository에서 login 함수를 만들자.

 

-UserRepository.java

package com.mary.blog.repository;

import com.mary.blog.model.User;

public interface UserRepository {
	public void save(User user);
	public User login(User user);
}

 

그리고, Repository를 스캔하는 xml에서 참조할 쿼리를 생성한다.

 

-user.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.mary.blog.repository.UserRepository">
	<insert id="save">
		INSERT INTO user(username,password,email,createDate)
		VALUES (#{username}, #{password}, #{email}, now()) 
	</insert>
	<select id="login" resultType="com.mary.blog.model.User">
		SELECT id, username, email,profile,createDate
		FROM user
		WHERE username=#{username} and password=#{password}
	</select>
</mapper>

 

이제, 비밀번호와 아이디가 데이터베이스에서 일치한다면 가득 찬 User객체를, 없다면 빈 객체를 리턴한다.

따라서, ObjectUtil 함수를 통해 객체를 받아오는 변수가 비어있다면 로그인 실패로 -1을,

객체를 받아오는 변수가 비지 않았다면 1을 리턴한다.

 

이제 로그인을 하면, 로그인을 했다는 증거로 내부에 세션을 남겨두는데, 그 세션에 따라 nav의 값을 다르게 조절한다.

jstl을 이용하여 세션 값이 비어있을 때는 로그인, 회원가입으로 가는 하이퍼링크를, 세션값이 차있을때는 글쓰기, 수정하기, 로그아웃을 하는 하이퍼링크를 출력한다.

 

header.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

<!DOCTYPE html>
<html lang="en">
<head>
<title>Mary Blog</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

</head>
<body>

	<nav class="navbar navbar-expand-md bg-dark navbar-dark">
		<a class="navbar-brand" href="#">Mary Blog</a>
		<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#collapsibleNavbar">
			<span class="navbar-toggler-icon"></span>
		</button>
		<div class="justify-content-between collapse navbar-collapse" id="collapsibleNavbar">
			<ul class="navbar-nav">
				<c:choose>
					<c:when test="${empty sessionScope.principal }">
						<li class="nav-item"><a class="nav-link" href="/auth/loginForm">로그인</a></li>
						<li class="nav-item"><a class="nav-link" href="/auth/joinForm">회원가입</a></li>
					</c:when>
					<c:otherwise>
						<li class="nav-item"><a class="nav-link" href="/post/saveForm">글쓰기</a></li>
						<li class="nav-item"><a class="nav-link" href="/user/updateForm">회원수정</a></li>
						<li class="nav-item"><a class="nav-link" href="/auth/logout">로그아웃</a></li>
					</c:otherwise>
				</c:choose>
						
			</ul>
			
			
			
			
		</div>
	</nav>
	<br>

 

반응형