Spring/전자 결재 Project

[Spring] 전자결재 : 로그인

v조아리v 2023. 12. 28. 13:36
반응형
저번에 설계한 회원 Table을 이용해서 로그인 기능을 구현해주도록 합니다.

 

1. 로그인 페이지

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

<c:set var="path" value="${ pageContext.request.contextPath }" />
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<link rel="stylesheet" href="/resources/assets/css/style.css">
	<title>Login Page</title>
</head>
<body>

	<div class="approval-container">
		<div class="document">
			<h2>로그인</h2>
			<form id="login_form">
				<label for="id-input">ID : </label>
				<input type="text" id="id-input" name="id">
				<hr>
				<label for="pw-input">PW : </label>
				<input type="password" id="pw-input" name="pw">
				<button id='login_btn'>로그인</button>
			</form>
		</div>
	</div>
</body>
</html>

 

2. (로그인 성공 시) 넘어갈 페이지 (list.jsp)

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

<c:set var="path" value="${ pageContext.request.contextPath }" />
<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<link rel="stylesheet" href="/resources/assets/css/style.css">
	<title>List Page</title>
</head>
<body>

	<h3>리스트 페이지</h3>
	<p>${sessionName } ${sessionPst }님 환영합니다.</p>
	<a href="${path }/logout">로그아웃</a>

</body>
</html>

 

3. 로그인 Controller

@Controller
public class MemController {
	
	@Autowired
	private MemService memSer;
	
	//Login
	@RequestMapping(value = "/login", method = RequestMethod.GET)
	public String login() {
		return "login";
	}
	
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public String loginPost(@RequestParam Map<String, Object> param, HttpServletRequest request, RedirectAttributes redirectAttributes) {
		Map<String, Object> getMemInfo = memSer.getMemInfo(param);
		
		if(getMemInfo != null && getMemInfo.get("pw").equals(param.get("pw"))) {
			HttpSession session = request.getSession();
			session.setAttribute("sessionName", getMemInfo.get("memName"));
			session.setAttribute("sessionId", getMemInfo.get("id"));
			session.setAttribute("sessionPst", getMemInfo.get("memPosition"));
			
			return "redirect:/list";
		} else {
			if(getMemInfo == null || getMemInfo.isEmpty()) {
				redirectAttributes.addFlashAttribute("msg", "등록되지 않은 사용자입니다.");
			} else if (getMemInfo != null && !getMemInfo.get("pw").equals(param.get("pw"))) {
				redirectAttributes.addFlashAttribute("msg", "비밀번호가 일치하지 않습니다.");
			}
			
			return "redirect:/login";
		}
	}
}

 

4. 로그인 Service, Dao

// Service
public interface MemService {
	
	Map<String, Object> getMemInfo(Map<String, Object> param);

}

// Service Impl
@Service
public class MemServiceImpl implements MemService{
	
	@Autowired
	private MemDao memDao;
	
	@Override
	public Map<String, Object> getMemInfo(Map<String, Object> param){
		return memDao.getMemInfo(param);
	}

}

// Dao
public interface MemDao {
	
	Map<String, Object> getMemInfo(Map<String, Object> param);

}

// Dao Impl
@Repository
public class MemDaoImpl implements MemDao{
	
	@Autowired
	private SqlSessionTemplate sqlSession;
	
	@Override
	public Map<String, Object> getMemInfo(Map<String, Object> param){
		return sqlSession.selectOne("MemMapper.getMemInfo", param);
	}

}

 

5. Mapper

<?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="MemMapper">
	<resultMap id="Member" type="java.util.HashMap">
		<result column="MEM_SEQ" property="memSeq" />
		<result column="MEM_NAME" property="memName" />
		<result column="MEM_POSITION" property="memPosition" />
		<result column="ID" property="id" />
		<result column="PW" property="pw" />
	</resultMap>
	
	<select id="getMemInfo" resultMap="Member">
		select
			mem_seq,
			mem_name,
			decode(mem_position,'aa','부장','bb','과장','cc','대리','dd','대리') mem_position,
			id,
			pw
		from e_approval_member
		where id = #{id}
	</select>
	
</mapper>

Member의 DB속 계급에는 실제 계급 대신 코드로 저장될 테니 코드를 직급으로 반환해주는 Decode를 작성해 줍니다.

6. 로그아웃 구현 (Controller)

	// Logout
	@RequestMapping(value="/logout")
	public String logout(HttpSession session) {
		session.invalidate();
		return "redirect:/login";
	}

 

7. 로그인 건너 뛰고 리스트 페이지로 갈 수 없게 기능 구현 (list 페이지 Controller)

@Controller
public class AppController {
	
	@RequestMapping(value = "/list", method = RequestMethod.GET)
	public String list(HttpServletRequest request, RedirectAttributes redirectAttributes) {
		HttpSession session = request.getSession();
		String sessionId = (String) session.getAttribute("sessionId");
		
		if(sessionId == null) {
			redirectAttributes.addFlashAttribute("msg", "로그인 하세요");
			return "redirect:/login";
		}
		
		return "list";
	}

}

세션이 없으면 다시 로그인 화면으로 가도록 처리해 줍니다.

8. 로그인 관련 JQuery (유효성 검사, 로그인 정보 일치 여부 검사)

	<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous"></script>
	<script>
		$(function(){
			var msg = "${msg}";
			
			if(msg !== "" && msg !== null){
				alert(msg);
			}
			
			$("#login_btn").on('click', function(event){
				event.preventDefault();
				
				var idLength = $("#id-input").val().length;
				var pwLength = $("#pw-input").val().length;
				
				if(idLength == 0 && pwLength == 0){
					alert("로그인 정보를 입력하세요");
				} else if (idLength == 0 && pwLength > 0){
					alert("아이디를 입력하세요");
				} else if (idLength > 0 && pwLength == 0){
					alert("비밀번호를 입력하세요");
				} else {
					var idChk = $("#id-input").val();
					var pwChk = $("#pw-input").val();
					const regexaA0 = /^[a-z|A-Z|0-9|]+$/;
					
					if(!regexaA0.test(idChk) && regexaA0.test(pwChk)){
						alert("아이디가 유효하지 않습니다");
						$("#id-input").val('').focus();
					} else if (regexaA0.test(idChk) && !regexaA0.test(pwChk)){
						alert("비밀번호가 유효하지 않습니다");
						$("#pw-input").val('').focus();
					} else if (!regexaA0.test(idChk) && !regexaA0.test(pwChk)){
						alert("로그인 정보가 유효하지 않습니다");
						$("#pw-input").val('');
						$("#id-input").val('').focus();
					} else {
						$("#login_form").attr({
							"action" : "/login",
							"method" : "post"
						}).submit();
					}
				}
			})
			
		})
	</script>

<input> 태그에 입력 되어 있는 값의 길이를 이용해서 적었는지 안 적었는지 체크를 해주고, 유효성 검사를 통해 제대로 입력하지 않은 곳의 값을 지워주고 바로 입력할 수 있게 .val('').focus()를 해줍니다.

9. 결과물

로그인 화면
입력 여부 체크
유효성 검사
틀린 정보 입력 시
로그인 성공 시 & 로그아웃 결과

후기

로그인과 로그아웃 부분은 상대적으로 간단한 구현이었기 때문에 신속하게 완료했습니다. 보통 아이디와 비밀번호의 조건이 서로 다르기 때문에 제 코드에서처럼 하나의 기준으로만 검사하지 않고 각각의 조건을 고려해야 할 것으로 예상되지만, 테스트를 원활히 진행하기 위해 통일시켰습니다.

또한, URL을 통한 로그인 없이 다른 페이지로의 이동을 방지하기 위한 검사 및 리다이렉트 기능을 구현하는 것은 어려운 일은 아니었지만, 매우 중요한 부분이라고 판단했습니다. 더불어, 로그인이 필요한 페이지에 매번 해당 기능을 작성하는 것은 번거로우므로 별도의 메소드나 클래스로 추상화하는 것이 좋겠다는 생각을 했습니다.
반응형