Sign Up
<div>
<form action="insert" method="post" enctype="multipart/form-data" onsubmit="return check()">
<table>
<caption align="top"><b>회원가입</b></caption>
<tr>
<td rowspan="5" align="center">
<input type="file" name="multi" id="myphoto" style="display: none">
<button type="button" id="btnphoto">사진선택</button><br>
<img id="showimg">
</td>
<td>
<div>
<input type="text" name="id" id="id" class="form-control">
<button type="button" id="btnidcheck">중복체크</button>
<span class="idsuccess"></span>
</div>
</td>
</tr>
<tr>
<td>
<div>
<input type="password" class="form-control" name="pass" id="pass" maxlength="4" required="required">
<input type="password" class="form-control" name="pass2" id="pass2" maxlength="4" required="required">
<span class="passsuccess"></span>
</div>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<button type="submit">회원가입</button>
</td>
</tr>
</table>
</form>
</div>
- 회원 가입
<form>
의 기능 요구사항
- 파일(이미지) 선택 시 <img>
태그로 선택한 이미지 확인 (#showimg
)
- 중복 아이디 검증하여 검증 결과 출력 (.idsuccess
)
- 비밀번호 교차 검증하여 검증 결과 출력 (.passsuccess
)
- 결과 제출(action) 시 위의 3가지 검증 실행 및 통과하지 못하면 제출 거부 (onsubmit=”return check()”
)
Validation_Image Preview
$("#btnphoto").click(function(){
$("#myphoto").trigger("click");
});
$("#myphoto").on("change", function(event) {
var file = event.target.files[0];
var reader = new FileReader();
reader.onload = function(e) {
$("#showimg").attr("src", e.target.result);
}
reader.readAsDataURL(file);
});
on
메서드의 (event).target
함수의 files[i]
객체로 변경(선택)한 파일 호출
FileReader()
객체의 onload
함수는 파일을 성공적으로 읽을 시 작동
Validation_ID Validation
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="boot.data.mapper.MemberMapperInter">
<select id="validId" parameterType="String" resultType="int">
select count(*) from bootmember where id=#{id}
</select>
</mapper>
- Sql문을 통해 해당 아이디가 이미 존재하는지 확인
@Service
public class MemberServie implements MemberServiceInter {
@Autowired
MemberMapperInter mapperInter;
@Override
public int validId(String id) {
return mapperInter.validId(id);
}
}
@Service
에서 로직 처리 (처리 로직이 간단하거나 없으면 @Mapper
로 직행 가능)
@Controller
public class MemberController {
@Autowired
MemberServie service;
@GetMapping("/member/idcheck")
public @ResponseBody Map<String, String> idcheck(@RequestParam String id) {
Map<String, String> map=new HashMap<>();
int check=service.validId(id);
if(check==0)
map.put("check", "사용가능한 아이디입니다");
else
map.put("check", "사용중인 아이디입니다");
return map;
}
}
- Ajax 비동기 처리 방식을 사용하기 위해 반환 데이터를 Json 형식으로 전달하기 위해
@ResponseBody
선언
- 입력 값에 따른 반환 데이터 구분
$("#btnidcheck").click(function(){
var id=$("#id").val();
var s="";
$.ajax({
data:{"id":id},
dataType:"json",
type:"get",
url:"idcheck",
success:function(res){
s+="<b style='font-size:1em'>"+res.check+"</b>";
$("span.idsuccess").html(s);
if(res.check=="사용중인 아이디입니다"){
$("#id").val("");
$("span.idsuccess").attr("style","width: 200px;color: red");
}
else{
$("span.idsuccess").attr("style","width: 200px;color: blue");
}
}
});
});
- Ajax의 반환 데이터를 이용해 View 출력
Validation_PW Cross Validation
$("#pass2").keyup(function(){
var p1=$(this).val();
var p2=$("#pass").val();
if(p1==p2){
$("span.passsuccess").text("비밀번호 확인");
}
else{
$("span.passsuccess").text("비밀번호 일치시켜주세요");
}
});
keyup
이벤트는 key가 입력될 때 작동하는 이벤트 정의
Confirm Validation
function check(){
if($("#myphoto").val()==""){
alert("사진을 선택해주세요");
return false;
}
if($("span.idsuccess").text()!="사용가능한 아이디입니다"){
alert("아이디 중복체크를 해주세요");
return false;
}
if($("span.passsuccess").text()!="비밀번호 확인"){
alert("비밀번호가 서로 다릅니다");
return false;
}
}
- View의
<form>
태그의 onsubmit
조건에 해당 함수 return
Sign In (Log In)
Controller_Main
@Controller
public class LoginController {
@Autowired
MemberServie service;
@GetMapping("/login/main")
public String loginform(HttpSession session,Model model) {
String myid=(String)session.getAttribute("myid");
String loginok=(String)session.getAttribute("loginok");
if(loginok==null)
return "/login/loginForm";
else {
String name=service.getName(myid);
model.addAttribute("name", name);
return "/sub/login/logoutForm";
}
}
}
- 로그인 기능의 Main 매핑은 로그인 시 로그아웃 폼으로, 로그아웃 시 로그인 폼으로 이동해야 함
- 로그인, 아웃 후 Main으로 매핑 시 자동으로 이동 페이지 지정
<form action="loginprocess" method="post">
<div>
<input type="text" name="id" value="${sessionScope.saveok==null?'':sessionScope.myid}">
<input type="password" name="pass">
</div>
<button type="submit">LogIn</button>
<input type="checkbox" name="save" value="yes" ${sessionScope.saveok==null?'':'checked'}>아이디저장
</form>
- 로그아웃 시 전달 받을 데이터(
sessionScope
)에 의한 태그 조작을 위한 삼항연산자
- 초기에는
null
값이므로 이를 처리할 필요 있음
Controller_In Process
@Controller
public class LoginController {
@PostMapping("/login/loginprocess")
public String loginproc(@RequestParam String id,String pass,
@RequestParam(required = false) String save,
HttpSession session) {
HashMap<String, String> map=new HashMap<>();
int check=service.loginPassCheck(id, pass);
if(check==1) {
session.setMaxInactiveInterval(60*60*8);
session.setAttribute("myid", id);
session.setAttribute("loginok", "yes");
session.setAttribute("saveok", save);
MemberDto mdto=service.getDataById(id);
session.setAttribute("loginphoto", mdto.getPhoto());
return "redirect:main";
}
else {
return "/sub/member/passFail";
}
}
}
- ‘아이디저장’ 버튼(save)은 초기
null
이므로 반드시 required
조건을 false
로 고정
- Id와 Password의 유효성 검사 메서드 호출
- `session` 설정
setMaxInactiveInterval()
객체로 비동작 session 유지 시간 설정
- 아이디(myid), 로그인 여부 식별자(loginok), 아이디 저장 여부 식별자(saveok) session 지정
- 이미지 사용 편의성을 위해 이미지 데이터(loginphoto) session에 등록
Sign Out
Controller_Out Process
@GetMapping("/login/logoutprocess")
public String logout(HttpSession session) {
session.removeAttribute("loginok");
return "redirect:main";
}
- 로그아웃 폼은 간단하므로 생략
- 로그아웃 시 로그인 여부 식별자는 제거
View_My Page
<c:forEach var="dto" items="${list }">
<c:if test="${sessionScope.loginok!=null and sessionScope.myid==dto.id }">
<table>
<tr>
<td rowspan="5" align="center">
<img src="../membersave/${dto.photo }"><br><br>
<button type="button" class="mod" num="${dto.num }">사진수정</button>
</td>
<th><b>이름</b></th>
<td><b>${dto.name }</b></td>
<td rowspan="5" align="center" valign="middle">
<button type="button" class="mod" onclick="location.href='updatemember?num=${dto.num}'">수정</button>
<button type="button" class="del" num="${dto.num}">삭제</button>
</td>
</tr>
</table>
</c:if>
</c:forEach>
<input type="file" style="display: none" id="newphoto" num="">
<c:if>
조건문의 교집합 연산자는 &&
, and
모두 유효
- 아래에서 Ajax를 이용해 파일 처리 및 사진 수정할 것 : 아래의 JavaScript 코드는 이 HTML 코드와 연결
Ajax File Processing
- Ajax 사용 시 파일 형식의 데이터를 처리하려면 단순 Object 데이터와 다른 방법 필요
- 일반적으로 폼에
enctype
을 별도로 지정하지 않을 경우 application/x-www-form-urlencoded
타입으로 전송
application/x-www-form-urlencoded
란 데이터 형식이 key=value&key=value
의 형태로 전달되는 HTML form의 기본 Content-Type
이다.
- 참고 : 자주 사용되는
application/json
은 데이터 형식이 {key: value}
형태
Image Modify
$(".mod").click(function(){
var num=$(this).attr("num");
$("#newphoto").attr("num",num);
$("#newphoto").trigger("click");
});
$("#newphoto").change(function(){
var num=$(this).attr("num");
const form=new FormData();
form.append("photo",$(this)[0].files[0]);
form.append("num",num);
$.ajax({
data:form,
dataType:"html",
type:"post",
url:"updatephoto",
processData: false,
contentType: false,
success:function(){
location.reload();
}
});
});
- Ajax url로 전달할 데이터는
***FormData()***
객체에 담아 보냄
append
함수 이용하여 데이터 추가
- Ajax 사용 시
MultipartFile
객체의 참조변수명은 반드시 DB의 해당 컬럼명과 동일해야 함
processData: false
, contentType: false
설정
SQL_Mapper
<mapper namespace="boot.data.mapper.MemberMapperInter">
<update id="updatePhoto" parameterType="Map">
update bootmember set photo=#{photo} where num=#{num}
</update>
</mapper>
Service
@Service
public class MemberServie implements MemberServiceInter {
@Autowired
MemberMapperInter mapperInter;
@Override
public int loginPassCheck(String id, String pass) {
Map<String, String> map=new HashMap<>();
map.put("id", id);
map.put("pass", pass);
return mapperInter.loginPassCheck(map);
}
}
Controller
@Controller
public class MemberController {
@Autowired
MemberServie service;
@PostMapping("/member/updatephoto")
@ResponseBody
public void modify(@RequestParam String num,
MultipartFile photo,
HttpSession session) {
String path=session.getServletContext().getRealPath("/membersave");
String oldphoto=service.getPhotoByNum(num);
File file=new File(path+"\\"+oldphoto);
file.delete();
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
String newphoto=sdf.format(new Date())+"_"+photo.getOriginalFilename();
try {
photo.transferTo(new File(path+"\\"+newphoto));
service.updatePhoto(num, newphoto);
session.setAttribute("loginphoto", newphoto);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 반환 데이터가 없으므로 void 타입
- 기타 update 방식은 기존과 동일
Multiple Optional Event
View_List
<input type="checkbox" id="allcheck">
<c:forEach var="dto" items="${list }">
<table>
<input type="checkbox" num="${dto.num }" class="del">
</table>
</c:forEach>
<button type="button" id="btnmemberdel" num="">선택강퇴</button>
<c:forEach>
에 따라 ${list}
의 데이터가 반복되며 테이블 생성 (#del
포함)
- 유일한 버튼
- 전체 선택 버튼 (
#allcheck
)
- 선택한 요소들에게 이벤트 부여하는 버튼 (
#btnmemberdel
)
Event
$("#allcheck").click(function(){
var check=$(this).is(":checked");
alert(check);
$(".del").prop("checked",check);
});
.is(”:checked”)
메서드 중 (”:check”)
는 선택 이벤트를 의미하며, is()
는 해당 선택자가 해당 이벤트에 당했는지 여부에 따라 true
, false
반환
.prop(”checked”,check)
는 선택자에게 이벤트를 true
, false
값에 따라 부여 혹은 제거할지 결정하는 메서드
.del
에게 “checked”
이벤트를 부여(true), 제거(false)
$("#btnmemberdel").click(function(){
var cnt=$(".del:checked").length;
if(cnt==0){
alert("먼저 선택해주세요");
return false;
}
$(".del:checked").each(function(i,ele){
var num=$(this).attr("num");
$.ajax({
data:{"num":num},
dataType:"html",
type:"get",
url:"delete",
success:function(){
alert("삭제 완료");
location.reload();
}
});
});
});
.each()
반복문을 사용하여 :checked
이벤트를 부여한 선택자(.del
)에게 모두 결과 함수 적용
- 결과적으로 하나의 이벤트로 해당 선택자 전체 태그에 이벤트 부여