ㅁ header.jsp 전 후
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!-- Bootstrap 사용을 위한 CDN -->
<!-- ---------------------------->
<style>
header{height: 50px}
header a{color:black;}
header .profile-img{width:30px;}
</style>
<header class="row m-3">
<div class="col-3 d-flex justify-content-center align-items-center">
<a href=""><img src="${ contextPath }/resources/images/goodee_logo.png" width="100px"></a>
</div>
<div class="col-5"></div>
<div class="col-4 d-flex justify-content-center align-items-center">
<c:choose>
<c:when test="${ empty loginUser }">
<!-- case1. 로그인전 -->
<a href="">회원가입</a> |
<a href="#" data-toggle="modal" data-target="#loginModal">로그인</a>
</c:when>
<c:otherwise>
<!-- case2. 로그인후 -->
<div>
<img class="profile-img" src="${ contextPath }/resources/images/defaultProfile.png">
<a href="">홍길동님</a> |
<a href="">로그아웃</a>
</div>
</c:otherwise>
</c:choose>
</div>
</header>
<nav class="navbar m-3 navbar-expand-sm bg-dark navbar-dark d-flex justify-content-center">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">공지사항</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">일반게시판</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">사진게시판</a>
</li>
</ul>
</nav>
<!-- 로그인 클릭 시 뜨는 모달 (기존에는 안보이다가 위의 a 클릭시 보임) -->
<div class="modal fade" id="loginModal">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">Login</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<form action="로그인요청받아주는서버" method="post">
<!-- Modal Body -->
<div class="modal-body">
<label for="userId" class="mr-sm-2">ID :</label>
<input type="text" class="form-control mb-2 mr-sm-2" placeholder="Enter ID" id="userId" name=""> <br>
<label for="userPwd" class="mr-sm-2">Password:</label>
<input type="password" class="form-control mb-2 mr-sm-2" placeholder="Enter password" id="userPwd" name="">
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button type="submit" class="btn btn-primary">로그인</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">취소</button>
</div>
</form>
</div>
</div>
</div>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!-- Bootstrap 사용을 위한 CDN -->
<!-- ---------------------------->
<style>
header{height: 50px}
header a{color:black;}
header .profile-img{width:30px;}
</style>
<header class="row m-3">
<div class="col-3 d-flex justify-content-center align-items-center">
<a href=""><img src="${ contextPath }/resources/images/goodee_logo.png" width="100px"></a>
</div>
<div class="col-5"></div>
<div class="col-4 d-flex justify-content-center align-items-center">
<c:choose>
<c:when test="${ empty loginUser }">
<!-- case1. 로그인전 -->
<a href="">회원가입</a> |
<a href="#" data-toggle="modal" data-target="#loginModal">로그인</a>
</c:when>
<c:otherwise>
<!-- case2. 로그인후 -->
<div>
<img class="profile-img" src="${ contextPath }<c:out value='${loginUser.profileURL}' default='/resources/images/defaultProfile.png' />">
<a href="">${loginUser.userName}님</a> |
<a href="${contextPath}/member/signout.do">로그아웃</a>
</div>
</c:otherwise>
</c:choose>
</div>
</header>
<nav class="navbar m-3 navbar-expand-sm bg-dark navbar-dark d-flex justify-content-center">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">공지사항</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">일반게시판</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">사진게시판</a>
</li>
</ul>
</nav>
<!-- 로그인 클릭 시 뜨는 모달 (기존에는 안보이다가 위의 a 클릭시 보임) -->
<div class="modal fade" id="loginModal">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">Login</h4>
<button type="button" class="close" data-dismiss="modal">×</button>
</div>
<form action="${contextPath}/member/signin.do" method="post">
<!-- Modal Body -->
<div class="modal-body">
<label for="userId" class="mr-sm-2">ID :</label>
<input type="text" class="form-control mb-2 mr-sm-2" placeholder="Enter ID" id="userId" name="userId" required> <br>
<label for="userPwd" class="mr-sm-2">Password:</label>
<input type="password" class="form-control mb-2 mr-sm-2" placeholder="Enter password" id="userPwd" name="userPwd" required>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button type="submit" class="btn btn-primary">로그인</button>
<button type="button" class="btn btn-danger" data-dismiss="modal">취소</button>
</div>
</form>
</div>
</div>
</div>
- 로그인 인증방식은 세션 방식이다.
- 현재 로그인 요청을 할수있는 form은 구현되어있지 않다. modal로 연결해놨다.
그 안에 form요소가 있다.
- 아이디, 비번을 userId, userPwd로 MemberDto의 필드명과 맞췄다. 그러면 이제 매개변수의 MemberDto에 바로 담긴다.
- 로그인 id, 비번에 required 속성을 추가했다.
- 로그인 form의 action을 작성했다.
- 로그인은 url에 데이터가 노출되면 안되니 post방식으로 넘어온다.
- 홍길동님 바꿨다. 이름.
- 이미지는 2가지 경우다.
프로필 url 경로가 없으면 "${ contextPath }/resources/images/defaultProfile.png"이 보여진다. 기본 이미지다.
프로필 url 경로가 있으면 존재하는 프로필 url 경로가 들어가야 한다.
- <c:out />은 ~다.
ㅁ MemberController
package com.br.spring.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.br.spring.service.MemberService;
import lombok.RequiredArgsConstructor;
@RequestMapping("/member")
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MemberService memberService;
}
- 이게 현재 상태다.
ㅁ MemberService
package com.br.spring.service;
public interface MemberService {
}
package com.br.spring.service;
import com.br.spring.dto.MemberDto;
public interface MemberService {
// 로그인
MemberDto selectMember(MemberDto m);
// 회원가입
int insertMember(MemberDto m);
// 아이디중복체크
int selectUserIdCount(String checkId);
// 회원정보변경
int updateMember(MemberDto m);
// 회원탈퇴
int deleteMember(MemberDto m);
}
ㅁ MemberServiceImpl
package com.br.spring.service;
import org.springframework.stereotype.Service;
import com.br.spring.dao.MemberDao;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class MemberServiceImpl implements MemberService {
private final MemberDao memberDao;
}
package com.br.spring.service;
import org.springframework.stereotype.Service;
import com.br.spring.dao.MemberDao;
import com.br.spring.dto.MemberDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class MemberServiceImpl implements MemberService {
private final MemberDao memberDao;
@Override
public MemberDto selectMember(MemberDto m) {
return memberDao.selectMember(m);
}
@Override
public int insertMember(MemberDto m) {
return memberDao.insertMember(m);
}
@Override
public int selectUserIdCount(String checkId) {
return memberDao.selectUserIdCount(checkId);
}
@Override
public int updateMember(MemberDto m) {
return memberDao.updateMember(m);
}
@Override
public int deleteMember(String userId) {
return memberDao.deleteMember( userId );
}
}
- 마지막 메소드 수정필요.
ㅁ MemberController
package com.br.spring.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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 com.br.spring.dto.MemberDto;
import com.br.spring.service.MemberService;
import lombok.RequiredArgsConstructor;
@RequestMapping("/member")
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MemberService memberService;
@PostMapping("/signin.do")
public void signin(MemberDto m, HttpServletResponse response, HttpSession session, HttpServletRequest request) throws IOException {
MemberDto loginUser = memberService.selectMember(m);
// 로그인 성공시 => 세션에 회원정보를 담고, alert와 함께 메인 페이지로 이동
// 로그인 실패시 => alert와 함께 기존에 보던 페이지 유지(아이디, 비번 입력하는 로그인 폼 유지. 작성하던 입력데이터도 그대로.)
// script문을 응답데이터로 돌려줘서 흐름제어
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<script>");
if(loginUser != null) { // 로그인 성공
session.setAttribute("loginUser", loginUser);
out.println("alert('" + loginUser.getUserName() + "님 환영합니다~');");
out.println("location.href = '" + request.getHeader("referer") + "';"); // 메인페이지가 아닌 이전에 보던 페이지로 이동
}else { // 로그인 실패
out.println("alert('로그인에 실패하였습니다. 아이디 및 비밀번호를 다시 확인해주세요.');");
out.println("history.back();"); // modal이 띄워진 상태였으면 modal도 유지된다.
}
out.println("</script>");
}
}
- 시크릿창으로 테스트하세요. 자동저장때문에. ctrl shift n
ㅁ
ㅁ
- sample_script 다시 한번 실행해서 초기의 db로 돌아왔다.
======================================================
ㅁ 로그아웃
ㅁ MemberController
package com.br.spring.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.br.spring.dto.MemberDto;
import com.br.spring.service.MemberService;
import lombok.RequiredArgsConstructor;
@RequestMapping("/member")
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MemberService memberService;
@PostMapping("/signin.do")
public void signin(MemberDto m, HttpServletResponse response, HttpSession session, HttpServletRequest request) throws IOException {
MemberDto loginUser = memberService.selectMember(m);
// 로그인 성공시 => 세션에 회원정보를 담고, alert와 함께 메인 페이지로 이동
// 로그인 실패시 => alert와 함께 기존에 보던 페이지 유지(아이디, 비번 입력하는 로그인 폼 유지. 작성하던 입력데이터도 그대로.)
// script문을 응답데이터로 돌려줘서 흐름제어
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<script>");
if(loginUser != null) { // 로그인 성공
session.setAttribute("loginUser", loginUser);
out.println("alert('" + loginUser.getUserName() + "님 환영합니다~');");
out.println("location.href = '" + request.getHeader("referer") + "';"); // 메인페이지가 아닌 이전에 보던 페이지로 이동
}else { // 로그인 실패
out.println("alert('로그인에 실패하였습니다. 아이디 및 비밀번호를 다시 확인해주세요.');");
out.println("history.back();"); // modal이 띄워진 상태였으면 modal도 유지된다.
}
out.println("</script>");
}
@GetMapping("/singout.do")
public String signout(HttpSession session) {
session.invalidate();
return "redirect:/";
}
}
==========================================================================
ㅁ singup.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
.smallfont {font-size: 0.8em;}
.uncheck {display: none;} /* 유효성 검사 전일 경우 */
.unusable {color: red;} /* 사용 불가능인 경우 */
.usable {color: green;} /* 사용 가능인 경우 */
</style>
</head>
<body>
<div class="container p-3">
<!-- Header, Nav start -->
<jsp:include page="/WEB-INF/views/common/header.jsp"/>
<!-- Header, Nav end -->
<!-- Section start -->
<section class="row m-3" style="min-height: 500px">
<div class="container border p-5 m-4 rounded">
<h2 class="m-4">회원가입</h2>
<br>
<form action="${contextPath}/member/insert.do" method="post" id="signup_form">
<div class="form-group">
<label for="userId">* ID :</label>
<input type="text" class="form-control" id="userId" name="userId" placeholder="Please Enter ID" required>
<div id="idCheck_result" class="uncheck smallfont"></div>
<br>
<label for="userPwd">* Password :</label>
<input type="password" class="form-control" id="userPwd" name="userPwd" placeholder="Please Enter Password" required>
<div id="pwdCheck_result" class="uncheck smallfont"></div>
<br>
<label for="checkPwd">* Password Check :</label>
<input type="password" class="form-control" id="checkPwd" placeholder="Please Enter Password" required>
<div id="pwdEqualCheck_result" class="uncheck smallfont"></div>
<br>
<label for="userName">* Name :</label>
<input type="text" class="form-control" id="userName" name="userName" placeholder="Please Enter Name" required>
<div id="nameCheck_result" class="uncheck smallfont"></div>
<br>
<label for="email"> Email :</label>
<input type="email" class="form-control" id="email" name="email" placeholder="Please Enter Email"><br>
<label for="phone"> Phone :</label>
<input type="tel" class="form-control" id="phone" name="phone" placeholder="Please Enter Phone (-포함)"><br>
<label for="address"> Address :</label>
<input type="text" class="form-control" id="address" name="address" placeholder="Please Enter Address"><br>
<label for=""> Gender : </label>
<input type="radio" name="" id="Male" value="M">
<label for="Male">남자</label>
<input type="radio" name="" id="Female" value="F">
<label for="Female">여자</label><br>
</div>
<br>
<div class="btns" align="center">
<button type="submit" class="btn btn-primary" disabled>회원가입</button>
<button type="reset" class="btn btn-danger"> 초기화</button>
</div>
</form>
<script>
// 각 항목의 값들이 유효한 값으로 입력되면 true로 변경된다.
let idResult = false;
let pwdResult = false;
let pwdEqualResult = false;
let nameResult = false;
// input 요소들 유효성검사때마다 회원가입 버튼을 활성화 또는 비활성화 처리하는 함수
function validate(){
if(idResult && pwdResult && pwdEqualResult && nameResult){ // 각각이 true 또는 false이므로 조건으로 쓸 수도 있다. // 모든 값들이 다 유효한 값으로 입력되는 순간
$('#signup_form :submit').removeAttr('disabled'); // 회원가입버튼 활성화
}else {
$('#signup_form :submit').attr('disabled', true); // 회원가입버튼 비활성화
}
}
// input 요소에 작성된 값이 없는지 체크하는 함수
function noValueCheck($input, $result){ // 비교할 input 요소 객체, 메세지 출력되는 div요소객체
if($input.val().trim().length == 0){ // 값이 비어있을 경우
$result.removeClass("usable unusable")
.addClass("uncheck")
.text(""); // uncheck활성화 (결과 영역 출력 숨기기)
return false; // 유효성 검사를 진행할 필요가 없기 때문에 false 반환
}
return true; // 유효성 검사를 진행하도록 true 반환
}
// input 요소에 작성된 값이 정규식(유효한패턴)에 만족하는지 체크하는 함수
function regExpCheck($input, $result, regExp, msg1, msg2){
if(regExp.test($input.val())){ // 유효한 값으로 잘 입력됐을 경우
$result.removeClass("uncheck unusable")
.addClass("usable")
.text(msg1);
return true;
}else { // 유효하지 않은 값으로 입력됐을 경우
$result.removeClass("uncheck usable")
.addClass("unusable")
.text(msg2);
return false;
}
}
$(function(){
// 아이디 유효성검사
$('#signup_form #userId').on("keyup", function(){
let regExp = /^[a-z\d]{5,12}$/;
idResult = noValueCheck( $(this), $("#idCheck_result") )
&& regExpCheck( $(this), $("#idCheck_result")
,regExp, "사용가능한 아이디입니다."
, "영문, 숫자 포함 5~12자리로 작성해주세요.");
if(idResult){
$.ajax({
url: '${contextPath}/member/idcheck.do',
async: false,
data: "checkId=" + $(this).val(),
success: function(resData){
if(resData == "NNNN"){
$("#idCheck_result").removeClass("uncheck usable")
.addClass("unusable")
.text("중복된 아이디가 존재합니다. 다시 입력해주세요.");
idResult = false;
}
}
})
}
validate();
})
// 비밀번호 유효성검사
$('#signup_form #userPwd').on("keyup", function(){
let regExp = /^[a-z\d!@#$%^&*]{8,15}$/i;
pwdResult = noValueCheck( $(this), $("#pwdCheck_result") ) // true아니면 false가 돌아온다.
&& regExpCheck( $(this), $("#pwdCheck_result") // 자바스크립트의 short-circuit. 위의 조건이 true일 경우 실행할 내용을 뒤에 작성한다.
, regExp, '사용가능한 비밀번호입니다.'
, '영문, 숫자, 특수문자 포함 8~15자리로 작성해주세요.' );
validate();
})
// 비밀번호확인 유효성검사
$('#signup_form #checkPwd').on("keyup", function(){
//일치하는지 비교 할 거임
//비교할 패턴이 위(pwd유효성 검사)와 동일한지 비교
let regExp = new RegExp("^" + $("#signup_form #userPwd").val() + "$");
pwdEqualResult = pwdResult && noValueCheck($(this),$("#pwdEqualCheck_result"))
&& regExpCheck($(this),$("#pwdEqualCheck_result")
, regExp,'비밀번호가 일치합니다.'
,'비밀번호가 일치하지 않습니다.');
validate();
})
// 이름 유효성검사
$('#signup_form #userName').on("keyup", function(){
let regExp = /^[가-힣]{2,5}$/;
nameResult = noValueCheck( $(this), $("#nameCheck_result") )
&& regExpCheck( $(this), $("#nameCheck_result")
, regExp, "사용가능한 이름입니다."
, "한글 2~%글자로 작성해주세요.");
validate();
}) // name check end
})
</script>
</div>
</section>
<!-- Section end -->
<!-- Footer start -->
<jsp:include page="/WEB-INF/views/common/footer.jsp"/>
<!-- Footer end -->
</div>
</body>
</html>
- 아이디 유효성검사는 ajax로 중복체크도 한다.
- form의 action값으로 "${contextPath}/member/insert.do"를 줬다.
- 각 input에 name을 줬다.
ㅁ MemberController
package com.br.spring.controller;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.br.spring.dto.MemberDto;
import com.br.spring.service.MemberService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/member")
@RequiredArgsConstructor
@Controller
public class MemberController {
private final MemberService memberService;
@PostMapping("/signin.do")
public void signin(MemberDto m, HttpServletResponse response, HttpSession session, HttpServletRequest request) throws IOException {
MemberDto loginUser = memberService.selectMember(m);
// 로그인 성공시 => 세션에 회원정보를 담고, alert와 함께 메인 페이지로 이동
// 로그인 실패시 => alert와 함께 기존에 보던 페이지 유지(아이디, 비번 입력하는 로그인 폼 유지. 작성하던 입력데이터도 그대로.)
// script문을 응답데이터로 돌려줘서 흐름제어
response.setContentType("text/html; charset=utf-8");
PrintWriter out = response.getWriter();
out.println("<script>");
if(loginUser != null) { // 로그인 성공
session.setAttribute("loginUser", loginUser);
out.println("alert('" + loginUser.getUserName() + "님 환영합니다~');");
out.println("location.href = '" + request.getHeader("referer") + "';"); // 메인페이지가 아닌 이전에 보던 페이지로 이동
}else { // 로그인 실패
out.println("alert('로그인에 실패하였습니다. 아이디 및 비밀번호를 다시 확인해주세요.');");
out.println("history.back();"); // modal이 띄워진 상태였으면 modal도 유지된다.
}
out.println("</script>");
}
@GetMapping("/singout.do")
public String signout(HttpSession session) {
session.invalidate();
return "redirect:/";
}
@GetMapping("/signup.do")
public void signupPage() { } //WEB-INF/views/member/signup.jsp
@ResponseBody
@GetMapping("/idcheck.do")
public String idCheck(String checkId) {
return memberService.selectUserIdCount(checkId) == 0 ? "YYYY" : "NNNN";
}
@PostMapping("/insert.do")
public void signup(MemberDto m) {
log.debug("암호화 전 member: {}", m);
}
}
- idCheck 메소드를 만든다.
- 반환되는 문자열이 응답 뷰가 아닌 응답 문자열이므로 @ResponseBody를 붙여야 한다.
- 여기서 반환되는 문자열이 화면단으로 응답되어 success의 매개변수(resData)에 담긴다.
- signup 메소드를 만든다.
- 비밀번호 확인은 굳이 넘길 필요는 없다.
- 그런데 비밀번호를 db에 사용자가 있는 그대로(평문값으로) 기록하면 안된다.
암호화처리를 해서 암호문으로 기록해야 한다.
- 로그를 찍어보기 위해 @Slf4j를 쓴다.
debug로 멤버 객체를 찍어본다. 지금은 평문값으로 보여질 것이다.