D+55::스프링_회원가입/로그인(session이용/event(js) 이용)/부트스트랩

Am.Vinch·2022년 9월 14일
0
post-thumbnail

20220914 wed

실습

  • 모달 창이 닫힐 때, 안의 내용을 모두 지워라.
    (모달 창 안의 내용을 입력시, 지워지지않고 남아있기 때문에)
  • 방법) 부트스트랩 > component > modal > event (js 이용)
    : hidden.bs.modal(모달이 닫힌 후 실행 이벤트) 코드 사용
    https://getbootstrap.com/docs/5.2/components/modal/

EVENT

JS_Event 기능 참고
click – 클릭했을 때 이벤트 발생
dbclick – 더블클릭했을 때 이벤트 발생
mousedown – 마우스를 눌렀을 때 발생
mouseenter – 마우스가 진입했을 때
mouseleave – 마우스가 노드 영역을 벗어났을 때
mousemove – 마우스가 움직였을 때
hover – 마우스가 노드에 들어오거나 벗어날 때
change – 노드의 값이 변경되었을 때
focus – 노드가 포커스를 획득했을 때
keydown – 키보드를 눌렀을 때
keypress – 키보드를 계솟 누르고 있을 때
keyup – 키보드를 눌렀다가 떼었을 때


  • 모달이 있는 top.html에서 js 작업한다
    <script type="text/javascript" th:src="@{/js/layout/top.js}"></script>으로 이미 js불러오기 때문에 top.js 파일로 가서 만든다

실습내용
1. 로그인 모달창을 닫으면, input태그를 초기화시킨다
2. 로그인 기능 구현
(로그인 성공 시, 세션에 로그인한 회원의 id,이름,관리자여부를 등록한다)
3. ajax 사용하여만든다.
3-(1).로그인 성공시, alert('반갑습니다')를 띄운 후, 상품목록페이지로 이동한다.
3-(2).로그인 실패시, alert('아이디나 비밀번호가 잘못되었습니다')를 띄운 후, 로그인 모듈창이 닫혀지지않아야한다. 다시 로그인 할 수 있도록 만든다.

  • member-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="memberMapper">
	<!-- 패키지명 클래스명 -->
 	<resultMap type="Kh.study.shop.member.vo.MemberVO" id="selectMember">
		<id column="MEMBER_ID" 	property="memberId"/>
		<result column="MEMBER_PW" property="memberPw"/>
		<result column="MEMBER_NAME" property="memberName"/>
		<result column="MEMBER_ADDR" property="memberAddr"/>
		<result column="ADDR_DETAIL" property="addrDetail"/>
		<result column="MEMBER_EMAIL" property="memberEmail"/>
		<result column="IS_ADMIN" property="isAdmin"/>
		<result column="MEMBER_STATUS" property="memberStatus"/>
	</resultMap>  

<!-- 회원가입 -->
<!--  기본값 있는 관리자여부 제외 모두 입력해야함
     회원상태는 회원가입하면 기본적으로 활성화상태이기때문에 ACTIVE 입력-->
<insert id="join">
	INSERT INTO SHOP_MEMBER (MEMBER_ID,MEMBER_PW ,MEMBER_NAME,MEMBER_ADDR,ADDR_DETAIL,MEMBER_EMAIL,MEMBER_STATUS)
	VALUES (#{memberId},#{memberPw},#{memberName},#{memberAddr},#{addrDetail},#{memberEmail},'ACTIVE')
</insert>

<!-- 로그인 -->
  <select id="login" resultMap="selectMember">
	SELECT MEMBER_ID,MEMBER_NAME,IS_ADMIN
	FROM SHOP_MEMBER
	WHERE MEMBER_ID = #{memberId}
	AND MEMBER_PW = #{memberPw}
</select>  
</mapper> 
  • MemberService
package Kh.study.shop.member.service;

import Kh.study.shop.member.vo.MemberVO;

public interface MemberService {
	//회원가입
	void join(MemberVO memberVO);
	//로그인
	MemberVO login(MemberVO memberVO);
}
  • MemberServiceImpl
package Kh.study.shop.member.service;

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

import Kh.study.shop.member.vo.MemberVO;
import lombok.RequiredArgsConstructor;

@Service("memberService")
//@Transactional
//@RequiredArgsConstructor
public class MemberServiceImpl implements MemberService {
	@Autowired//어노테이션으로 객체생성
	private SqlSessionTemplate sqlSession;
	
	//join
	@Override
	public void join(MemberVO memberVO) {
		 sqlSession.insert("memberMapper.join", memberVO);
	}
	//login
	@Override
	public MemberVO login(MemberVO memberVO) {
		return sqlSession.selectOne("memberMapper.login",memberVO);
	}

}
  • item_list.html
<!DOCTYPE html>
<!--base_layout페이지와 같이보여지는 페이지들은 html과 div 이외 모든 태그 필요없다!!!-->

<html xmlns:th="http://www.thymeleaf.org"
	xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout" 
	layout:decorate="layout/base_layout"><!-- base_layout.html파일 처럼 레이아웃하겠다. 이페이지는 base_layout페이지랑 같이 열리는 것이다.-->

<!-- 같이열리는 base_layout페이지에서 content란 영역은 이 부분이 열린다. -->
<div layout:fragment="content">
	<!-- <th:block layout:fragment="css"></th:block> -->
	<span id="test" onclick="">상품목록페이지</span>
	<!-- <th:block layout:fragment="script"></th:block> -->
	
	<script type="text/javascript">
		//(test) 
		//const mySpan = document.querySelector('#test');
	    /* 부트스트랩 event 사용해보기 */
	    //자바스크립트에서는 매개변수로 메소드 funtion(){}이 들어올 수 있다
		//mySpan.addEventListener('click', function(){//span태그가 클릭을 할 때 실행
		//	alert(111);			
		//}); 
		
	</script>
</div>
</html>

  • base_layout.html

    ※(주의사항)
    : ajax사용위해 제이쿼리문법 가장먼저 로딩한다 <script src="https://code.jquery.com/jquery-latest.min.js"></script>

<!-- 모든 페이지에 적용시키는 파일 -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"
		xmlns:layout="http://www.ultra.net.nz/thymeleaf/layout"><!-- 타임리프,레이아웃 기능 사용하겠다 -->
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<!-- 레이아웃페이지에서 앞으로 모든 내용들이 나오기때문에 여기에 부트스트랩가져오면 모두 자동 적용된다 -->
<!-- 외부의 부트스트랩 사용위해 가져오기 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
<!-- css가져오기  단,static 경로설정은 /부터 넣고 시작한다!(template와의 차이)-->
<link href="/css/common.css" rel="stylesheet">
</head>
<body>
<div class="container">
	<!-- 고정된 top.html 화면 -->
	<div class="row">
		<div class="col">
			<div th:replace="fragment/top::top"></div><!-- fragment폴더 안에 top html파일을 top이라는 이름으로, fragment를 불러와 대체하겠다 -->
		</div>
	</div>
	<!-- 계속바뀌는 베이스화면 -->
	<div class="row">
		<div class="col">
			<div layout:fragment="content"></div>
		</div>
	</div>
</div>
<!-- ajax사용위해 제이쿼리문법 가장먼저 로딩한다 -->
<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<!-- 화면에 나타나지는 내용은 없지만,
부트스크랩 자바스크랩은 바디태그 끝나기전 해야하기때문에 footer라는 html파일에 자바스크립트를 붙여넣어서 아래 div영역으로 표시한다.(footer.html 삭제) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js" integrity="sha384-u1OknCvxWvY5kfmNBILK2hRnQC3Pr17a+RTT6rIHI7NnikvbZlHgTPOOmMi466C8" crossorigin="anonymous"></script>
</body>
</html>
  • top.html

base_layout페이지와 같이보여지는 페이지들은 html과 div 이외 모든 태그 필요없다

  • 부트스트랩으로 글자 위치 조정
    text-end : 오른쪽 text-start : 왼쪽 text-center :가운데
  • 해석) 로그인 클릭시, modal 실행, 어떤 id값으로 modal을 띄울지: data-bs-toggle="modal" data-bs-target="#join_modal"
  • 로그인 -> 세션이용 데이터 form태그로 보내주기
  • class값은 고정 건드리면 안됨. 부트스트랩사용하기위한 의미부트스트랩에서 가져왔기때문에 함부러 수정x 형식에 맞게 해야사용가능하다
  • class="row g-3" :label은 input태그의 라벨(스티커)이다. 웬만하면, for값과 id값이 같아야한다. html 파일 내에 memberId 라는 id값이 여러개 존재한다.(중복발생)
    단,오류가 발생하지는 않지만, js에서 태그를 선택할 때 값이 중복이 되어 주의해야한다.
  • submit은 ajax에서 사용하는 것이 아니기 때문에 onclick이용해서 ajax로 갈 수 있다.
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div th:fragment="top">
	<div class="row">
		<div class="col text-end">
			<span data-bs-toggle="modal" data-bs-target="#join_modal">JOIN</span>
			<span data-bs-toggle="modal" data-bs-target="#login_modal">LOGIN</span>
		</div>
	</div>
	<div class="row">
		<div class="col text-center"> 
			<h1>S H O P</h1>
		</div>
	</div>
	<!-- MENU -->
	<div class="row">
		<div class="col">
			<nav class="navbar navbar-expand-lg bg-light">
			  <div class="container-fluid">
			    <a class="navbar-brand" href="#">Naver</a>
			    <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
			      <span class="navbar-toggler-icon"></span>
			    </button>
			    <div class="collapse navbar-collapse" id="navbarNav">
			      <ul class="navbar-nav">
			        <li class="nav-item">
			          <a class="nav-link active" aria-current="page" href="#">Home</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link" href="#">Features</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link" href="#">Pricing</a>
			        </li>
			        <li class="nav-item">
			          <a class="nav-link disabled">Disabled</a>
			        </li>
			      </ul>
			    </div>
			  </div>
			</nav>
		</div>
	</div>
	
<!-- login 클릭시 실행 Modal  -->
<div class="modal fade" id="login_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel" style="font-weight: bold;">L O G I N</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      
      <!-- 로그인창  -->
      <div class="modal-body">
		<form class="row g-3" name="formLogin" method="post" action="/member/login" >
		  <div class="mb-3">
		    <label for="memberId" class="form-label" >ID</label>
		    <input id="memberId" type="text" class="form-control" name="memberId" aria-describedby="emailHelp" placeholder="Input your ID"  >
		  </div>
		  <div class="mb-3">
		    <label for="memberPw" class="form-label">Password</label>
		    <input type="password" class="form-control" name="memberPw"  id="memberPw" placeholder="Input your password">
		  </div>
	      <div class="modal-footer">
	        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
	        <button type="submit" class="btn btn-primary">Login</button>
	        <button type="button" class="btn btn-primary" onclick="goLogin();">AjaxLogin</button>
	      </div>
		</form>
      </div>

    </div>
  </div>
</div>

<!-- join 클릭시 실행 Modal -->
<div class="modal fade" id="join_modal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel" style="font-weight: bold;">J O I N</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
      <!--  너비 조정-->
      <!-- col-12 / col-6 :  한 줄 너비 총 크기 12이기때문에 12분등 중 어느만큼 사용하는건지 -->
	  	<form class="row g-3" action="/member/join" method="post">
		  	<!-- class값은 고정 건드리면 안됨. 부트스트랩사용하기위한 의미 -->
		    <!-- for태그와 id태그 이름은 같게 하도록 -->
		  <div class="col-12">
		    <label for="memberId" class="form-label">ID</label>
		    <input type="text" class="form-control" id="memberId" placeholder="put your ID" name="memberId">
		  </div>
		  <div class="col-12">
		    <label for="memberPw" class="form-label">PASSWORD</label>
		    <input type="password" class="form-control" id="memberPw" name="memberPw" placeholder="put your Password">
		  </div>
		  <div class="col-12">
		    <label for="memberName" class="form-label">NAME</label>
		    <input type="text" class="form-control" id="memberName" name="memberName" placeholder="put your Name">
		  </div>

		 <!-- 주소(상세주소 + 검색버튼추가) -->
		 <!-- 줄을 맞추기위해 실제내용은 화면에 안보이도록 공백문자사용/ class에 form-control 붙여넣기 -->
		  <div class="col-9">
		    <label for="memberAddr" class="form-label"> ADDRESS </label>
		    <input type="text" class="form-control" id="memberAddr" name="memberAddr"  readonly onclick="searchAddr();"><!-- 값변경못하도록 읽기전용속성값부여하기 ( 데이터넘기기 가능) -->
		  </div>
		  <div class="col-3" >
		    <label for="" class="form-label">&nbsp;</label>
			<input type="button" class="btn btn-secondary form-control" onclick="searchAddr();" value="Search">
		  </div>
		  <div class="col-12" >
		    <input type="text" class="form-control" id="addrDetail" name="addrDetail">
		  </div>
		 
		  <div class="col-12">
		    <label for="memberEmail" class="form-label">EMAIL</label>
		    <input type="text" class="form-control" id="memberEmail" placeholder="put your Email" name="memberEmail">
		  </div>
		  
		  <div class="d-grid gap-2 col-12" >
		    <!-- 버튼클릭하나로 모든 데이터 가져가야하므로 무조건 submit!  -->
		    <button type="submit" class="btn btn-primary">JOIN</button>
		  </div>
		</form>
	  </div>      
    </div>
  </div>
</div>
<!-- 카카오API 사용위해 자바스크립트 사용 전, 미리 로드하기 -->
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<!-- js불러오는 방법_기본 (1) -->
<!-- 주의)html 파일 이외의 파일들은 static 폴더 기준이면서 맨앞은 '/'넣어줘야한다!!!(templates X) -->
<!-- src속성값을 넣어주면 해당 파일에 js찾아서 불러온다는 기능 -->
<!-- <script type="text/javascript" src="/js/layout/top.js"></script> -->

<!-- js불러오는 방법_타임리프 (2) -->
<!-- 기본방법과 비슷하지만 @{}사용해야한다! -->
<script type="text/javascript" th:src="@{/js/layout/top.js}"></script>

<!-- 위 방법의 차이점은? -->
<!-- 디자이너와 협업시 용이하다.
이클립스를 사용하지 않는 디자이너분들은 타임리프를 사용해서 타임리프로 된 html파일은 데이터 던져주면,
데이터값을 확인할 수 있다. 타임리프를 사용하지않으면 디자이너분들이 사용하기 불편하기때문에 사용하는 것이다. -->
</div>
</html>

EVENT 사용

hidden.bs.modal :모달이 닫히면 실행

  • top.js
//------------------------------ 변수 ---------------------------------------//
//회원가입 모달
const join_modal = document.querySelector('#join_modal');
//로그인 모달
const login_modal = document.querySelector('#login_modal');



//------------------------------ 함수 정의 ----------------------------------//

//회원가입에서 search 버튼 클릭시, 진행 메소드
function searchAddr() {
	new daum.Postcode({
		oncomplete: function(data) {
			// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분입니다.
			// 예제를 참고하여 다양한 활용법을 확인해 보세요.

			// 팝업에서 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.

			// 도로명 주소의 노출 규칙에 따라 주소를 조합한다.
			// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
			const roadAddr = data.roadAddress; // 도로명 주소 변수
			document.querySelector('#memberAddr').value = roadAddr;

		}
	}).open();
}

//로그인 기능 함수(ajax로 로그인실행시 진행) 
function goLogin(loginInfo) {
	//${sessionScope.loginInfo};
	const member_id = login_modal.querySelector('#memberId').value;
	const member_pw = login_modal.querySelector('#memberPw').value;
	
	
		//ajax start
		//$을 사용하려면, 제이쿼리 문법이기때문에 자스보다 먼저 로딩해야한다
		$.ajax({
			url: '/member/ajaxLogin', //요청경로
			type: 'post',
			data: { 'memberId':member_id ,'memberPw' :member_pw ,'loginInfo' : loginInfo }, //필요한 데이터
			success: function(result) {
				if(result) {/*loginInfo 값이 null아니면(false)*/
					alert('로그인 성공!!!');
				}
				else{
					alert('로그인 실패!!!');
				}
			},
			error: function() {
				alert('로그인 실패');
			}
		});
		//ajax end
	
}


//----------------------------- 이벤트 정의 ---------------------------------//

//////////////////회원가입 모달이 닫히면 실행되는 이벤트(함수 매개변수 event)
join_modal.addEventListener('hidden.bs.modal', function(event) {//모달이 완전히 닫혔을 때 실행
	//--------- <모듈창을 다시 열었을 때, 이전 값들 남아있지 않도록 빈값 만들기> ---------//
	//[방법(1)_기본]
	//회원가입 모달창에 있는 input 태그를 모두들고온다
	//('')안에는 css와 동일하다
	//input태그 중 버튼만 제외하고 모두 선택한다
	//	const modal_inputs = join_modal.querySelectorAll('input:not[type="button"]');

	//반복문돌려서 모든 input태그 ''빈값을 주면 모듈창을 닫았을 때 빈값이다.
	//	for(const inputTag of modal_inputs){
	//		inputTag.value ='';
	//	}
	//[방법(2)_간략]
	// 모달창의 from태그를 선택하여 초기화
	join_modal.querySelector('form').reset();
});


//////////////////로그인 모달이 닫히면 실행되는 이벤트
login_modal.addEventListener('hidden.bs.modal', function(event) {
	login_modal.querySelector('form').reset();
});

Login Session

  • itemController
package Kh.study.shop.item.controller;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import Kh.study.shop.member.vo.MemberVO;

@Controller
@RequestMapping("/item")
class ItemController {

	//상품목록페이지
	@GetMapping("/list")
	//스프링에서 데이터 던질 때 model 객체사용한다
	public String itemList(MemberVO memberVO ,HttpSession session, Model model) {
		
		return "content/item/item_list";//html경우 template부터 차례로 내려오기
	}
}

Login_Ajax

  • MemberController.java

login alert창을 띄우는 방법은 2가지

  • 1) alert창으로 login_result.html
  • 2) ajax사용
    • 로그인 세션 HttpSession session
    • 삼항연산자로 return하기 (자료형 boolean)
      return loginInfo == null? false :true ;
package Kh.study.shop.member.controller;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import Kh.study.shop.member.service.MemberService;
import Kh.study.shop.member.vo.MemberVO;

@Controller
@RequestMapping("/member")
public class MemberController {
	@Resource(name = "memberService")
	private MemberService memberService;
	
	//회원가입
	@PostMapping("/join")
	public String join(MemberVO memberVO) {
		System.out.println(memberVO);
		memberService.join(memberVO);
		
		return "redirect:/item/list";//redirect: 사용해야 컨트롤러 사용한다.(상품목록페이지 )
	}
	
	//로그인(ajax)
	@ResponseBody//ajax사용할때(단,리턴값은 필요한 데이터만! html페이지가 아님!)
	@PostMapping("/ajaxLogin")
	public boolean ajaxLogin(HttpSession session, MemberVO memberVO) {
        MemberVO loginInfo = memberService.login(memberVO);
        // 로그인 정보 세션 저장
        if(loginInfo != null) {
			session.setAttribute("loginInfo", loginInfo);
			
        } 
        // 바로 loginInfo를 주지않고 삼항연산자 사용한다
		return loginInfo == null? false :true ;//자료형 boolean
	}
	//로그인(alert 띄우기)
	@PostMapping("/login")
	public String login(HttpSession session, MemberVO memberVO) {
		//System.out.println(memberVO);
		MemberVO loginInfo = memberService.login(memberVO);
		// 로그인 정보 세션 저장
		if(loginInfo != null) {
			session.setAttribute("loginInfo", loginInfo);
			
		} else {
			System.out.println("null!!!!!");
			session.setAttribute("loginInfo", null);
		}
		return "content/member/login_result";
	}
}
  • login_result.html (alert 사용시)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<th:block th:if="${session.loginInfo == null}"> 
	<script type="text/javascript">
		alert('로그인 실패!!');
		location.href='/item/list';
	</script>
</th:block>
<th:block th:unless="${session.loginInfo == null}"> 
	<script type="text/javascript">
		alert('로그인 성공!!!');
		location.href='/item/list';
	</script>
</th:block>

</body>
</html>
profile
Dev.Vinch

0개의 댓글