ON/spring

[ Spring ] 프로필 이미지 추가 | 변경 | 삭제 ⑨

박도비 2023. 8. 24. 10:00
728x90

이번시간에는 프로필 이미지 추가 / 변경 / 삭제 할 예정이다. 

그 전에 이미지 선택 하여 동그란 화면에 이미지를 미리보기 할 예정이다. 

📚 VS code

📕 myPage-profile.jsp (프로필 부분만 발췌함)

🤔 accpt = "image/*" : 이미지만 허락하겠다라는 뜻이다. 

<section class="myPage-main">

    <h1 class="myPage-title">프로필</h1>
    <span class="myPage-subject">프로필 이미지를 변경할 수 있습니다.</span>

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

        <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>



</section>

📕 myPage.js (프로필 부분만 발췌함)

const profileImage = document.getElementById("profileImage");//Img 태그
const deleteImage = document.getElementById("deleteImage");//x버튼
const imageInput = document.getElementById("imageInput");//input 태그

if(imageInput != null){ // main.js는 다른 jsp랑도 연결되어 있어 검사가 필요하다. 

	imageInput.addEventListener("change",e=>{
    
    	const maxSize = 1 * 1024 * 1024 * 2 
        const file = e.target.files[0]
        
        if(file.size > maxSize){
        	alert("2MB 이하의 이미지를 선택해주세요")
         	return
        }
        
        const reader = new FileReader();
        reader.readAsDateURL(file);
        
        reader.onload = e =>{
        
        	const url = e.target.result;
        	profileImage.setAttribute("src",url)
        
        }
    
    }

}

 

🤔💭 코드 해석 

change : input 값 입력 후 포커스를 잃었을 때 이전 값과 다르면 change 이벤트 발생
const maxSize = 1 * 1024 * 1024 * 2 // 파일의 최대 크기(2MB 크기 제한)
const file = e.target.files[0]; // 업로드한 파일의 정보가 담긴 객체 
const reader = new FileReader(); // JS 파일을 읽는 객체 (파일을 읽고 클라이언트 컴퓨터에 저장)

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

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

🤔💭 콘솔 해석

console.log(e.target); : input 태그 나옴 

console.log(e.target.value) : 업로드된 파일 경로


console.log(e.target.files) : 업로드된 파일 정보가 담긴 배열 

console.log(e.target.result) : 읽은 파일의 URL

🔮 출력화면 

 

 

화면에 미리보기랑 x 표시 등등 모두 설정해놓았다.

이제 변경이나 취소를 위해  DB에 파일을 업로드 하기 위해서는 jsp / js 파일 업로드하기 위해 변수를 설정한다. 

 

📚 VS code

📕 myPage.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) { 


    originalImage = profileImage.getAttribute("src");

    // 회원 프로필 화면 진입 시 현재 회원의 프로필 이미지 상태를 확인
    if(originalImage == "/resources/images/user.png"){ // 기본 이미지인 경우
        initCheck = false;
    } else{
        initCheck = true;
    }

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

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

        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 = ""; //(이렇게 안해주면 일단 올라가 있고 db에 반영 될 수 있따.)
            // input type = "file"태그에 대입할 수 있는 value 는 ""(빈칸) 뿐이다. 

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

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

        }

        const reader = new FileReader();

        reader.readAsDataURL(file);
        
        // 다 읽었을때 
        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;

            // 결론 : 이미지는 url 형식이고 읽으면 result로 반환할 수 있따! 

        }

    });

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

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

    // #profileFrm이 제출 되었을때
    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){ // fals == true -> 제출하면 안되는 경우
            e.preventDefault(); // form 기본 이벤트 제거
            alert("이미지 변경 후 클릭하세요.");
        }

        

    });

}

 

📕 myPage-profile.jsp (프로필 부분만 발췌함)

<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>

 

📖 multipart/form-data 란 ? 

 파일 제출 시 무조건 POST 방식 
 
- enctype 속성 추가 
- enctype : form 태그 데이터가 서버로 제출될 때 인코딩 되는 방법을 지정 
  (POST 방식 일 때만 사용가능)

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

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

 

📗 porm.xml (필요한 부분만 발췌함) 

<!-- 파일 업로드 관련 라이브러리 -->
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.5</version>
</dependency>

📗 root-context.xml (필요한 부분만 발췌함)

<!-- 
파일 업로드를 위한 MutipartResolver 구현체 CommonsMultipartResolver  bean 등록 
-> CommonsMultipartResolver를 bean으로 등록하면
  multipart/form-data 형식으로 요청 시  input type="file" 태그를 자동적으로 인식하여 
  MultipartFile 객체로 반환하고
  파일 외의 데이터(정수, 문자열 등의 텍스트 데이터)는 기존처럼 사용 가능(MultipartRequest 필요 없음)
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
   <property name="maxUploadSize" value="104857600"/>
   <property name="maxUploadSizePerFile" value="104857600"/>
   <property name="maxInMemorySize" value="104857600"/>
</bean>
<!-- 
  104857600 byte == 100MB

  maxUploadSize 
     : 한 요청당 업로드가 허용되는 최대 용량을 바이트 단위로 설정.
     -1 은 제한이 없다는 뜻으로 이 프로퍼티를 지정하지 않을때 기본값.

  maxUploadSizePerFile
   : 한 파일당 업로드가 허용되는 최대 용량을 바이트 단위로 설정.
     -1 은 제한이 없다는 뜻으로 이 프로퍼티를 지정하지 않을때 기본값.

  maxInMemorySize 
     : 디스크에 저장하지 않고 메모리에 유지하도록 
     허용하는 바이트 단위의 최대 용량을 설정.

      사이즈가 이보다 클 경우 이 사이즈 이상의 데이터는 파일에 저장됩니다. 
      기본값은 10240 바이트.
-->
728x90