프로필 이미지 변경

Yuri Lee·2020년 11월 12일
0

Profile 파라미터 추가

settings/Profile.java

    private String profileImage;

    public Profile(Account account) {
        this.bio = account.getBio();
        this.url = account.getUrl();
        this.occupation = account.getOccupation();
        this.location = account.getLocation();
        this.profileImage = account.getProfileImage();
    }

추가해주기

<div class="form-group">
<input id="profileImage" type="hidden" th:field="*{profileImage}" class="form-control" />
</div>

type="hidden" 주목, Cropper.JS 사용해서 이미지 영역을 잘라서 지정할 수 있다. (마우스 휠을 이용해서) 잘라낸 만큼만 프로필 이미지로 사용할 수 있도록 할 것이다.

아바타 이미지 잘라서 저장하기

<div class="card-header">
  프로필 이미지
</div>
<div id="current-profile-image" class="mt-3">
  <svg th:if="${#strings.isEmpty(profile.profileImage)}" class="rounded"
       th:data-jdenticon-value="${account.nickname}" width="125" height="125"></svg>
  <img th:if="${!#strings.isEmpty(profile.profileImage)}" class="rounded"
       th:src="${profile.profileImage}"
       width="125" height="125" alt="name" th:alt="${account.nickname}"/>
</div>

현재 이미지를 보여주는 곳, profile.profileImage 가 비어있는 값이면 account.nickname 의 data-jdenticon-value로 자동 생서된 이미지를 보여준다. profile.profileImage 가 있으면 프로필 이미지를 그대로 src 소스로 사용해서 보여준다. 우리는 Image 를 파일로 저장하지 않을 것이다. 문자열 그대로를 저장할 것이다.

db에 저장이 되어있다. 아바타는 유저 정보를 불러올 때마다 불러와야 하고, 그때마다 파일을 불러오는 게 번거롭다. html에서 DataURL 를 제공한다.

DataURL 이란?

  • data: 라는 접두어를 가진 URL로 파일을 문서에 내장 시킬때 사용할 수 있다.
  • 이미지를 DataURL로 저장할 수 있다.

/settings/profile

/settings/profile.html

<div class="card text-center">
  <div class="card-header">
    프로필 이미지
  </div>
  <div id="current-profile-image" class="mt-3">
    <svg th:if="${#strings.isEmpty(profile.profileImage)}" class="rounded"
         th:data-jdenticon-value="${account.nickname}" width="125" height="125"></svg>
    <img th:if="${!#strings.isEmpty(profile.profileImage)}" class="rounded"
         th:src="${profile.profileImage}"
         width="125" height="125" alt="name" th:alt="${account.nickname}"/>
  </div>
  <div id="new-profile-image" class="mt-3"></div>
  <div class="card-body">
    <div class="custom-file">
      <input type="file" class="custom-file-input" id="profile-image-file">
      <label class="custom-file-label" for="profile-image-file">프로필 이미지 변경</label>
    </div>
    <div id="new-profile-image-control" class="mt-3">
      <button class="btn btn-outline-primary btn-block" id="cut-button">자르기</button>
      <button class="btn btn-outline-success btn-block" id="confirm-button">확인</button>
      <button class="btn btn-outline-warning btn-block" id="reset-button">취소</button>
    </div>
    <div id="cropped-new-profile-image" class="mt-3"></div>
  </div>
</div>

처음 나오는 곳에서는 자르기, 확인, 취소 버튼을 숨겨줘야 한다.

프론트 라이브러리 설치

  • Cropper.JS
  • npm install cropper
  • npm install jquery-cropper

/settings/profile.html

<script type="application/javascript">
    $(function() {
        cropper = '';
        // html에 있는 각각의 요소들
        let $confirmBtn = $("#confirm-button"); //확인 버튼
        let $resetBtn = $("#reset-button"); //취소 버튼
        let $cutBtn = $("#cut-button"); // 자르기 버튼
        let $newProfileImage = $("#new-profile-image"); // 새로 선택한 이미지
        let $currentProfileImage = $("#current-profile-image"); // 현재 이미지
        let $resultImage = $("#cropped-new-profile-image"); // 선택한 이미지 중 잘라낸 영역만 나타낸 이미지
        let $profileImage = $("#profileImage"); // 최종적으로 form 에다가 넣어줘야 하는 값

        // 먼저 필요 없는 영역과 버튼을 숨겨줌
        $newProfileImage.hide();
        $cutBtn.hide();
        $resetBtn.hide();
        $confirmBtn.hide();

        // 프로필 이미지 변경 Browse를 선택하면 이 창의 값이 바뀌면
        $("#profile-image-file").change(function(e) {
            if (e.target.files.length === 1) { // 파일을 하나 선택했으면
                const reader = new FileReader(); //1. File Reader 를 먼저 만듦
                reader.onload = e => { // 파일이 읽어와 졌으면
                    if (e.target.result) { // event에서 target를 가져와서
                        let img = document.createElement("img"); // 이미지 태그를 만들어서 가져온 이미지를 채워넣음
                        img.id = 'new-profile';
                        img.src = e.target.result;
                        img.width = 250;

                        // $newProfileImage 영역에다가 새로운 태그(img)를 추가한다.
                        $newProfileImage.html(img);
                        $newProfileImage.show(); // 새로운 이미지 보여주고
                        $currentProfileImage.hide(); // 현재 이미지는 숨김

                        let $newImage = $(img); // jquery 로 img 감싸고 cropper 를 적용
                        $newImage.cropper({aspectRatio: 1});
                        cropper = $newImage.data('cropper');

                        $cutBtn.show();
                        $confirmBtn.hide(); // 확인 버튼 숨기기 (잘라낸 다음에 사용해야 하므로)
                        $resetBtn.show();
                    }
                };

                reader.readAsDataURL(e.target.files[0]); // 2. 그리고 그 파일을 읽어옴
            }
        });

        // reset 버튼이 사용될 경우
        $resetBtn.click(function() {
            $currentProfileImage.show();
            $newProfileImage.hide();
            $resultImage.hide();
            $resetBtn.hide();
            $cutBtn.hide();
            $confirmBtn.hide();
            // reset 버튼 누르는 순간 최종적으로 셋팅해야 하는 인풋에 해당하는 값을 비어있는 문자열로 셋팅한다. 저장하더라도 아무 일도 발생하지 않도록!
            $profileImage.val('');
        });

        // 자르기 버튼
        $cutBtn.click(function () {
            let dataUrl = cropper.getCroppedCanvas().toDataURL();
            let newImage = document.createElement("img");
            newImage.id = "cropped-new-profile-image"; // 실제로 잘라낸 이미지 만큼큼            newImage.src = dataUrl;
            newImage.width = 125;
            $resultImage.html(newImage);
            $resultImage.show();
            $confirmBtn.show(); // 확인 버튼 보여줌

            // 잘라낸 영역만을 $profileImage 셋팅함
            $confirmBtn.click(function () {
                $newProfileImage.html(newImage);
                $cutBtn.hide();
                $confirmBtn.hide();
                $profileImage.val(dataUrl);
            });
        });
    });
</script>
  • .val() .val()은 양식(form)의 값을 가져오거나 값을 설정하는 메소드

실제값은?

개발자 도구를 켜서 실제 src 값을 확인하면 굉장히 값이 들어가 있음을 볼 수 있다. 이 값 자체가 이미지라고 생각하면 된다. 그대로 db에 저장된다.

AccountService

updateProfile 메소드에 이미지도 넣어준다.

account.setProfileImage(profile.getProfileImage());

문제

fragments.html

<svg data-jdenticon-value="user127" 
     th:data-jdenticon-value="${#authentication.name}" 
     width="24" height="24" class="rounded border bg-light">
</svg>

수정하기 버튼을 눌러서 저장을 하더라도 네비게이션에 반영이 안된다.

<svg th:if="${#strings.isEmpty(account?.profileImage)}" th:data-jdenticon-value="${#authentication.name}"
     width="24" height="24" class="rounded border bg-light">
</svg>
<img th:if="${!#strings.isEmpty(account?.profileImage)}" th:src="${account.profileImage}"
     width="24" height="24" class="rounded border"/>

?. 은 user가 null이 아닌 경우에 네비게이션 하는 것이다.
account?.profileImage 프로필 이미지가 비어있지 않으면, 있으면 account에 있는 프로필 이미지를 그대로 보여지도록 한다.


출처 : 인프런 백기선님의 스프링과 JPA 기반 웹 애플리케이션 개발

profile
Step by step goes a long way ✨

0개의 댓글