Java/Spring Framework

[ Spring ] 마이페이지 프로필 변경 / 삭제

Mungwang 2023. 8. 24. 11:58

🥇 VS-Code [  jsp ] 


📺  JSP 본문 코드

 
        <form action="profile" method="POST" name="myPageFrm" id="profileFrm">

                    <div class="profile-image-area">

                        <img src="/resources/images/user.png" id="profileImage">

                    </div>
                    <span id="deleteImage">x</span>

                    <div class="profile-btn-area">
                        <label for="imageInput">이미지 선택</label>
                        <input type="file" name="profileImage" id="imageInput" accept="image/*">
                        <button>변경하기</button>
                    </div>
                   
                    <div class="myPage-row">
                        <label>이메일</label>
                        <span>로그인 회원 이메일</span>
                    </div>
                   
                    <div class="myPage-row">
                        <label>가입일</label>
                        <span>로그인 회원 가입일</span>
                    </div>
                   
         </form>

📺 기본 브라우저화면


🥈 VS-Code [  js ]

📺  JS 코드 ( 추가, 변경, 삭제 )

       
         // 프로필 이미지 추가 / 변경 / 삭제
 
        const profileImage = document.getElementById("profileImage"); // img 태그
        const deleteImage = document.getElementById("deleteImage"); // x 버튼
        const imageInput = document.getElementById("imageInput"); // input태그

        let initCheck; // 초기 프로필 이미지 상태를 저장하는 변수
                             // false == 기본 이미지, true == 이전 업로드 이미지

        let deleteCheck = -1;
        // 프로필 이미지가 새로 업로드 되거나 삭제 되었음을 나타내는 변수
        // -1 == 초기값, 0 == 프로필 삭제(x 버튼), 1 == 새 이미지 업로드

        let originalImage; // 초기 프로필 이미지 파일 경로 저장


        if(imageInput != null){ // 화면에 imageInput이 있을 경우

        // 프로필 이미지가 출력되는 img태그의 src 속성을 저장
        originalImage = profileImage.getAttribute("src");

        if(originalImage == "/resources/images/user.png"){
            // 기본 이미지인 경우
 
            initCheck = false;
 
        } else{
 
            initCheck = true;
        }


        // 회원 프로필 화면 진입 시
        // 현재 회원의 프로필 이미지 상태를확인

        imageInput.addEventListener("change", e =>{

            // 2MB로 최대 크기 제한
            const maxSize = 1 * 1024 * 1024 * 2// 파일 최대 크기 지정(바이트 단위)

            console.log(e.target); // input
            console.log(e.target.value); // 업로드된 파일 경로
            console.log(e.target.files); // 업로드된 파일의 정보가 담긴 배열

            const file = e.target.files[0]; // 업로드한 파일의 정보가 담긴 객체

            // 파일을 한번 선택한 후 취소했을 때
 
            if(file == undefined){
 
                console.log("파일 선택이 취소됨");
                deleteCheck = -1; // 취소 == 파일 없음 == 초기상태

                // 취소 시 기존 프로필 이미지로 변경
 
                profileImage.setAttribute("src",originalImage);
                return;
 
            }

            if(file.size > maxSize){ // 선택된 파일의 크기가 최대 크기를 초과한 경우
 
                alert("2MB 이하의 이미지를 선택해주세요");
                imageInput.value= "";
                // input type="file" 태그에 대입할수 있는 value는 ""(빈칸) 뿐이다!

                deleteCheck = 1; // 취소 == 파일 없음 == 초기상태

                // 기존 프로필 이미지로 변경
                profileImage.setAttribute("src",originalImage);
                return;
            }

            // JS에서 파일을 읽는 객체
            // - 파일을 읽고 클라이언트 컴퓨터에 파일을 저장할 수 있음
            const reader = new FileReader();

            reader.readAsDataURL(file);
            // 매개변수에 작성된 파일을 읽어서 저장 후
            // 파일을 나타내는 URL을 result 속성으로 얻어올 수 있게 함

            // 다 읽었을 때
            reader.onload = e =>{
                    //console.log(e.target);
                    //console.log(e.target.result); // 읽은 파일의 URL

                const url = e.target.result;

                // 프로필이미지(img) 태그에 src 속성으로 추가
                profileImage.setAttribute("src", url);

                deleteCheck = 1;
            }

        });
 
    }

 

* Key Point !

- chang 이벤트 : 값이 변했을때 적용되는 이벤트 ( input type = "file", "checkbox", "radio"에 많이 사용 )

- text / number 형식 사용가능 -> input값 입력 후 포커스를 잃었을때 이전 값과 다르면 chang 이벤트 발생

📺  console.log() 결과 값

- console.log(e.target) : ↓  첫번째 값

- console.log(e.target.value) : ↓  두번째 값

- console.log(e.target.files) : ↓  세번째 값 

-> * files는 무조건 배열 형식으로  나오기 때문에 무조건 [0]으로 사용해야한다

- reader.onload = e => { }  console.log() 결과값

- console.log(e.target) : ↓ 첫번째 값

- console.log(e.target.result) : ↓ 두번째 값 URL

📺  JS 코드 (  x 버튼 클릭 )

 

    // x 버튼 클릭 시
    deleteImage.addEventListener("click",e=>{

        // 프로필 이미지 기본이미지 변경
        profileImage.setAttribute("src","/resources/images/user.png");
        imageInput.value=""; // input type="file"의 value 삭제
        deleteCheck = 0;

    });

📺  JS 코드 ( 조건을 걸어 form sumit 제어 )

   
       document.getElementById("profileFrm").addEventListener("submit",e=>{

        // let initCheck;
        // 초기 프로필 이미지 상태를 저장하는 변수
        // false == 기본 이미지, true == 이전 업로드 이미지

        // let deleteCheck = -1;
        // 프로필 이미지가 새로 업로드 되거나 삭제 되었음을 나타내는 변수
        // -1 == 초기값, 0 == 프로필 삭제(x 버튼), 1 == 새 이미지 업로드

        let flag = true;

        // 프로필 이미지가 없다 -> 있다
        if(!initCheck && deleteCheck ==1) flag = false;

        // 이전 프로필 이미지가 있다 -> 삭제
        if(initCheck && deleteCheck == 0) flag = false;

        // 이전 프로필 이미지가 없다 -> 새 이미지
        if(initCheck && deleteCheck == 1) flag = false;

        if(flag){ // flag == true -> 제출하면 안되는 경우
            e.preventDefault(); // form 기본 이벤트 제거
            alert("이미지 변경 후 클릭하세요");

        }

    });
 

🥉 Spring

📺  Spring와 연결을 위한 jsp 코드

-  파일 제출시 무조건 Post 방식 enctype 속성 추가

-  enctype : from 태그 데이터가 서버로 제출될 때 인코딩 되는 방법을 지정 ( Post방식 일때만 사용 가능 )

-  application/x-www-form-urlencoded : 모든 문자를 서버로 전송하기 전에 인코딩 ( form 태그 기본값 )

-  multipart/form-data : 모든 문자를 인코딩 하지 않음 ( 원본 데이터가 유지되어 이미지, 파일등을 서버로 전송할 수있음 )

 

 
   <form action="profile" method="POST" name="myPageFrm" id="profileFrm" enctype="multipart/form-data">

                    <div class="profile-image-area">
                        <c:if test="${empty loginMember.profileImage}" >
                             <img src="/resources/images/user.png" id="profileImage">
                        </c:if>

                        <c:if test="${!empty loginMember.profileImage}" >
                             <img src="${loginMember.profileImage}" id="profileImage">
                        </c:if>

                    </div>
                    <span id="deleteImage">x</span>

                    <div class="profile-btn-area">
                        <label for="imageInput">이미지 선택</label>
                        <input type="file" name="profileImage" id="imageInput" accept="image/*">
                        <button>변경하기</button>
                    </div>
                   
                    <div class="myPage-row">
                        <label>이메일</label>
                        <span>${loginMember.memberEmail}</span>
                    </div>
                   
                    <div class="myPage-row">
                        <label>가입일</label>
                        <span>${loginMember.enrollDate}</span>
                    </div>
                   
   </form>
 

📺  root-context.xml에 multipartResolver bean을 객체로 추가 

📺  MyPageController

-  MultipartFile : input type= "file"로 제출된 파일을 저장한 객체

-  [ MuitipartFile ] 이 제공하는 메소드 ( transferTo(), getOriginalFileName(), getSize()

1)  transferTo() : 파일을 지정된 경로에 저장 ( 메모리 -> HDD/SSD )

2)  getOriginalFileName() : 파일 원본명

3)  getSize() : 파일 크기

📺  MyPageService

📺  MyPageServiceImpl

- throws IllegalStateException, IOException 로 예외처리를 해주지 않으면 

profileImage.transferTo(new File(filePath + rename));   <- 이부분에서 에러메세지가 뜬다.

// 프로필 이미지 수정 서비스
	@Transactional(rollbackFor = {Exception.class})
	@Override
	public int updateProfile(MultipartFile profileImage, String webPath, String filePath, Member loginMember) throws IllegalStateException, IOException {
		
		// 프로필 이미지 변경 실패 대비
		String temp = loginMember.getProfileImage(); // 이전 이미지 저장
		
		// 업로드된 이미지가 있을 경우 <-> 없는 경우 ( x버튼 )
		
		String rename = null; // 변경 이름 저장 변수
		
		if(profileImage.getSize() >0 ) { // 업로드된 이미지가 있을 경우
			
			// 1) 파일 이름 변경
			rename = fileRename(profileImage.getOriginalFilename());
			
			// 2) 바뀐 이름 loginMember에 세팅
			loginMember.setProfileImage(webPath + rename);
				     	// /resources/images/member/ + 20230824114510_ 12345.jpg

		} else { // 업로드된 이미지가 없을 경우
			
			loginMember.setProfileImage(null);
			// 세션 이미지를 null 변경해서 삭제
			
		}
		
		// 프로필 이미지 수정 DAO 메소드 호출
		int result = dao.updateProfileImage(loginMember);
		
		if(result > 0) { // 성공
			
			// 새 이미지가 업로드 된 경우
			if(rename != null ) {
				profileImage.transferTo(new File(filePath + rename));
			}
			
		} else { // 실패
			
			// 이전 이미지로 프로필 다시 세팅
			loginMember.setProfileImage(temp);
			
		}
		
		return result;
	}

📺  MyPageServiceImpl

-  파일명 변경 메소드 생성

-  reutrn date + str + ext  : 현재시간 + "_" 랜덤한 5자리 정수 + .확장자

-  lastIndexOf() :  원본 문자열의 뒤에서부터 탐색하여 처음으로 나오는 ( ) 안에 문자열 리턴 <-> IndexOf()

📺  MyPageDAO

📺  myPage-mapper.xml