[JavaScript] SpringBoot 프로젝트 : 프로필 사진 기능 구현(4) - 이미지 미리보기(input type = "file", FileReader)
목적
- 회원제 웹사이트 개발시 거의 필수적으로 사용되는 프로필 사진 등록 및 수정 기능 구현
- 프로필 이미지 등록 관련 DB, Java, JS 처리 총 정리
- 일부 받아서 쓴 코드 리뷰
지난 글까지는 기본이미지 또는 DB에 저장되어 있는 이미지를 가져와 JSP에 표시하는 과정을 살펴봤다.
그 과정에서 I/O Stream, VO, Mybatis, Tiles, AbstractView, Base64 encoding 등의 개념들도 짧게나마 정리했다.
이번 글 이후부터는 회원이 프로필 이미지를 등록하거나 수정할 때 처리과정을 알아보겠다.
데이터 흐름도는 다음과 같다.
[데이터 흐름도]
일단 JSP쪽 코드부터 보자.
[JSP]
JSP의 코드는 아래와 같은데, 프로필 이미지와 관련된 태그들만 적었다.
<!--페이지에 표시되는 프로필이미지-->
<a href="#profile-change" data-bs-toggle="modal">
<img src = "${pageContext.request.contextPath}/mypage/photoView.do" class = "profile-photo">
</a>
<!--모달창-->
<div id="profile-change" class="modal fade" tabindex="-1" data-bs-backdrop="static" class="modal-dialog modal-dialog-centered">
<div class="modal-dialog modal-width">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" style = "margin-left: 70px;">프로필 사진 변경</h5>
<button type="button" class="btn-close photo-reset" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>
<img src = "${pageContext.request.contextPath}/mypage/photoView.do" class = "profile-photo" width = "150" height = "150">
<br>
<br>
<input type = "file" id = "upload" accept = "image/gif, image/png, image/jpeg" style = "margin-left : 57px;">
</p>
</div>
<div class="modal-footer justify-content-center" style = "align-items: center;">
<button type="button" class="btn photo-reset" id = "photo_reset" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn" id = "photo_submit">변경</button>
</div>
</div>
</div>
</div>
모달창으로 구현하려고 위와 같이 좀 복잡해보이는 코드가 됐는데 실행 화면을 보면 다음과 같다.
관심있게 봐야할 태그는 아래 3개다. 불필요한 인라인 스타일은 뺐다.
<!--IMG TAG-->
<img src = "${pageContext.request.contextPath}/mypage/photoView.do" class = "profile-photo">
<!--INPUT TAG-->
<input type = "file" id = "upload" accept = "image/gif, image/png, image/jpeg">
<!--BUTTONS-->
<button type="button" class="btn photo-reset" id = "photo_reset" data-bs-dismiss="modal">닫기</button>
<button type="button" class="btn" id = "photo_submit">변경</button>
일단 이 태그들과 각각의 class, id를 기억해 두자.
[JavaScript]
JS로는 아래 세 가지 기능을 구현했다.
- 회원이 input 태그를 통해 이미지 파일을 선택했을 때, 파일 크기 확인 및 화면에 선택한 이미지 미리보기
- 이미지 파일 선택 후 변경 버튼을 클릭했을 때, Ajax 실행
- 닫기 버튼을 클릭했을 때, 미리보기 이미지가 보이는 상태라면 원래 이미지로 변경(모달 닫기는 dismiss가 해줌)
이번엔 이미지 미리보기를 보도록 하자.
[JavaScript - 이미지 미리보기]
$(function(){
//처음 이미지 가져오기
let photo_path = $('.profile-photo').attr('src');
let my_photo; //회원이 업로드할 이미지 담을 변수
$('#upload').change(function(){
my_photo = this.files[0];
if(!my_photo){
$('.profile-photo').attr('src', photo_path);
return
}
if(my_photo.size > 1024*1024){
alert(Math.round(my_photo.size/1024/1024) + 'MB(1MB까지만 업로드 가능)');
$('.profile-photo').attr('src',photo_path);
$(this).val('');
return;
}
//이미지 미리보기 처리
let reader = new FileReader();
reader.readAsDataURL(my_photo);
reader.onload = function(){
$('.profile-photo').attr('src', reader.result);
};
});
//이하 코드 생략
[파일 업로드 체크]
가장 처음으로 해야할 것은 Input태그에 change 이벤트가 발생했을 때 파일이 업로드 됐는지 확인하는 것이다.
회원이 업로드할 이미지를 담을 변수인 my_photo에 this.files[0]을 할당했다.
그리고 조건문을 통해 my_photo에 데이터가 없으면 처음 설정되어 있던 이미지를 변경하지 않은 채 리턴하여 종료시켰다.
this.files[0]에 대해 간단히 설명하자면,
Input태그의 type을 file로 설정한 경우 FileList라는 것을 갖게 되는데, 여기에 Input 태그를 통해 등록한 파일들이 리스트로 저장되고 해당 리스트에서 인덱스로 파일을 불러오는 방식이다.
아래 예시를 보자.
<!--multiple 속성 추가-->
<input type = "file" id = "upload" accept = "image/gif, image/png, image/jpeg" multiple="multiple">
태그에 multiple = "multiple" 속성을 추가해주면 파일을 여러개 등록할 수 있다.
$('#upload').change(function(){
console.log(this.files);
console.log(this.files[0]);
console.log(this.files[1]);
});
파일이 추가되면 콘솔에 찍어서 확인해보기 위해 위와 같이 작성해줬다.
위와 같이 FileList 안에 업로드한 파일이 저장되어 있고, 각각 인덱스로 꺼내보면 하나의 File이 나온다.
프로필 이미지는 하나만 등록할 수 있으므로 multiple 속성을 빼고 그냥 files[0]하면 등록된 하나의 요소를 가져올 수 있다.
[파일 크기 체크]
다음은 파일 크기를 체크하는 부분이다.
코드는 간단하다.
my_photo.size(= this.files[0].size)하면 파일의 크기를 가져올 수 있다.
console.log(this.files[0].size);
직접 콘솔에 찍히는 것을 보면 다음과 같다.
여기서 나오는 크기는 파일의 byte 크기이다.
1024byte가 1KB니까 지금 이 파일은 약 25KB정도 되는 것이다.
1024byte가 1KB이고 1KB가 1024개 있으면 1MB이므로 1024 * 1024 하면 1MB가 된다. 뒤에 *2 해주면 2MB가 되니까 이런식으로 파일 크기를 체크해서 제한을 걸어두는 것이 좋다.
나는 파일 크기가 1MB이상이면 alert를 통해 제한 크기를 알려주고 Input태그에 들어온 데이터를 지운 뒤 리턴했다.
[파일 미리보기 처리]
마지막은 파일 미리보기 처리를 해주는 것이다.
회원이 파일을 업로드 했을 때, 당장 화면에 자신이 올린 이미지를 띄워 확인시켜주는 과정이다.
이를 위해 FileReader객체를 이용하는데 FileReader 객체에 대한 설명은 다음과 같다.
더 자세한 내용을 알고 싶다면 다음을 참고하자.
참고: https://developer.mozilla.org/ko/docs/Web/API/FileReader
암튼 이 FileReader 객체를 생성해서 readAsDataURL이라는 메소드를 사용했다. 이 메소드 설명은 다음과 같다.
종합적으로 보면 FileReader 객체의 readAsDataURL메소드에 Input태그로 읽어 FileList에 담겨있던 파일을 넘겨주면, 이걸 base64 인코딩한 데이터가 result라는 속성에 담긴다는 것이다.
다시 해당 부분 코드를 보면 이렇다.
//FileReader 객체 생성
let reader = new FileReader();
//readAsDataURL에 Input 태그로 읽은 파일 넘겨주기
reader.readAsDataURL(my_photo);
//파일읽기 로딩 완료시
reader.onload = function(){
//result 꺼내서 src 속성에 담아주기
$('.profile-photo').attr('src', reader.result);
};
이렇게 하면 지난 글에서 다룬 것처럼 base64 인코딩 방식으로된 데이터가 src 속성에 들어가게된다.
[결과화면]
이번 글에서는 JS로 회원이 업로드한 이미지 미리보기 기능에 대해 알아봤다.
다음 글에서는 회원이 파일 선택 후 변경 버튼을 눌렀을 때 Ajax 방식으로 데이터를 전송하는 과정에 대해 다뤄보겠다.