26. 프로필 이미지 변경
아바타 이미지 잘라서 저장하기
필요한 프론트 라이브러리
> Cropper.JS
> npm install cropper
> npm install jquery-cropper
DataURL 이란?
> data: 라는 접두어를 가진 URL로 파일을 문서에 내장 시킬 때 사용할 수 있다.
> 이미지를 DataURL로 저장할 수 있다.
백엔드 로직 처리하기
1. Profile DTO에 profileImage 변수 추가하기
/**
* setting form을 채울 Data (DTO)
*/
@Data
@NoArgsConstructor
public class Profile {
// ...
private String profileImage;
public Profile(Account account) {
// ...
this.profileImage = account.getProfileImage();
}
}
뷰 생성하기
2. Profile.html에 profile input태그를 추가
<div class="form-group">
<input id="profileImage" type="hidden" th:field="*{profileImage}" class="form-control"/>
</div>
3. main/resources/static 경로 cropper설치하기
$ npm install cropper
$ npm install jquery-cropper
그리고 profile.html에 다시 profileImage를 보여주는 Card를 추가한다.
<!-- 프로필 이미지-->
<div class="col-sm-6">
<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>
</div>
위의 Card의 모습은 아래와 같이 나온다.
4. Button Js 추가 (Cropper)
이젠 조건에 따라 버튼의 여부와 그에 대한 동작을 설정해줘야한다.
<link href="/node_modules/cropper/dist/cropper.min.css" rel="stylesheet">
<script src="/node_modules/cropper/dist/cropper.min.js"></script>
<script src="/node_modules/jquery-cropper/dist/jquery-cropper.min.js"></script>
<script type="application/javascript">
$(function() {
cropper = '';
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"); // 최종 프로필 이미지
// 버튼 숨기기
$newProfileImage.hide();
$cutBtn.hide();
$resetBtn.hide();
$confirmBtn.hide();
// 이미지 파일 선택하면 해당 파일 읽어오고 저장하는 로직
// profile-image-file (프로필 이미지 변경 선택)
$("#profile-image-file").change(function(e) {
if (e.target.files.length === 1) {
const reader = new FileReader();
reader.onload = e => {
if (e.target.result) {
if (!e.target.result.startsWith("data:image")) {
alert("이미지 파일을 선택하세요.");
return;
}
let img = document.createElement("img");
img.id = 'new-profile';
img.src = e.target.result;
img.setAttribute('width', '100%');
$newProfileImage.html(img);
$newProfileImage.show();
$currentProfileImage.hide();
let $newImage = $(img);
$newImage.cropper({aspectRatio: 1});
cropper = $newImage.data('cropper');
$cutBtn.show();
$confirmBtn.hide();
$resetBtn.show();
}
};
reader.readAsDataURL(e.target.files[0]);
}
});
// 취소 버튼 로직
$resetBtn.click(function() {
$currentProfileImage.show();
$newProfileImage.hide();
$resultImage.hide();
$resetBtn.hide();
$cutBtn.hide();
$confirmBtn.hide();
$profileImage.val(''); // 프로필 이미지 비어있게 저장
});
// 자르기 버튼 로직
$cutBtn.click(function () {
let dataUrl = cropper.getCroppedCanvas().toDataURL();
if (dataUrl.length > 1000 * 1024) {
alert("이미지 파일이 너무 큽니다. 1024000 보다 작은 파일을 사용하세요. 현재 이미지 사이즈 " + dataUrl.length);
return;
}
let newImage = document.createElement("img");
newImage.id = "cropped-new-profile-image";
newImage.src = dataUrl;
newImage.width = 125;
$resultImage.html(newImage);
$resultImage.show();
$confirmBtn.show();
$confirmBtn.click(function () {
$newProfileImage.html(newImage);
$cutBtn.hide();
$confirmBtn.hide();
$profileImage.val(dataUrl);
});
});
});
</script>
각 버튼에 대해 로직을 설정해주면 위의 그림과 같이 작동한다. 하지만 아직 해당 이미지가 DB에 저장되지는 않는다.
Back
5. UpdateProfile 로직에 ProfileImage부분 추가
AccountService.java
public void updateProfile(Account account, Profile profile) {
// ...
account.setProfileImage(profile.getProfileImage());
}
Front 뷰
Nav부분 image은 UserImage가 변경되어도 아직 반영이 안되므로 수정해준다.
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<!-- authentication.name에 따라 각기 다른 이미지 생성 -->
<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"/>
</a>
참고
'Dot Programming > Spring Clone' 카테고리의 다른 글
[스프링 웹앱 프로젝트 #29] 알림 설정 (0) | 2021.03.05 |
---|---|
[스프링 웹앱 프로젝트 #27 #28] 패스워드 수정 및 테스트 (0) | 2021.03.04 |
[스프링 웹앱 프로젝트 #25] 프로필 수정 테스트 (0) | 2020.12.22 |
[스프링 웹앱 프로젝트 #24] 프로필 수정 처리 (0) | 2020.12.21 |
[스프링 웹앱 프로젝트 #23] 프로필 수정 폼 (0) | 2020.12.17 |