💬
Member inputMember : 커맨드 객체(제출된 파라미터가 저장된 객체)
String[] memberAddress : input name="memberAddress" 3개가 저장된 배열
RedirectAttributes ra : 리다이렉트 시 데이터를 request scope로 전달하는 객체
🔎 MemberController
// 만약 주소를 입력하지 않은 경우(,,) null로 변경
if(inputMember.getMemberAddress().equals(",,")) {
inputMember.setMemberAddress(null);
}else {
// String.join("구분자", String[])
// 배열의 요소를 하나의 문자열로 변경
// 단, 요소 사이에 "구분자" 추가
String addr = String.join("^^^", memberAddress);
inputMember.setMemberAddress(addr);
}
// 회원 가입 서비스 호출
int result = service.signUp(inputMember);
// 가입 성공 여부에 따라 주소 결정
String path = "redirect:";
String message = null;
if(result > 0) { // 가입 성공
path+="/"; // 메인 페이지
message = inputMember.getMemberNickname() + "님의 가입을 환영합니다.";
} else { // 가입 실패
// path += "/member/signUp"; // 절대 경로 (회원 가입 페이지)
path += "sighUp"; // 상대 경로 (회원 가입 페이지)
message = "회원 가입 실패!";
}
// 리다이렉트 시 session에 잠깐 올라갔다 내려오도록 세팅
ra.addFlashAttribute("message", message );
return path;
}
🔎 MemberService
@Transactional(rollbackFor= {Exception.class} )
@Override
public int signUp(Member inputMember) {
// 비밀번호를 BCrypt를 이용하여 암호화 후 다시 inputMember에 세팅
String encPw = bcrypt.encode(inputMember.getMemberPw());
inputMember.setMemberPw(encPw);
// DAO 호출
int result = dao.signUp(inputMember);
return result;
}
🔎 MemberDAO
/** 회원 가입 DAO
* @param inputMember
* @return result
*/
public int signUp(Member inputMember) {
// 1) mapper의 namespace를 지정 후
// 그 안에 어떤 id를 가지는 sql를 수행할지 작성
// 2) sql에 사용할 데이터를 전달 (자료형 중요! )
// return sqlSession.insert("1) namespace.id", 2) inputMember);
//insert 성공한 행의 개수 반환
return sqlSession.insert("memberMapper.signUp", inputMember);
}
🔎 Member-mapper
<!-- 회원가입 --> <!-- mybatis-config.xml에 지정된 별칭 -->
<insert id="signUp" parameterType="Member">
INSERT INTO "MEMBER"
VALUES(SEQ_MEMBER_NO.NEXTVAL
, #{memberEmail}
, #{memberPw}
, #{memberNickname}
, #{memberTel}
, #{memberAddress}
,NULL, DEFAULT, DEFAULT, DEFAULT)
</insert>
JS 객체 : {"K":V, "K":V} (Map 형식)
**특징**
1) 원하는 value를 얻어오는 방법
- 객체명.key
- 객체명["key"]
2) 객체에 특정 key가 존재하지 않으면 추가할 수 있다.
ex) const obj = {"a":1, "b":2}
obj.c = 3 // -> {"a":1, "b":2, "c":3}
3) 객체에 특정 key를 삭제할 수 있다 (delete 연산자)
ex) const obj = {"a":1, "b":2}
delete obj.b; // {"a" : 1}
/* 유효성 검사 진행 여부 확인용 객체 */
// -> 모든 value가 true인 경우에만 회원 가입 진행
const checkObj = {
"memberEmail" : false,
"memberPw" : false,
"memberPwConfirm" : false,
"memberNickname" : false,
"memberTel" : false
};
// 이메일 유효성 검사
const memberEmail = document.getElementById("memberEmail");
const emailMessage = document.getElementById("emailMessage");
// 이메일이 입력 될 때 마다
memberEmail.addEventListener("input", ()=>{
// 입력된 이메일이 없을 경우
if(memberEmail.value.trim().length==0){
memberEmail.value = "";
emailMessage.innerText = "메일을 받을 수 있는 이메일을 입력해주세요";
// confirm, error 클래스 삭제해서 검정 글씨 만들기
emailMessage.classList.remove("confirm", "error");
checkObj.memberEmail = false; // 빈칸 == 유효 X
return;
}
// 정규 표현식을 이용해서 유효한 형식인지 판별
// 1) 정규 표현식 객체 생성
const regEX = /^[A-Za-z\d\-\_]{4,}@[가-힣\w\-\_]+(\.\w+){1,3}$/
// 2) 입력 받은 이메일과 정규식 일치 여부 판별
if(regEX.test(memberEmail.value)){ // 유효한 경우
emailMessage.innerText = "유효한 형식 입니다.";
emailMessage.classList.add("confirm");
emailMessage.classList.remove("error");
checkObj.memberEmail = true; // 유효 O
}else{ // 유효하지 않은 경우
emailMessage.innerText = "이메일 형식이 유효하지 않습니다.";
emailMessage.classList.add("error");
emailMessage.classList.remove("confirm");
checkObj.memberEmail = false; // 유효 X
}
});
// 비밀번호/비밀번호 확인 유효성 검사
const memberPw = document.getElementById("memberPw");
const memberPwConfirm = document.getElementById("memberPwConfirm");
const pwMessage = document.getElementById("pwMessage");
// 비밀번호 입력 시 유효성 검사
memberPw.addEventListener("input", ()=>{
// 비밀번호가 입력되지 않은 경우
if(memberPw.value.trim().length==0){
memberPw.value = "";
pwMessage.innerText = "영어,숫자,특수문자(!,@,#,-,_) 6~20글자 사이로 입력해주세요.";
pwMessage.classList.remove("confirm","error"); // 검정 글씨
checkObj.memberPw = false; // 빈칸 == 유효 X
return;
}
// 정규 표현식을 이용한 비밀번호 유효성 검사
const regEX = /^[A-Za-z\d\-\_\#\@\!]{6,20}$/;
// 입력한 비밀번호가 유효한 경우
if(regEX.test(memberPw.value)){
checkObj.memberPw = true;
// 비밀번호가 유효하게 작성된 상태에서
// 비밀번호 확인이 입력되지 않았을 때
if(memberPwConfirm.value.trim().length == 0){
pwMessage.innerText = "유효한 비밀번호 형식 입니다.";
pwMessage.classList.add("confirm");
pwMessage.classList.remove("error");
} else{
// 비밀번호가 유효하게 작성된 상태에서
// 비밀번호 확인이 입력되어 있을 때
// 비밀번호 == 비밀번호 확인(같을 경우)
if(memberPw.value == memberPwConfirm.value ){
pwMessage.innerText = "비밀번호가 일치합니다.";
pwMessage.classList.add("confirm");
pwMessage.classList.remove("error");
checkObj.memberPwConfirm = true;
}else{ // 다른 경우
pwMessage.innerText = "비밀번호가 일치하지 않습니다.";
pwMessage.classList.add("error");
pwMessage.classList.remove("confirm");
checkObj.memberPwConfirm = false;
}
}
}else{ // 유효하지 않은 경우
pwMessage.innerText = "유효하지 않은 비밀번호 형식 입니다.";
pwMessage.classList.add("error");
pwMessage.classList.remove("confirm");
checkObj.memberPw = false;
}
})
// 비밀번호 확인 유효성 검사
memberPwConfirm.addEventListener("input", ()=>{
if(checkObj.memberPw){ // 비밀번호가 유효하게 작성된 경우에
// 비밀번호 == 비밀번호 확인(같을 경우)
if(memberPw.value == memberPwConfirm.value ){
pwMessage.innerText = "비밀번호가 일치합니다.";
pwMessage.classList.add("confirm");
pwMessage.classList.remove("error");
checkObj.memberPwConfirm = true;
}else{ // 다른 경우
pwMessage.innerText = "비밀번호가 일치하지 않습니다.";
pwMessage.classList.add("error");
pwMessage.classList.remove("confirm");
checkObj.memberPwConfirm = false;
}
} else{ // 비밀번호가 유효하지 않은 경우
checkObj.memberPwConfirm = false;
}
})
// 닉네임 유효성 검사
const memberNickname = document.getElementById("memberNickname");
const nickMessage = document.getElementById("nickMessage");
// 닉네임이 입력이 되었을 때
memberNickname.addEventListener("input", ()=>{
// 닉네임 입력이 되지 않은 경우
if(memberNickname.value.trim().length==0){
memberNickname.value = "";
nickMessage.innerText = "한글,영어,숫자로만 2~10글자";
nickMessage.classList.remove("confirm","error");
checkObj.memberNickname = false;
memberNickname.value = "";
return;
}
// 정규 표현식으로 유효성 검사
const regEx = /^[가-힣\w\d]{2,10}$/;
if(regEx.test(memberNickname.value)){ // 유효할 때
nickMessage.innerText = "유효한 닉네임 입니다.";
nickMessage.classList.add("confirm");
nickMessage.classList.remove("error");
checkObj.memberNickname = true;
}else{
nickMessage.innerText = "유효하지 않은 닉네임 입니다..";
nickMessage.classList.add("error");
nickMessage.classList.remove("confirm");
checkObj.memberNickname = false;
}
})
// 전화번호 유효성 검사
const memberTel = document.getElementById("memberTel");
const telMessage = document.getElementById("telMessage");
// 전화번호가 입력 되었을 때
memberTel.addEventListener("input", ()=>{
// 전화번호가 입력이 되지 않은 경우
if(memberTel.value.trim().length==0){
telMessage.innerText = "전화번호를 입력해주세요.(- 제외)";
telMessage.classList.remove("confirm","error");
checkObj.memberTel = false;
memberTel.value = "";
return;
}
// 정규표현식으로 유효성 검사
const regExp = /^0(1[01679]|2|[3-6][1-5]|70)\d{3,4}\d{4}$/;
if(regExp.test(memberTel.value)){ // 유효할 때
telMessage.innerText = "유효한 전화번호 형식 입니다.";
telMessage.classList.add("confirm");
telMessage.classList.remove("error");
checkObj.memberTel = true;
}else{
telMessage.innerText = "유효하지 않은 전화번호 형식 입니다..";
telMessage.classList.add("error");
telMessage.classList.remove("confirm");
checkObj.memberTel = false;
}
})
// 회원 가입 form태그가 제출 되었을 때
document.getElementById("signUpFrm").addEventListener("submit",(e)=>{
// checkObj에 모든 value가 true인지 검사
// (배열용 for문)
// for ... of : 향상된 for문
// -> iterator(반복자) 속성을 지닌 배열, 유사 배열 사용 가능
// (객체용 for문)
// *** for ... in ***
// -> JS 객체가 가지고 있는 key를 순서대로 하나씩 꺼내는 반복문
for(let key in checkObj){
if( !checkObj[key]){ // 각 key에 대한 value(true/false)를 얻어와
// false인 경우 == 유효하지 않다!
switch(key){
case "memberEmail" : alert("유효하지 않은 이메일 입니다."); break;
case "memberPw" : alert("유효하지 않은 비밀번호 입니다."); break;
case "memberPwConfirm" : alert("비밀번호가 확인되지 않았습니다."); break;
case "memberNickname" : alert("닉네임이 유효하지 않습니다."); break;
case "memberTel" : alert("전화번호가 유효하지 않습니다."); break;
}
// 유효하지 않은 input 태그로 focus 이동
// - key를 input의 id와 똑같이 설정했음
document.getElementById(key).focus();
// form 태그 기본 이벤트 제거
e.preventDefault();
return; // 함수 종료
}
}
})
💬
@SessionAttributes
1) Model에 세팅된 값의 key와 {}작성된 값이 일치하면 session scope로 이동
2) Session으로 올려둔 값을 해당 클래스에서 얻어와 사용 가능하게 함
-> @SessionAttribute(key)로 사용 가능 (Attribute "s"안붙음!!!)
Member updateMember : 수정할 닉네임, 전화번호 담긴 커맨드 객체
String[] memberAddress : name="memberAddress"인 input 3개의 값(주소)
@SessionAttribute("loginMember") Member loginMember
: Session에서 얻어온 "loginMember"에 해당하는 객체를
: 매개변수 Member loginMember에 저장
🔎 MyPageController
@PostMapping("/info")
public String info(Member updateMember, String[] memberAddress
, @SessionAttribute("loginMember") Member loginMember
, RedirectAttributes ra){
// 주소 하나로 합치기(a^^^b^^^c^^^)
String addr = String.join("^^^", memberAddress);
updateMember.setMemberAddress(addr);
// 로그인한 회원의 번호를 updateMember에 추가
updateMember.setMemberNo( loginMember.getMemberNo() );
// DB에 회원 정보 수정(UPDATE) 서비스 호출
int result = service.updateInfo(updateMember);
String message = null;
if(result > 0) { // 성공
message = "회원 정보가 수정 되었습니다.";
// Session 에 로그인된 회원 정보도 수정(동기화)
loginMember.setMemberNickname( updateMember.getMemberNickname() );
loginMember.setMemberTel( updateMember.getMemberTel() );
loginMember.setMemberAddress( updateMember.getMemberAddress() );
}else { // 실패
message = "회원 정보 수정 실패";
}
ra.addFlashAttribute("message", message);
return "redirect:info"; // 상대 경로 (/myPage/info GET방식)
}
🔎 MyPageServiceImpl
@Autowired // MyPageDAO 의존성 주입(DI)
private MyPageDAO dao;
// 스프링에서는 트랜잭션을 처리할 방법을 지원해줌.(코드기반, 선언적)
// 1) <tx:advice> -> AOP를 이용한 방식(XML에 작성)
// 2) @Transactional 어노테이션을 이용한 방식(클래스 또는 인터페이스에 작성)
// - 인터페이스를 구현한 클래스로 선언된 빈은 인터페이스 메소드에 한해서 트랜잭션이 적용됨
// * 트랜잭션 처리를 위해서는 트랜잭션 매니저가 bean으로 등록되어 있어야 함.
// -> root-context.xml 작성
// 정상 여부는 RuntimeException이 발생했는지 기준으로 결정되며,
// RuntimeException 외 다른 Exception(대표적으로 SQLException 등)에도 트랜잭션 롤백처리를 적용하고 싶으면
// @Transactional의 rollbackFor 속성을 활용하면 된다
// 회원 정보 수정 서비스
@Transactional(rollbackFor = {Exception.class} )
@Override
public int updateInfo(Member updateMember) {
return dao.updateInfo(updateMember);
}
}
🔎 MyPageDAO
/** 회원 정보 수정 DAO
* @param updateMember
* @return
*/
public int updateInfo(Member updateMember) {
return sqlSession.update("myPageMapper.updateInfo", updateMember);
}
}
🔎 MyPage-mapper.xml
<mapper namespace="myPageMapper">
<!-- 회원 정보 수정 -->
<update id="updateInfo" parameterType="Member">
UPDATE MEMBER SET
MEMBER_NICKNAME = #{memberNickname},
MEMBER_TEL = #{memberTel},
MEMBER_ADDR = #{memberAddress}
WHERE MEMBER_NO = #{memberNo}
</update>