프로필 이미지 수정 기능

oYJo·2025년 3월 13일

Spring

목록 보기
23/29
post-thumbnail

프로필 이미지 수정 기능

1️⃣ Visual Studio Code

✔️webapp(WEB-INF(views(myPage))) → myPage-profile.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
  <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"  %>

    <!DOCTYPE html>
    <html lang="ko">
      <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">

        <title>My Page</title>

        <link rel="stylesheet" href="/resources/css/myPage/myPage-style.css">
      </head>
      <body>
        <main>
          <jsp:include page="/WEB-INF/views/common/header.jsp"/>


          <!-- 마이페이지 - 내 정보 -->
          <section class="myPage-content">

            <!-- 사이드메뉴 include -->
            <!-- jsp 액션 태그 -->
            <jsp:include page="/WEB-INF/views/myPage/sideMenu.jsp"/>

            <!-- 오른쪽 마이페이지 주요 내용 부분 -->
            <section class="myPage-main">

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

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



                    </section>

                  </section>

                </main>

              <jsp:include page="/WEB-INF/views/common/footer.jsp"/>

              <%-- myPage.js 연결 --%>
                <script src="/resources/js/myPage/myPage.js"></script>

                </body>
              </html>

✔️webapp(resources(js(myPage))) → 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){ // 화면에 imageInput이 있을 경우

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

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

  } else {
    initCheck = true;
  }

  // change 이벤트 : 값이 변했을 때
  // - input type="file", "checkbox", "radio"에서 많이 사용
  // - text/number 형식 사용 가능
  //  -> 이때 input값 입력 후 포커스를 잃었을 때
  //     이전 값과 다르면 change 이벤트 발생

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

  });

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








근데 이미지 이미 있는 상태에서 올리려다가 취소 누르면 에러 뜸

size에서 에러가 뜨는 것
아무것도 없는데 어떻게 사이즈를 초과하냐!
그래서 한번 더 걸러줘야함

event.preventDefault()는 브라우저에 동작하는 이벤트를 실행하지 못하게 막아주는 메서드

2️⃣ Spring

✔️pom.xml, root-context.xml

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

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

✔️project(myPage(controller)) → MyPageController.java

...
	// 프로필 이미지 수정
	@PostMapping("/profile")
	public String updateProfile(
			@RequestParam("profileImage") MultipartFile profileImage // 업로드 파일
			, @SessionAttribute("loginMember") Member loginMember // 로그인한 회원
			, RedirectAttributes ra // 리다이렉트 시 메시지 전달
			, HttpSession session // 세션 객체
			) throws IllegalStateException, IOException {
		
		// 웹 접근 경로
		String webPath = "/resources/images/member/";
		
		// 실제로 이미지 파일이 저장되어야 하는 서버 컴퓨터 경로
		String filePath = session.getServletContext().getRealPath(webPath);
		
		// 프로필 이미지 수정 서비스 호출
		int result = service.updateProfile(profileImage, webPath, filePath, loginMember);
		
		String message = null;
		
		if(result > 0) message = "프로필 이미지가 변경되었습니다.";
		else		   message = "프로필 변경 실패";
		
		ra.addFlashAttribute("message", message);
		
		return "redirect:profile";
		
	}

⭐️ MultipartFile란?

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

✔️project(myPage(model(service))) → MyPageService.java

...
	/** 프로필 이미지 수정 서비스
	 * @param profileImage
	 * @param webPath
	 * @param filePath
	 * @param loginMember
	 * @return result
	 */
	int updateProfile(MultipartFile profileImage,
			String webPath, String filePath, Member loginMember) throws IllegalStateException, IOException;

✔️project(myPage(model(service))) → MyPageServiceImpl.java

...
	// 프로필 이미지 수정 서비스
	@Override
	public int updateProfile(MultipartFile profileImage, String webPath, String filePath, Member loginMember) throws IllegalStateException, IOException {
		
		// 프로필 이미지 변경 실패 대비
		String temp = loginMember.getProfileImage(); // 이전 이미지 저장
		
		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 { // 없는 경우 (X 버튼)
			loginMember.setProfileImage(null);
			// 세션 이미지를 null로 변경해서 삭제
		}
		
		// 프로필 이미지 수정 DAO 메소드 호출
		int result = dao.updateProfile(loginMember);
		
		if(result > 0) { // 성공

			// 새 이미지가 업로드된 경우
			if(rename != null) {
				profileImage.transferTo(new File(filePath + rename));
			}
			
		} else { // 실패
			
			// 이전 이미지로 프로필 다시 세팅
			loginMember.setProfileImage(temp);
		}
		
		return result;
	}
	
	// 파일명 변경 메소드
	public static String fileRename(String originFileName) {
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		String date = sdf.format(new java.util.Date(System.currentTimeMillis()));
		
		int ranNum = (int) (Math.random() * 100000); // 5자리 랜덤 숫자 생성
		
		String str = "_" + String.format("%05d", ranNum);
		
		String ext = originFileName.substring(originFileName.lastIndexOf("."));
		
		return date + str + ext;
	}

✔️project(myPage(model(dao))) → MyPageDAO.java

...
	/** 프로필 이미지 수정
	 * @param loginMember
	 * @return result
	 */
	public int updateProfile(Member loginMember) {
		return sqlSession.update("myPageMapper.updateProfile", loginMember);
	}

✔️resources(mappers) → myPage-mapper.xml

...
<!-- 프로필 이미지 수정 -->
<update id="updateProfile">
  UPDATE MEMBER SET
  PROFILE_IMG = #{profileImage}
  WHERE MEMBER_NO = #{memberNo}
</update>
profile
Hello! My Name is oYJo

0개의 댓글