KSI일기장
0428spring마이페이지 본문
MyPageController
package edu.kh.comm.member.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import edu.kh.comm.member.model.service.MyPageService;
import edu.kh.comm.member.model.vo.Member;
// /member/myPage/profile
// /member/myPage/info
// /member/myPage/changePw
// /member/myPage/secession
@Controller //bean으로 등록
@RequestMapping("/member/myPage")
@SessionAttributes({"loginMember"}) //session scope에서 loginMember를 얻어옴
public class MyPageController {
@Autowired
private MyPageService service; //Service 연결
//회원 정보 조회 이동
@GetMapping("/info")
public String info() {
return "member/myPage-info";
// WEB-INF/views/member/myPage-info
}
//비밀번호 변경 이동
@GetMapping("/changePw")
public String changePw() {
return "member/myPage-changePw";
// WEB-INF/views/member/myPage-info
}
//회원탈퇴 이동
@GetMapping("/secession")
public String secession() {
return "member/myPage-secession";
// WEB-INF/views/member/myPage-info
}
//프로필 변경 이동
@GetMapping("/profile")
public String profile() {
return "member/myPage-profile";
// WEB-INF/views/member/myPage-info
}
//회원정보수정
@PostMapping("/info")
public String updateInfo(@ModelAttribute("loginMember") Member loginMember,
@RequestParam Map<String, Object> paramMap,
//->요청시 전달된 파라미터를 구분하지 않고 모두 Map에 담아서 얻어옴)
String[] updateAddress, RedirectAttributes ra){
//필요한 값
//-닉네임
//-전화번호
//-주소
//-회원번호(Session -> 로그인한 회원정보를 통해서 얻어옴)
// ->@SessionAttribute, @ModelAttribute 필요
//@SessionAttribute 의 역할
// 1.Model에 세팅된 데이터의 key와
// @SessionAttribute에 작성된 key 같으면
// Model에 세팅된 데이터를 request -> session scope로 이동
// 2.기존에 session scope 에 이동시킨 값을 얻어오는 역할
/*@SessionAttribute 의 역할
1.Model에 세팅된 데이터의 key와
@SessionAttribute에 작성된 key 같으면
Model에 세팅된 데이터를 request -> session scope로 이동
2.기존에 session scope 에 이동시킨 값을 얻어오는 역할
@ModelAttrbute("loginMember") Member loginMember
session key값
->@SessionAttribute를 통해 session scope에 등록된 값을 얻어와
오른쪽 작성된 Member loginMember 변수에 대입
단, 클래스위에 @SessionAttribute({"loginMember"})가 작성되어 있어야 가능
*/
//***매개변수를 이용해서 session, 파라미터 데이터를 동시에 얻어올 때 문제점*****
//session에서 객체를 얻어오기 위해 매개변수에 작성한 상태에서
//->@ModelAttribute("loginMember") Member loginMember
//파라미터의 name속성값이 매개변수에 작성된 객체(loginMember)의 필드와 일치하면
//-> ex) name="memberNickname"
//session에서 얻어온 객체의 필드에 덮어쓰기가 된다
//해결방법 파라미터의 namet속성을 변경해서 얻어오면 문제 해결할 수 있다
//->필드명이 겹쳐서 문제니깐 겹치지 않게 해준다는 의미
//파라미터를 저장한 paramMap에 회원번호, 주소를 추가
String memberAddress = String.join(",," , updateAddress);
//주소가 미입력 일때
if(memberAddress.equals(",,,,")) memberAddress = null;
paramMap.put("memberNo", loginMember.getMemberNo());
paramMap.put("memberNo", memberAddress);
//회원정보 수정 서비스 호출
int result = service.updateInfo(paramMap);
String message = null;
if(result > 0) {
message = "회원정보가 수정되었습니다";
//DB-Session의 회원정보 동기화
loginMember.setMemberNickname((String)paramMap.get("updateNickname"));
loginMember.setMemberTel((String)paramMap.get("updateTel"));
loginMember.setMemberAddress((String)paramMap.get("updateAddress"));
}else {
message = "회원정보 수정 실패";
}
ra.addFlashAttribute("message", message);
return "redirect:info";
//비밀번호 변경(암호화)
//회원탈퇴(세션, 쿠키 무효화)
}
}
MyPageService
package edu.kh.comm.member.model.service;
import java.util.Map;
public interface MyPageService {
/**회원정보 수정 서비스
* @param paramMap
* @return result
*/
int updateInfo(Map<String, Object> paramMap);
}
MyPageServiceImpl
package edu.kh.comm.member.model.service;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import edu.kh.comm.member.model.dao.MyPageDAO;
@Service
public class MyPageServiceImpl implements MyPageService {
@Autowired
private MyPageDAO dao;
@Override
public int updateInfo(Map<String, Object> paramMap) {
return dao.updateInfo(paramMap);
}
}
MyPageDAO
package edu.kh.comm.member.model.dao;
import java.util.Map;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class MyPageDAO {
@Autowired
private SqlSessionTemplate sqlSession;
/**회원정보 수정 DAO
* @param paramMap
* @return
*/
public int updateInfo(Map<String, Object> paramMap) {
return sqlSession.update("myPageMapper.updateInfo", paramMap);
}
}
myPage.js
// 내 정보 수정 유효성 검사
function infoValidate(){
const memberNickname = document.getElementById("memberNickname");
const memberTel = document.getElementById("memberTel");
const regExp1 = /^[a-zA-Z0-9가-힣]{2,10}$/; // 닉네임 정규식
const regExp2 = /^0(1[01679]|2|[3-6][1-5]|70)\d{3,4}\d{4}$/; // 전화번호 정규식
// 닉네임 유효성 검사
if(memberNickname.value.length == 0){ // 미작성 시 : 닉네임을 입력해주세요.
alert("닉네임을 입력해주세요.");
memberNickname.focus();
return false;
}
if(!regExp1.test(memberNickname.value)){ // 유효하지 않은 경우
alert("닉네임은 영어/숫자/한글 2~10 글자 사이로 작성해주세요.");
memberNickname.focus();
return false;
}
// 전화번호 유효성 검사
if(memberTel.value.length == 0){ // 미작성 시
alert("전화번호를 입력해주세요.(- 제외)");
memberTel.focus();
return false;
}
if(!regExp2.test(memberTel.value)){ // 유효하지 않은 경우
// alert(" 전화번호 형식이 올바르지 않습니다.");
// memberTel.focus();
// return false;
return printAlert(memberTel, "전화번호 형식이 올바르지 않습니다.");
}
return true; // true를 반환해서 form 제출 수행
}
// 경고 출력 + 포커스 + false 반환 함수
function printAlert(el, message){ // 매개변수 el은 요소
alert(message);
el.focus();
return false;
}
// 비밀번호 변경 제출 시 유효성 검사
function changePwValidate(){
// 비밀번호 변경 관련 input 요소 얻어오기
const currentPw = document.getElementsByName("currentPw")[0];
//const currentPw = document.getElementById("currentPw");
const newPw = document.getElementsByName("newPw")[0];
const newPwConfirm = document.getElementsByName("newPwConfirm")[0];
// 비밀번호 정규표현식
const regEx = /^[\w!@#_-]{6,30}$/;
// 현재 비밀번호 미작성
if(currentPw.value.trim().length == 0){
/*alert("현재 비밀번호를 입력해주세요.");
currentPw.focus();
return false;*/
return printAlert(currentPw, "현재 비밀번호를 입력해주세요.");
}
// 새 비밀번호
// 미작성
if(newPw.value.trim().length == 0){
alert("새 비밀번호를 입력해주세요.");
newPw.focus();
return false;
}
// 유효하지 않은 경우
if(!regEx.test(newPw.value)){
alert("영어, 숫자, 특수문자(!,@,#,-,_) 6~30 글자 사이로 작성해주세요.");
newPw.focus();
return false;
}
// 새 비밀번호 확인
// 미작성
if(newPwConfirm.value.trim().length == 0){
return printAlert(newPwConfirm, "새 비밀번호 확인을 입력해주세요.");
}
// 새 비밀번호 != 새 비밀번호 확인
if(newPw.value != newPwConfirm.value){
return printAlert(newPwConfirm, "새 비밀번호가 일치하지 않습니다.");
}
return true; // 위 조건을 모두 수행하지 않은 경우 true 반환
}
// 회원 탈퇴 유효성 검사
function secessionValidate(){
const memberPw = document.getElementById("memberPw");
const agree = document.getElementById("agree");
// 비밀번호 미작성
if(memberPw.value.trim().length == 0){
alert("비밀번호를 입력해주세요.");
memberPw.focus();
return false;
}
// 약관 동의 체크 여부
// - 체크박스요소.checked : 체크 시 true, 해제 시 false 반환
if( !agree.checked ){ // 체크를 안했을 때
alert("약관 동의 후 탈퇴 버튼을 클릭해주세요.");
agree.focus();
return false;
}
// 정말 탈퇴할지 확인
// - window.confirm("내용") : 대화 상자에 확인/취소 생성
// 확인 클릭 시 true / 취소 클릭 시 false
// window는 생략 가능
if( !confirm("정말 탈퇴 하시겠습니까?") ){ // 취소를 누른 경우
return false;
}
return true;
}
// 회원 프로필 이미지 변경(미리보기)
const inputImage = document.getElementById("input-image");
if(inputImage != null){ // inputImage 요소가 화면에 존재 할 때
// input type="file" 요소는 파일이 선택 될 때 change 이벤트가 발생한다.
inputImage.addEventListener("change", function(){
// this : 이벤트가 발생한 요소 (input type="file")
// files : input type="file"만 사용 가능한 속성으로
// 선택된 파일 목록(배열)을 반환
//console.log(this.files)
//console.log(this.files[0]) // 파일목록에서 첫 번째 파일 객체를 선택
if(this.files[0] != undefined){ // 파일이 선택되었을 때
const reader = new FileReader();
// 자바스크립트의 FileReader
// - 웹 애플리케이션이 비동기적으로 데이터를 읽기 위하여 사용하는 객체
reader.readAsDataURL(this.files[0]);
// FileReader.readAsDataURL(파일)
// - 지정된 파일의 내용을 읽기 시작함.
// - 읽어오는게 완료되면 result 속성 data:에
// 읽어온 파일의 위치를 나타내는 URL이 포함된다.
// -> 해당 URL을 이용해서 브라우저에 이미지를 볼 수 있다.
// FileReader.onload = function(){}
// - FileReader가 파일을 다 읽어온 경우 함수를 수행
reader.onload = function(e){ // 고전 이벤트 모델
// e : 이벤트 발생 객체
// e.target : 이벤트가 발생한 요소(객체) -> FileReader
// e.target.result : FileReader가 읽어온 파일의 URL
// 프로필 이미지의 src 속성의 값을 FileReader가 읽어온 파일의 URL로 변경
const profileImage = document.getElementById("profile-image");
profileImage.setAttribute("src", e.target.result);
// -> setAttribute() 호출 시 중복되는 속성이 존재하면 덮어쓰기
document.getElementById("delete").value = 0;
// 새로운 이미지가 선택 되었기 때문에 1 -> 0(안눌러짐 상태)으로 변경
}
}
});
}
// 이미지 선택 확인
function profileValidate(){
const inputImage = document.getElementById("input-image");
const del = document.getElementById("delete"); // hidden 타입
if( inputImage.value == "" && del.value == 0 ){
// 빈문자열 == 파일 선택 X / del의 값이 0 == x를 누르지도 않았다
// --> 아무것도 안하고 변경버튼을 클릭한 경우
alert("이미지를 선택한 후 변경 버튼을 클릭해주세요.");
return false;
}
return true;
}
// 다음 주소 API
// 우편번호 찾기 화면을 넣을 element
var element_layer = document.getElementById('layer');
function closeDaumPostcode() {
// iframe을 넣은 element를 안보이게 한다.
element_layer.style.display = 'none';
}
function execDaumPostcode() {
new daum.Postcode({
oncomplete: function(data) {
// 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
// 각 주소의 노출 규칙에 따라 주소를 조합한다.
// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
var addr = ''; // 주소 변수
var extraAddr = ''; // 참고항목 변수
//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
addr = data.roadAddress;
} else { // 사용자가 지번 주소를 선택했을 경우(J)
addr = data.jibunAddress;
}
// 우편번호와 주소 정보를 해당 필드에 넣는다.
document.getElementById('postcode').value = data.zonecode;
document.getElementById("address").value = addr;
// 커서를 상세주소 필드로 이동한다.
document.getElementById("detailAddress").focus();
// iframe을 넣은 element를 안보이게 한다.
// (autoClose:false 기능을 이용한다면, 아래 코드를 제거해야 화면에서 사라지지 않는다.)
element_layer.style.display = 'none';
},
width : '100%',
height : '100%',
maxSuggestItems : 5
}).embed(element_layer);
// iframe을 넣은 element를 보이게 한다.
element_layer.style.display = 'block';
// iframe을 넣은 element의 위치를 화면의 가운데로 이동시킨다.
initLayerPosition();
}
// 브라우저의 크기 변경에 따라 레이어를 가운데로 이동시키고자 하실때에는
// resize이벤트나, orientationchange이벤트를 이용하여 값이 변경될때마다 아래 함수를 실행 시켜 주시거나,
// 직접 element_layer의 top,left값을 수정해 주시면 됩니다.
function initLayerPosition(){
var width = 500; //우편번호서비스가 들어갈 element의 width
var height = 400; //우편번호서비스가 들어갈 element의 height
var borderWidth = 5; //샘플에서 사용하는 border의 두께
// 위에서 선언한 값들을 실제 element에 넣는다.
element_layer.style.width = width + 'px';
element_layer.style.height = height + 'px';
element_layer.style.border = borderWidth + 'px solid';
// 실행되는 순간의 화면 너비와 높이 값을 가져와서 중앙에 뜰 수 있도록 위치를 계산한다.
element_layer.style.left = (((window.innerWidth || document.documentElement.clientWidth) - width)/2 - borderWidth) + 'px';
element_layer.style.top = (((window.innerHeight || document.documentElement.clientHeight) - height)/2 - borderWidth) + 'px';
}
-> 이미지 x버튼 클릭 이벤트에 에러 발생으로 해당 이벤트는 myPage-profile.jsp로 <script></script> 만들어 안에 넣어 주었습니다
myPage-profile.jsp
<script>
// 프로필 이미지 옆 x버튼 클릭 시
document.getElementById("delete-image").addEventListener("click", function(){
// 0 : 안눌러짐
// 1 : 눌러짐
const del = document.getElementById("delete");
if(del.value == 0){ // 눌러지지 않은 경우
// 1) 프로필 이미지를 기본 이미지로 변경
document.getElementById("profile-image").setAttribute("src", contextPath + "/resources/images/user.png");
// 2) input type="file"에 저장된 값(value)에 "" 대입
document.getElementById("input-image").value = "";
del.value = 1; // 눌러진걸로 인식
}
});
</script>
mybatis-config
myPage-info
myPage-info.jsp 하단 스크립트 추가
<%-- 다음 주소 API --%>
<div id="layer" style="display:none;position:fixed;overflow:hidden;z-index:11;-webkit-overflow-scrolling:touch;">
<img src="//t1.daumcdn.net/postcode/resource/images/close.png" id="btnCloseLayer" style="cursor:pointer;position:absolute;right:-3px;top:-3px;z-index:1" onclick="closeDaumPostcode()" alt="닫기 버튼">
</div>
<script src="//t1.daumcdn.net/mapjsapi/bundle/postcode/prod/postcode.v2.js"></script>
<!-- myPage.js 추가 -->
<script src="${contextPath}/resources/js/member/myPage.js"></script>
myPage-mapper.xml
<?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="myPageMapper">
<!-- 파라미터 타입이 map인 경우
1.parameterType="java.util.Map"
2.parameterType="map"(map ->mybatis 별칭)
3.생략(TypeHandler가 알아서 지정해준다)
-->
<!-- 회원정보 수정 -->
<update id="updateInfo">
UPDATE MEMBER_S SET
MEMBER_NICK = #{updateNickname}, MEMBER_TEL = #{updateTel}, MEMBER_ADDR = #{memberAddress}
WHERE MEMBER_NO = ${memberNo}
</update>
<!-- id="DAO에서 sqlSession.update("myPageMapper.id값", paramMap); -->
<!-- resultType="_int"로 기본값이어서 안써줘도 된다 -->
<!-- -> ''들어가야하면 #{}, ''안들어가면 ${} -->
</mapper>
주소검색기능 추가
mypage.js
// 다음 주소 API
// 우편번호 찾기 화면을 넣을 element
var element_layer = document.getElementById('layer');
function closeDaumPostcode() {
// iframe을 넣은 element를 안보이게 한다.
element_layer.style.display = 'none';
}
function execDaumPostcode() {
new daum.Postcode({
oncomplete: function(data) {
// 검색결과 항목을 클릭했을때 실행할 코드를 작성하는 부분.
// 각 주소의 노출 규칙에 따라 주소를 조합한다.
// 내려오는 변수가 값이 없는 경우엔 공백('')값을 가지므로, 이를 참고하여 분기 한다.
var addr = ''; // 주소 변수
var extraAddr = ''; // 참고항목 변수
//사용자가 선택한 주소 타입에 따라 해당 주소 값을 가져온다.
if (data.userSelectedType === 'R') { // 사용자가 도로명 주소를 선택했을 경우
addr = data.roadAddress;
} else { // 사용자가 지번 주소를 선택했을 경우(J)
addr = data.jibunAddress;
}
// 우편번호와 주소 정보를 해당 필드에 넣는다.
document.getElementById('postcode').value = data.zonecode;
document.getElementById("address").value = addr;
// 커서를 상세주소 필드로 이동한다.
document.getElementById("detailAddress").focus();
// iframe을 넣은 element를 안보이게 한다.
// (autoClose:false 기능을 이용한다면, 아래 코드를 제거해야 화면에서 사라지지 않는다.)
element_layer.style.display = 'none';
},
width : '100%',
height : '100%',
maxSuggestItems : 5
}).embed(element_layer);
// iframe을 넣은 element를 보이게 한다.
element_layer.style.display = 'block';
// iframe을 넣은 element의 위치를 화면의 가운데로 이동시킨다.
initLayerPosition();
}
// 브라우저의 크기 변경에 따라 레이어를 가운데로 이동시키고자 하실때에는
// resize이벤트나, orientationchange이벤트를 이용하여 값이 변경될때마다 아래 함수를 실행 시켜 주시거나,
// 직접 element_layer의 top,left값을 수정해 주시면 됩니다.
function initLayerPosition(){
var width = 500; //우편번호서비스가 들어갈 element의 width
var height = 400; //우편번호서비스가 들어갈 element의 height
var borderWidth = 5; //샘플에서 사용하는 border의 두께
// 위에서 선언한 값들을 실제 element에 넣는다.
element_layer.style.width = width + 'px';
element_layer.style.height = height + 'px';
element_layer.style.border = borderWidth + 'px solid';
// 실행되는 순간의 화면 너비와 높이 값을 가져와서 중앙에 뜰 수 있도록 위치를 계산한다.
element_layer.style.left = (((window.innerWidth || document.documentElement.clientWidth) - width)/2 - borderWidth) + 'px';
element_layer.style.top = (((window.innerHeight || document.documentElement.clientHeight) - height)/2 - borderWidth) + 'px';
}
'Spring' 카테고리의 다른 글
0501spring sql log출력, (0) | 2023.05.01 |
---|---|
spring 파일업로드 중 에러springframework.beans.factory.BeanCreationException: Error creating bean with name 'sqlSessionFactoryBean' defined in class with path resource (0) | 2023.04.29 |
0427spring 비동기로 웹페이지에 회원정보 전체조회 (0) | 2023.04.27 |
0427 spring웹페이지에서 이메일 입력해 회원정보조회 (0) | 2023.04.27 |
0427spring 회원가입 (+닉네임 유효성 중복검사) (0) | 2023.04.27 |