게시판 하나를 만들 때, 그에 따른 순서와 필요한 데이터들만 잘 숙지한다면 어렵지 않게 해낼수가 있다.
그 뒤에 따라오는 부가적인 것들은 따로 공부한다면 더 좋은 퀄리티를 낼 수 있다.
오늘은 회원가입 폼을 만드는 작업을 했다.
이 역시, 이클립스에서 했던것과 거의 유사했기 때문에 어렵지는 않았다.
하지만 write.jsp에서 JS를 이용한 유효성 검사라든지 Alert 창을 띄우는 작업은 조금 어려웠다.
솔직히 말해, 잘 이해하지 못하고 그냥 따라치기 바빴던 거 같다.
자바스크립트는 조금 더 욕심내서 공부하면 좋을듯하다.
최종적으로 만들어낸 회원가입 폼은 이렇게 생겼다.
순서는 VO 객체 생성 - Mapper.java(추상메서드) - Mapper.xml(쿼리문) - Service - Controller - JSP
<MemberVO>
package com.webjjang.member.vo;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;
import lombok.Data;
@Data
public class MemberVO {
private String id, pw, name, gender, tel, email, status, photo;
// 날짜형 입력을 받을 때, 문자열로 들어오므로 패턴을 지정해서 정의해 놓으면 Date 객체로 만들때 사용한다.
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birth;
private Date regDate;
private Date conDate;
private int gradeNo;
private String gradeName;
// 사용자가 업로드한 프로필 사진을 저장하는 변수
// write.jsp에서 name="photoFile"로 지정할것★
// post이고 enctype="multipart/form-data" 지정해야만 한다.
private MultipartFile photoFile;
}
MemberVO 에서는 @Data를 이용해 getter(),setter(), toString을 생성했다.
여기서 중요하게 봐야할 부분은 @DateTimeFormat 부분이다.
생년월일(bitrh)은 날짜형을 입력받기 때문에 문자열로 들어오므로 패턴을 정의해놓은 Annotation을 사용했다.
그리고, 프로필사진(photoFile)에 해당하는 부분은 리턴타입을 MultipartFile로 지정했다.
추상 메서드, 쿼리문 작성, 서비스 부분은 기본적이니까 넘어가도록 하고
MemberController 로 넘어간다.
<MemberController>
package com.webjjang.member.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.ui.Model;
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.servlet.mvc.support.RedirectAttributes;
import com.webjjang.member.service.MemberService;
import com.webjjang.member.vo.LoginVO;
import com.webjjang.member.vo.MemberVO;
import com.webjjang.util.file.FileUtil;
import lombok.extern.log4j.Log4j;
@Controller
@RequestMapping("/member")
@Log4j
public class MemberController {
@Autowired
private MemberService service;
// 1. loginForm
@GetMapping("/login.do")
public String loginForm() throws Exception {
log.info("login 폼으로 이동");
return "member/login";
}
// 2. login
@PostMapping("/login.do")
public String login(LoginVO invo, HttpSession session, RedirectAttributes rttr) throws Exception {
log.info("login 처리 - invo : " + invo);
session.setAttribute("login", service.login(invo));
rttr.addFlashAttribute("msg", "성공적으로 로그인이 되었습니다.");
// 원래 main으로 redirect
return "redirect:/board/list.do";
}
// 3. logout
@GetMapping("/logout.do")
public String logout(HttpSession session) throws Exception {
log.info("logout 처리");
// 로그아웃 처리 - session의 정보를 지운다.
session.removeAttribute("login");
// 원래 main으로 redirect
return "redirect:/board/list.do";
}
// 4. 회원가입 폼
@GetMapping("/write.do")
public String writeForm() throws Exception {
return "member/write";
}
// 5. 회원가입 처리
@PostMapping("/write.do")
public String write(MemberVO vo, HttpServletRequest request, RedirectAttributes rttr) throws Exception {
// 회원 사진을 저장할 위치
String path = "/upload/member";
// 서버에 파일 저장히기 → 서버에 저장된 파일명을 받아서 photo에 넣기
vo.setPhoto(FileUtil.upload(path, vo.getPhotoFile(), request));
// 회원 가입 처리
service.write(vo);
// redirect 하는 페이지에서 한 번만 사용되는 속성값을 전달할 수 있다. → session
rttr.addFlashAttribute("msg", "성공적으로 회원가입이 되었습니다. \\n로그인 후 이용하세요.");
return "redirect:/member/login.do";
}
}
회원가입 시, 프로필 사진을 따로 첨부하지 않은 회원은 noImage.jpg 라는 파일을
default로 지정하기 위해 Controller 부분에서 저렇게 설정하였다.
<Write.jsp>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>회원가입</title>
<script type="text/javascript">
$(function() {
// id중복체크 변수, 비밀번호와 비밀범호 확인이 같은지 체크 변수 -> 전역 변수 선언
var idCheck = false;
var pwCheck = false;
// datepicker 클래스 이벤트 - 적정한 옵션을 넣어서 초기화 시켜 준다. 기본 datepicker()로 사용 가능
$(".datepicker").datepicker(
{
changeMonth : true,
changeYear : true,
dateFormat : "yy-mm-dd",
dayNamesMin : [ "일", "월", "화", "수", "목", "금", "토" ],
monthNamesShort : [ "1월", "2월", "3월", "4월", "5월", "6월",
"7월", "8월", "9월", "10월", "11월", "12월" ]
});
var now = new Date();
var startYear = now.getFullYear();
var yearRange = (startYear - 100) + ":" + startYear;
// datepicker - 초기값으로 셋팅하는 방법을 사용하면 2번째는 무시 당한다.
//원래 있던 datepicker에 option을 추가하는 방법이다.
$(".datepicker").datepicker("option", {
"maxDate" : new Date(),
"yearRange" : yearRange
});
// $( ".datepicker" ).datepicker();
// 아이디 체크 이벤트
$("#id").keyup(function() {
idCheck = false;
var id = $("#id").val();
// 공백문자 처리
id = $.trim(id);
$("#id").val(id);
// alert("입력한 아이디 : " + id);
// 4자 미만 처리
if (id.length < 4) {
$("#idCheckDiv").removeClass("alert-success");
$("#idCheckDiv").addClass("alert-danger");
$("#idCheckDiv").text("아이디는 4자 이상 영숫자이여야 합니다.");
return;
}
// 20자 초과
if (id.length > 20) {
$("#idCheckDiv").removeClass("alert-success");
$("#idCheckDiv").addClass("alert-danger");
$("#idCheckDiv").text("아이디는 20자 이내 영숫자이여야 합니다.");
return;
}
// 4~20 사이 아이디인 경우 - 중복 체크하러 간다. -> 서버로 간다. URL필요 -> 화면에 다른 데이터는 변하지 않으면서 일부 처리에 필요한 데이터만 변경 URL은 변경이 없다. ->Ajax
// 중복안된 경우(아이디가 null인 경우) - 사용가능한 아이디 입니다., 중복이된 경우(아이디가 null이 아닌 경우) - 중복된 아이디입니다. -> 서버에서 처리
// /member/idCheck -> *.do:sitemesh 위 아래 메뉴와 카피라이트가 붙는다.
// result -> 서버에서 전달해 주는 데이터
$("#idCheckDiv").load("/member/idCheck?id=" + id, function(result) {
// 결과에 따른 배경색 처리
// alert(result);
// 클래스 다 지우기
$("#idCheckDiv").removeClass("alert-success alert-danger");
if (result.indexOf("가능한") == -1) {
// 중복된 아이디인 경우 배경은 빨간색
$("#idCheckDiv").addClass("alert-danger");
idCheck = false;
} else {
// 사용가능한 아이디인 경우 배경은 파란색
$("#idCheckDiv").addClass("alert-success");
idCheck = true;
}
});
}); //$("#id").keyup() 이벤트의 끝
// 비밀번호 처리 이벤트
$("#pw").keyup(function() {
pwCheck = false;
// $(this) == $("#pw")
var pw = $(this).val();
//alert(pw.length);
// 4자 미만 처리
if (pw.length < 4) {
$("#pwCheckDiv").removeClass("alert-success");
$("#pwCheckDiv").addClass("alert-danger");
$("#pwCheckDiv").text("비밀번호는 4자 이상이여야 합니다.");
return;
}
// 20자 초과 처리
if (pw.length > 20) {
$("#pwCheckDiv").removeClass("alert-success");
$("#pwCheckDiv").addClass("alert-danger");
$("#pwCheckDiv").text("비밀번호는 20자 이내이여야 합니다.");
return;
}
// 4~20 사이 pw2와 같은지 체크
var pw2 = $("#pw2").val();
if (pw == pw2) {
// 비밀번호와 비밀번호 확인이 같은 경우
$("#pwCheckDiv, #pw2CheckDiv").removeClass("alert-danger");
$("#pwCheckDiv, #pw2CheckDiv").addClass("alert-success");
$("#pwCheckDiv, #pw2CheckDiv").text("적당한 비밀번호입니다.");
pwCheck = true;
} else {
// 비밀번호와 비밀번호 확인이 같지 않은 경우
$("#pwCheckDiv, #pw2CheckDiv").removeClass("alert-success");
$("#pwCheckDiv, #pw2CheckDiv").addClass("alert-danger");
$("#pwCheckDiv").text("비밀번호와 비밀번호 확인은 같아야 합니다.");
if (pw2.length < 4)
$("#pw2CheckDiv").text("비밀번호확인은 4자 이상이여야 합니다.");
else if (pw2.length > 20)
$("#pw2CheckDiv").text("비밀번호 확인은 20자 이내이여야 합니다.");
else
$("#pw2CheckDiv").text("비밀번호와 비밀번호 확인은 같아야 합니다.");
}
});
// 비밀번호 확인 처리 이벤트
$("#pw2").keyup(function() {
pwCheck = false;
// $(this) == $("#pw2")
var pw2 = $(this).val();
//alert(pw2.length);
// 4자 미만 처리
if (pw2.length < 4) {
$("#pw2CheckDiv").removeClass("alert-success");
$("#pw2CheckDiv").addClass("alert-danger");
$("#pw2CheckDiv").text("비밀번호확인은 4자 이상이여야 합니다.");
return;
}
// 20자 초과 처리
if (pw2.length > 20) {
$("#pw2CheckDiv").removeClass("alert-success");
$("#pw2CheckDiv").addClass("alert-danger");
$("#pw2CheckDiv").text("비밀번호 확인은 20자 이내이여야 합니다.");
return;
}
// 4~20 사이 pw와 같은지 체크
var pw = $("#pw").val();
if (pw == pw2) {
// 비밀번호와 비밀번호 확인이 같은 경우
$("#pw2CheckDiv, #pwCheckDiv").removeClass("alert-danger");
$("#pw2CheckDiv, #pwCheckDiv").addClass("alert-success");
$("#pw2CheckDiv, #pwCheckDiv").text("적당한 비밀번호입니다.");
pwCheck = true;
} else {
// 비밀번호와 비밀번호 확인이 같지 않은 경우
$("#pwCheckDiv, #pw2CheckDiv").removeClass("alert-success");
$("#pwCheckDiv, #pw2CheckDiv").addClass("alert-danger");
$("#pw2CheckDiv").text("비밀번호와 비밀번호 확인은 같아야 합니다.");
if (pw.length < 4)
$("#pwCheckDiv").text("비밀번호확인은 4자 이상이여야 합니다.");
else if (pw.length > 20)
$("#pwCheckDiv").text("비밀번호 확인은 20자 이내이여야 합니다.");
else
$("#pwCheckDiv").text("비밀번호와 비밀번호 확인은 같아야 합니다.");
}
});
// 비밀번호 처리 이벤트의 끝
// 회원 가입 이벤트
$("#writeForm").submit(function(){
// alert("아이디 체크 : " + idCheck + "\n비밀번호 체크 : " + pwCheck);
// 아이디 중복체크 - 사용 가능한 아이디 인지 확인
if(!idCheck){
alert("중복이 되지 않는 적당한 형식의 아이디를 사용하셔야만 합니다.");
$("#id").focus();
// form 전송을 무시시킨다.
return false;
}
// 비밀번호와 비밀번호 확인
if(!pwCheck){
alert("비밀번호와 비밀번호 확인의 길이가 4~20이여야 하고 같아야 합니다.");
$("#pw").focus();
// form 전송을 무시시킨다.
return false;
}
// form 전송을 무시시킨다. -> 나중에 꼭 주석처리해야만 한다.
// return false;
});
});// $(function(){}) 의 끝
</script>
</head>
<body>
<div class="container">
<h1 style="text-align: center;">
<strong>회원가입 폼</strong>
</h1>
<hr />
<br>
<form action="write.do" method="post" enctype="multipart/form-data"
id="writeForm">
<div class="form-group">
<label for="id">아이디</label> <input name="id" id="id"
class="form-control" required="required"
pattern="[A-Za-z0-9]{4,20}" autocomplete="off">
<div class="alert alert-danger" id="idCheckDiv">ID는 4자 이상의
영ㆍ숫자를 입력하셔야 합니다.</div>
</div>
<div class="form-group">
<label for="pw">비밀번호</label> <input type="password" name="pw"
id="pw" class="form-control" required="required" pattern=".{4,20}"
placeholder="비밀번호 입력">
<div id="pwCheckDiv" class="alert alert-danger">PW는 4자 이상이어야
합니다.</div>
</div>
<div class="form-group">
<label for="pw2">비밀번호 확인</label> <input type="password" id="pw2"
class="form-control" required="required" pattern=".{4,20}"
placeholder="비밀번호 입력 확인">
<div id="pw2CheckDiv" class="alert alert-danger">PW 확인은 4자
이상이어야 합니다.</div>
</div>
<div class="form-group">
<label for="name">이름</label> <input name="name" id="name"
class="form-control" required="required" pattern="[가-힣]{2,10}">
</div>
<div class="form-group">
<label>성별</label>
<div>
<label class="radio-inline"><input type="radio"
name="gender" value="남자" checked="checked" /> 남자</label><label
class="radio-inline"> <input type="radio" name="gender"
value="여자" /> 여자
</label>
</div>
</div>
<div class="form-group">
<label>생년월일</label> <input name="birth" id="birth"
class="form-control datepicker" autocomplete="off">
</div>
<div class="form-group">
<label>연락처</label> <input name="tel" id="tel" class="form-control"
placeholder="예)010-1111-2222">
</div>
<div class="form-group">
<label>이메일</label> <input name="email" id="email"
class="form-control" placeholder="예)test@naver.com">
</div>
<div class="form-group">
<label>사진</label> <input type="file" name="photoFile" id="photoFile"
class="form-control">
</div>
<div class="text-center">
<button class="btn btn-primary">가입</button>
<button class="btn btn-default" type="button"
onclick="history.back()">취소</button>
</div>
</form>
</div>
</body>
</html>
함수를 이용했으면 코드가 더 간결해졌을텐데, 그러지 못해서 JS 부분의 코드가 좀 길어졌다.
JS 부분은 나도 완벽히 이해하지 못했지만 저 코드로 유효성 검사를 실시할 수 있고
비밀번호와 비밀번호 확인칸에 입력한 데이터 값이 같은지를 검사할 수 있다.
유효성에 적합하지 않는다면 false 값이 리턴돼 페이지 이동을 하지 않는다.
그리고 Ajax를 이용해 ID 중복 검사도 실시할 수 있다.
여기서 주의깊게 볼 것은 생년월일을 jQuery의 datepicker를 이용해서 데이터를 받고 있다.
음 솔직히 input type을 date로 작성하는 것과 datepicker를 이용하는 것의 큰 차이점을 모르겠다.
datepicker도 좋지만 나라면 그냥 input type을 date 타입으로 지정해서 데이터 입력하는 걸로 할 듯...
input 태그 속성 중에 하나인 autocomplete="off" 기능은 굉장히 좋은 거 같다.데이터를 입력할 때, 밑에 자동 완성 되는 목록들이 뜨지 않게 하는 속성.
그리고 Ajax를 이용한 ID 중복 체크를 하는 기능도 배웠는데Ajax를 알기전까지는 굉장히 어렵게 생각했었는데 막상 듣고보니 그렇게 별 거 아니었던...나는 Ajax가 무슨 프로그램이나 라이브러리 쯤일줄 알았는데 그냥 jQuery의 일부분이었다.
Ajax는 URL에는 변경이 없고 화면에 다른 데이터는 변하지 않으면서일부 처리에 필요한 데이터만 변경하는 것이라고 했다.
https://www.w3schools.com/jquery/jquery_ajax_load.asp
더 자세한건 위의 링크에서 확인할 수 있다.
jQuery AJAX load 메서드를 이용해 ID 중복 체크를 실시했다.
$("#idCheckDiv").load("/member/idCheck?id=" + id, function(result) {
// 결과에 따른 배경색 처리
// alert(result);
// 클래스 다 지우기
$("#idCheckDiv").removeClass("alert-success alert-danger");
if (result.indexOf("가능한") == -1) {
// 중복된 아이디인 경우 배경은 빨간색
$("#idCheckDiv").addClass("alert-danger");
idCheck = false;
} else {
// 사용가능한 아이디인 경우 배경은 파란색
$("#idCheckDiv").addClass("alert-success");
idCheck = true;
}
});
JS 부분에서 이렇게 작성하면 URL은 변경되지 않으면서 ID 중복 체크를 할 수 있다.
사실 코드 전체에 대한 이해도는 거의 1-2 에 가깝다.
Ajax 사용법에 대해서 30분만에 짧게 배웠고 다 이해하기에는 확실히 무리가 있다.
아무튼 배우긴 했으니, 개인 프로젝트 준비할 때 게시판에 댓글 기능을 꼭 구현해보려고 한다.
'BACK-END > SPRING' 카테고리의 다른 글
[Spring-JS]데이터 변경 시 Confirm이벤트 처리 (0) | 2022.03.15 |
---|---|
[Spring]예외처리(404에러, 500에러) (0) | 2022.03.15 |
[Spring]AOP (0) | 2022.03.12 |
[Spring]JUnit을 이용한 단위 테스트 (0) | 2022.03.11 |
[Spring]Spring-MyBatis를 이용해 공지사항 게시판 만들기 (0) | 2022.03.02 |
댓글